2

I'm trying to figure out whats the best possible way to perform a bulk update via my mini console application in SQL server. I have written my own way of bulk update like following:

  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();

But I'm not so happy with the performance of this one, updating 50000-100000 records in my DB gets quite slow when doing it like this, even tho it does them in batches of 1000....

Is there any library/solution out there that could "speed things up"?

Can someone help me out ?

User987
  • 3,663
  • 15
  • 54
  • 115
  • 3
    This would be a LOT faster if you created a set based update instead of doing a separate update for each row. I would consider creating a table parameter and moving this to a stored procedure. – Sean Lange May 24 '17 at 19:02
  • create a queue in your database and dump records into it, let a database process update the data. Inserts are cheap and fast. – Marshall Tigerus May 24 '17 at 19:03
  • @SeanLange could you reply in a form of answe so that I can see what do you exactly mean ? =) – User987 May 24 '17 at 19:04
  • @MarshallTigerus How to do that ? :D – User987 May 24 '17 at 19:04
  • Guys any answer is really really appreciated so that I get a better insight of what you mean ^^ – User987 May 24 '17 at 19:05
  • 1
    Set update is the way to go, but another reason it is slow is because you keep opening and closing connections, and there is no need to do it, do it on a single one. – LB2 May 24 '17 at 19:05
  • 1
    And also, you don't need to keep creating parameters with different numbers. Set up your command object once, then just keep changing value of parameter (not name) to keep passing updates. – LB2 May 24 '17 at 19:06
  • @LB2 set update within a stored procedure, and then simply map the procedure with Entity framework n loop through the collection ? – User987 May 24 '17 at 19:07

3 Answers3

8

The fastest way would be to bulk insert the data into temporary table using the built in SqlBulkCopy Class, and then update using join to that table

Or you can use a tool such as SqlBulkTools which does exactly this in an easy way.

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();
}
Magnus
  • 45,362
  • 8
  • 80
  • 118
  • 1
    This is really more of a comment than an answer. And I am not sure I agree that is always going to be the fastest way to do this. – Sean Lange May 24 '17 at 19:10
  • 2
    Well I put it as a comment first, but since it is the correct answer I thought I might as well put it as an answer. – Magnus May 24 '17 at 19:12
  • It is certainly one of the best ways of doing this. And the performance is usually pretty good. There are many ways to skin this cat and yours is a good one. :) – Sean Lange May 24 '17 at 19:15
  • @Magnus is this a free library or a paid one , for which u posted the reference? – User987 May 24 '17 at 19:33
  • It is a free library under the [MIT license](https://en.wikipedia.org/wiki/MIT_License) – Magnus May 24 '17 at 19:35
  • @Magnus a okay thx :) .. Sry for extra questions ... It may seem abit weird, but I'm not able to find a github link to this one or where I cna download the reference library? – User987 May 24 '17 at 19:41
  • 1
    Nuget package: https://www.nuget.org/api/v2/package/SqlBulkTools/4.5.0. Or type: `Install-Package SqlBulkTools -Version 4.5.0` – Magnus May 24 '17 at 19:46
  • @Magnus ty very much sir ! =) – User987 May 24 '17 at 19:48
  • This is now longer listed on Nuget as obsolete. Apparently it's been "sold" in some way. – Josh Noe Sep 12 '18 at 19:36
1

You can use Kros.KORM for bulk operation.

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

Or if you do not need to use ORM and have the source data reader available, you can use the SqlServerBulkInsert / SqlServerBulkUpdate or MsAccessBulkInsert / MsAccessBulkUpdate classes to perform bulk operations.

For example:

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

You can see comparison with pure ADO.NET commands https://github.com/Kros-sk/Kros.Libs/wiki

Mino
  • 305
  • 2
  • 12
  • Hey just tested your library! Most of the examples set up on the git page are not working and plus it's lacking bulk delete... Overall average performance, although big improvement over standard ADO.NET ! – User987 May 29 '18 at 16:29
  • thank you for your feedback. Work on bulk delete is in progress. it's a issue in our repository. It is possible that our documentation is still not perfect. please can you specify which examples do not work, ideally write issue directly on github? you will help us improve our open source. well thank you. – Mino May 30 '18 at 18:52
  • I tried SqlServerBulkUpdate which works well for thousands of records, but it goes in time out exception for 1milion of records, any idea? thanks – CDominik Oct 07 '20 at 17:54
0

I don't know what items is in your code so I don't know how to get the items into a table valued parameter. I can help with that if you need it but I would need to know what that object is.

Regardless you could do something like this on the sql side. Then you simply execute this procedure with your items collection as inbound parameter.

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
Sean Lange
  • 33,028
  • 3
  • 25
  • 40
  • Hey Sean thanks alot for the reply :) ! The Items is the mapped class of the already existing table in my DB – User987 May 24 '17 at 19:11
  • If this data is already in the DB why do you need to update like this then? Surely nobody is going through 50-100k rows and changing those values manually. Perhaps this whole challenge could be solved entirely in the DB with no dotnet code at all? – Sean Lange May 24 '17 at 19:18