Как преобразовать медленные параметризованные вставки в быстрое массовое копирование (даже из памяти) - PullRequest
3 голосов
/ 24 сентября 2008

У меня было что-то подобное в моем коде (.Net 2.0, MS SQL)

SqlConnection connection = new SqlConnection(@"Data Source=localhost;Initial
Catalog=DataBase;Integrated Security=True");
  connection.Open();

  SqlCommand cmdInsert = connection.CreateCommand();
  SqlTransaction sqlTran = connection.BeginTransaction();
  cmdInsert.Transaction = sqlTran;

  cmdInsert.CommandText =
     @"INSERT INTO MyDestinationTable" +
      "(Year, Month, Day, Hour,  ...) " +
      "VALUES " +
      "(@Year, @Month, @Day, @Hour, ...) ";

  cmdInsert.Parameters.Add("@Year", SqlDbType.SmallInt);
  cmdInsert.Parameters.Add("@Month", SqlDbType.TinyInt);
  cmdInsert.Parameters.Add("@Day", SqlDbType.TinyInt);
  // more fields here
  cmdInsert.Prepare();

  Stream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read);

  StreamReader reader = new StreamReader(stream);
  char[] delimeter = new char[] {' '};
  String[] records;
  while (!reader.EndOfStream)
  {
    records = reader.ReadLine().Split(delimeter, StringSplitOptions.None);

    cmdInsert.Parameters["@Year"].Value = Int32.Parse(records[0].Substring(0, 4));
    cmdInsert.Parameters["@Month"].Value = Int32.Parse(records[0].Substring(5, 2));
    cmdInsert.Parameters["@Day"].Value = Int32.Parse(records[0].Substring(8, 2));
    // more here complicated stuff here
    cmdInsert.ExecuteNonQuery()
  }
  sqlTran.Commit();
  connection.Close();

С cmdInsert.ExecuteNonQuery () закомментировано, этот код выполняется менее чем за 2 секунды. При выполнении SQL это занимает 1 м 20 сек. Есть около 0,5 миллионов записей. Стол опорожняется раньше. Задача потока данных служб SSIS с аналогичной функциональностью занимает около 20 секунд.

  • Массовая вставка была не вариант (см. Ниже). Во время этого импорта я сделал кое-что необычное.
  • Мой тестовый компьютер - Core 2 Duo с 2 ГБ ОЗУ.
  • При просмотре в диспетчере задач ЦП не был полностью урезан. IO, похоже, также не был полностью использован.
  • Схема проста, как ад: одна таблица с AutoInt в качестве основного индекса и менее 10 дюймов, крошечные числа и символы (10).

После некоторых ответов здесь я обнаружил, что можно выполнить массовое копирование из памяти ! Я отказывался использовать массовые копии, потому что я думал, что это должно быть сделано из файла ...

Теперь я использую это, и это занимает около 20 секунд (как задача SSIS)

  DataTable dataTable = new DataTable();

  dataTable.Columns.Add(new DataColumn("ixMyIndex", System.Type.GetType("System.Int32")));   
  dataTable.Columns.Add(new DataColumn("Year", System.Type.GetType("System.Int32")));   
  dataTable.Columns.Add(new DataColumn("Month", System.Type.GetType("System.Int32")));
  dataTable.Columns.Add(new DataColumn("Day", System.Type.GetType("System.Int32")));
 // ... and more to go

  DataRow dataRow;
  object[] objectRow = new object[dataTable.Columns.Count];

  Stream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read);

  StreamReader reader = new StreamReader(stream);
  char[] delimeter = new char[] { ' ' };
  String[] records;
  int recordCount = 0;
  while (!reader.EndOfStream)
  {
    records = reader.ReadLine().Split(delimeter, StringSplitOptions.None);

    dataRow = dataTable.NewRow();
    objectRow[0] = null; 
    objectRow[1] = Int32.Parse(records[0].Substring(0, 4));
    objectRow[2] = Int32.Parse(records[0].Substring(5, 2));
    objectRow[3] = Int32.Parse(records[0].Substring(8, 2));
    // my fancy stuf goes here

    dataRow.ItemArray = objectRow;         
    dataTable.Rows.Add(dataRow);

    recordCount++;
  }

  SqlBulkCopy bulkTask = new SqlBulkCopy(connection, SqlBulkCopyOptions.TableLock, null);
  bulkTask.DestinationTableName = "MyDestinationTable"; 
  bulkTask.BatchSize = dataTable.Rows.Count;
  bulkTask.WriteToServer(dataTable);
  bulkTask.Close();

Ответы [ 12 ]

0 голосов
/ 24 сентября 2008

Делайте модные вещи на данных, сначала на всех записях. Затем навальный-вставить их.

(так как вы не делаете выбор после вставки .. я не вижу проблемы применения всех операций с данными до BulkInsert

0 голосов
/ 24 сентября 2008

Я полагаю, что примерно 58 секунд занимает физическая вставка 500 000 записей, поэтому вы получаете около 10000 вставок в секунду. Не зная спецификации вашего сервера базы данных (я вижу, вы используете localhost, поэтому задержки в сети не должны быть проблемой), трудно сказать, хорошо это, плохо или ужасно.

Я бы посмотрел на схему вашей базы данных - есть ли в таблице несколько индексов, которые нужно обновлять после каждой вставки? Это может быть из других таблиц с внешними ключами, ссылающимися на таблицу, над которой вы работаете. В SQL Server встроены средства профилирования SQL и средства мониторинга производительности, но я никогда не использовал их. Но они могут обнаруживать проблемы, такие как замки и тому подобное.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...