Самый быстрый способ вставить 30 тысяч строк в временную таблицу на SQL Server с помощью С#
Я пытаюсь выяснить, как улучшить производительность вставки во временной таблице на SQL Server с помощью С#. Некоторые люди говорят, что я должен использовать SQLBulkCopy, но я должен делать что-то неправильно, поскольку он работает намного медленнее, чем просто создавать строку вставки SQL.
Мой код для создания таблицы с использованием SQLBulkCopy приведен ниже:
public void MakeTable(string tableName, List<string> ids, SqlConnection connection)
{
SqlCommand cmd = new SqlCommand("CREATE TABLE ##" + tableName + " (ID int)", connection);
cmd.ExecuteNonQuery();
DataTable localTempTable = new DataTable(tableName);
DataColumn id = new DataColumn();
id.DataType = System.Type.GetType("System.Int32");
id.ColumnName = "ID";
localTempTable.Columns.Add(id);
foreach (var item in ids)
{
DataRow row = localTempTable.NewRow();
row[0] = item;
localTempTable.Rows.Add(row);
localTempTable.AcceptChanges();
}
using (SqlBulkCopy bulkCopy = new SqlBulkCopy(connection))
{
bulkCopy.DestinationTableName = "##" + tableName;
bulkCopy.WriteToServer(localTempTable);
}
}
Таким образом, мои вставки занимают много времени. Я получил свои вставки для работы быстрее по-другому:
Я создал бит inserts как строку и присоединил его в инструкции SQL create temp table:
Создание строки вставки:
public string prepareInserts(string tableName, List<string> ids)
{
List<string> inserts = new List<string>();
var total = ids.Select(p => p).Count();
var size = 1000;
var insert = 1;
var skip = size * (insert - 1);
var canPage = skip < total;
while (canPage)
{
inserts.Add(" insert into ##" + tableName + @" (ID) values " + String.Join(",", ids.Select(p => string.Format("({0})", p))
.Skip(skip)
.Take(size)
.ToArray()));
insert++;
skip = size * (insert - 1);
canPage = skip < total;
}
string joinedInserts = String.Join("\r\n", inserts.ToArray());
return joinedInserts;
}
Используя их в инструкции SQL после создания запроса:
inserts = prepareInserts(tableName, ids);
var query = @"IF EXISTS
(
SELECT *
FROM tempdb.dbo.sysobjects
WHERE ID = OBJECT_ID(N'tempdb..##" + tableName + @"')
)
BEGIN
DELETE FROM ##" + tableName + @"
END
ELSE
BEGIN
CREATE TABLE ##" + tableName + @"
(ID int)
END " + inserts;
var command = new SqlCommand(query, sqlConnection);
...
Поскольку я видел, как люди говорили мне (при обмене столами https://dba.stackexchange.com/info/44217/fastest-way-to-insert-30-thousand-rows-in-sql-server/44222?noredirect=1#comment78137_44222), я должен использовать SQLBulkCopy, и это было бы быстрее, я считаю, что я должен улучшить Я это делаю. Поэтому, если кто-нибудь может предложить, как я могу улучшить свой код SQLBulkCopy, или скажите мне, есть ли лучший оператор insert, который может улучшить производительность моего приложения, что было бы здорово.
Ответы
Ответ 1
Ваша проблема может быть в localTempTable.AcceptChanges();
Поскольку она фиксирует ваши изменения.
Если вы сделаете следующее, я думаю, что он будет работать быстрее
foreach (var item in ids)
{
DataRow row = localTempTable.NewRow();
row[0] = item;
localTempTable.Rows.Add(row);
}
localTempTable.AcceptChanges();
using (SqlBulkCopy bulkCopy = new SqlBulkCopy(connection))
{
bulkCopy.DestinationTableName = "##" + tableName;
bulkCopy.WriteToServer(localTempTable);
}
Из MSDN - DataSet.AcceptChanges
Выполняет все изменения, внесенные в этот DataSet с момента его загрузки или с момента последнего вызова AcceptChanges.
Ответ 2
Я запускаю этот код сам с объектами StopWatch для измерения времени. Его AcceptChanges на каждой итерации делает медленным.
public void MakeTable(string tableName, List<string> ids, SqlConnection connection)
{
SqlCommand cmd = new SqlCommand("CREATE TABLE ##" + tableName + " (ID int)", connection);
cmd.ExecuteNonQuery();
DataTable localTempTable = new DataTable(tableName);
DataColumn id = new DataColumn();
id.DataType = System.Type.GetType("System.Int32");
id.ColumnName = "ID";
localTempTable.Columns.Add(id);
System.Diagnostics.Stopwatch sw1 = new System.Diagnostics.Stopwatch();
sw1.Start();
foreach (var item in ids)
{
DataRow row = localTempTable.NewRow();
row[0] = item;
localTempTable.Rows.Add(row);
}
localTempTable.AcceptChanges();
long temp1 = sw1.ElapsedMilliseconds;
sw1.Reset();
using (SqlBulkCopy bulkCopy = new SqlBulkCopy(connection))
{
bulkCopy.DestinationTableName = "##" + tableName;
bulkCopy.WriteToServer(localTempTable);
}
long temp2 = sw1.ElapsedMilliseconds;
}
Результат, когда AccpetChanges находится внутри цикла foreach
![enter image description here]()
И когда его не
![enter image description here]()
Разница на 3 порядка:)
Ответ 3
Используйте IDataReader, и он будет работать еще быстрее
вместо cmd.ExecuteNonQuery();
Выполнить
cmd.ExecuteReader()