Самый быстрый способ выполнения массового обновления в C#/.NET

Я пытаюсь выяснить, как лучше всего выполнить массовое обновление через мое мини-консольное приложение на сервере SQL. Я написал свой собственный способ массового обновления, например:

  SqlCommand command = new SqlCommand();
  command.Connection = new SqlConnection("Data Source=.;Initial Catalog=mydb;Integrated Security=SSPI");
  command.Connection.Open();

  for (int i = 0; i < items.Count; i = i + 1000)
  {
     var batchList = items.Skip(i).Take(1000).ToList();
     for (int j = 0; j < batchList.Count(); j++)
     {
       command.CommandText += string.Format("update Items set QuantitySold=@s_id{0} where ItemID = @id{0};", j);
       command.Parameters.AddWithValue("@s_id" + j, batchList[j].QuantitySold);
       command.Parameters.AddWithValue("@id" + j, batchList[j].ItemID);
      }
     command.ExecuteNonQuery();
     command = new SqlCommand();
     command.Connection = new SqlConnection("Data Source=.;Initial Catalog=mydb;Integrated Security=SSPI");
     command.Connection.Open();
            }
     command.Connection.Close();

Но я не очень доволен производительностью этого, обновление 50000-100000 записей в моей БД происходит довольно медленно, когда я делаю это так, даже если оно делает их партиями по 1000....

Есть ли какая-нибудь библиотека/решение, которое могло бы «ускорить процесс»?

Кто-нибудь может мне помочь ?


person User987    schedule 24.05.2017    source источник
comment
Это было бы НАМНОГО быстрее, если бы вы создали обновление на основе набора вместо того, чтобы делать отдельное обновление для каждой строки. Я бы подумал о создании параметра таблицы и перемещении его в хранимую процедуру.   -  person Sean Lange    schedule 24.05.2017
comment
создайте очередь в своей базе данных и сбрасывайте в нее записи, пусть процесс базы данных обновит данные. Вставки дешевые и быстрые.   -  person Marshall Tigerus    schedule 24.05.2017
comment
@SeanLange, не могли бы вы ответить в форме ответа, чтобы я мог понять, что именно вы имеете в виду? знак равно   -  person User987    schedule 24.05.2017
comment
@MarshallTigerus Как это сделать? :D   -  person User987    schedule 24.05.2017
comment
Ребята, любой ответ действительно ценится, чтобы я лучше понял, что вы имеете в виду ^^   -  person User987    schedule 24.05.2017
comment
Установить обновление — это то, что нужно, но еще одна причина, по которой оно медленное, заключается в том, что вы продолжаете открывать и закрывать соединения, и в этом нет необходимости, делайте это на одном.   -  person LB2    schedule 24.05.2017
comment
Кроме того, вам не нужно постоянно создавать параметры с разными номерами. Настройте свой командный объект один раз, а затем просто продолжайте изменять значение параметра (не имени), чтобы продолжать передавать обновления.   -  person LB2    schedule 24.05.2017
comment
@LB2 устанавливает обновление в хранимой процедуре, а затем просто сопоставляет процедуру с Entity framework n циклом по коллекции?   -  person User987    schedule 24.05.2017


Ответы (3)


Самый быстрый способ — массовая вставка данных во временную таблицу с помощью встроенного класса SqlBulkCopy, а затем обновление с помощью присоединения к этой таблице

Или вы можете использовать такой инструмент, как SqlBulkTools, который делает именно это простым способом.

var bulk = new BulkOperations();

using (TransactionScope trans = new TransactionScope())
{
    using (SqlConnection conn = new SqlConnection("Data Source=.;Initial Catalog=mydb;Integrated Security=SSPI")
    {
        bulk.Setup()
            .ForCollection(items)
            .WithTable("Items")
            .AddColumn(x => x.QuantitySold)
            .BulkUpdate()
            .MatchTargetOn(x => x.ItemID) 
            .Commit(conn);
    }

    trans.Complete();
}
person Magnus    schedule 24.05.2017
comment
Это действительно больше комментарий, чем ответ. И я не уверен, что согласен с тем, что это всегда будет самый быстрый способ сделать это. - person Sean Lange; 24.05.2017
comment
Ну, сначала я поставил это как комментарий, но, поскольку это правильный ответ, я подумал, что могу также поместить его как ответ. - person Magnus; 24.05.2017
comment
Это, безусловно, один из лучших способов сделать это. И производительность, как правило, очень хорошая. Есть много способов снять шкуру с этой кошки, и ваш — хороший. :) - person Sean Lange; 24.05.2017
comment
@Magnus, это бесплатная библиотека или платная, ссылку на которую вы разместили? - person User987; 24.05.2017
comment
Это бесплатная библиотека под лицензией MIT. - person Magnus; 24.05.2017
comment
@Magnus, хорошо, спасибо :) .. Извините за дополнительные вопросы ... Это может показаться немного странным, но я не могу найти ссылку на github или где я могу скачать справочную библиотеку? - person User987; 24.05.2017
comment
@Магнус, очень, сэр! знак равно - person User987; 24.05.2017
comment
Теперь это больше не указано в Nuget как устаревшее. Видимо, как-то продали. - person Josh Noe; 12.09.2018

Вы можете использовать Kros.KORM для массовых операций.

using (var database = new Database("connectionstring ...", "ado client name ..."))
{
    database
       .Query<Movie>()
       .AsDbSet()
       .BulkUpdate(_data);
}

Или, если вам не нужно использовать ORM и у вас есть средство чтения исходных данных, вы можете использовать классы SqlServerBulkInsert / SqlServerBulkUpdate или MsAccessBulkInsert / MsAccessBulkUpdate для выполнения массовых операций.

Например:

using (var bulkInsert = new SqlServerBulkInsert("connection string"))
{
    bulkInsert.Insert(reader);
}

Вы можете увидеть сравнение с чистыми командами ADO.NET https://github.com/Kros-sk/Kros.Libs/wiki

person Mino    schedule 02.05.2018
comment
Эй, только что проверил вашу библиотеку! Большинство примеров, настроенных на странице git, не работают, и, кроме того, отсутствует массовое удаление... Общая средняя производительность, хотя и большое улучшение по сравнению со стандартным ADO.NET! - person User987; 29.05.2018
comment
Спасибо за ваш отзыв. Работа над массовым удалением продолжается. это проблема в нашем репозитории. Возможно, наша документация еще не идеальна. пожалуйста, можете ли вы указать, какие примеры не работают, в идеале написать проблему прямо на github? вы поможете нам улучшить наш открытый исходный код. что ж, спасибо тебе. - person Mino; 30.05.2018
comment
Я попробовал SqlServerBulkUpdate, который хорошо работает для тысяч записей, но он выходит за пределы времени ожидания для 1 миллиона записей, есть идеи? Благодарность - person CDominik; 07.10.2020

Я не знаю, какие элементы находятся в вашем коде, поэтому я не знаю, как получить элементы в параметре с табличным значением. Я могу помочь с этим, если вам это нужно, но мне нужно знать, что это за объект.

Независимо от того, что вы могли бы сделать что-то подобное на стороне sql. Затем вы просто выполняете эту процедуру с вашей коллекцией элементов в качестве входящего параметра.

create type Items as TABLE
(
    ItemID int
    , Quantity int
)

GO

create procedure UpdateItemsBulk
(
    @Items Items READONLY
) as

    set nocount on;

    Update i
    set QuantitySold = items.Quantity
    from items i
    join @Items items on items.ItemID = i.ItemID

GO
person Sean Lange    schedule 24.05.2017
comment
Эй, Шон, большое спасибо за ответ :) ! Элементы - это сопоставленный класс уже существующей таблицы в моей БД. - person User987; 24.05.2017
comment
Если эти данные уже есть в БД, зачем тогда вам нужно так обновляться? Наверняка никто не просматривает 50-100 тыс. строк и не меняет эти значения вручную. Возможно, всю эту проблему можно было бы решить полностью в БД вообще без кода dotnet? - person Sean Lange; 24.05.2017