How to update large table with millions of rows in SQL Server?

后端 未结 6 845
面向向阳花
面向向阳花 2020-11-28 10:07

I\'ve an UPDATE statement which can update more than million records. I want to update them in batches of 1000 or 10000. I tried with @@ROWCOUNT bu

6条回答
  •  时光取名叫无心
    2020-11-28 10:29

    1. You should not be updating 10k rows in a set unless you are certain that the operation is getting Page Locks (due to multiple rows per page being part of the UPDATE operation). The issue is that Lock Escalation (from either Row or Page to Table locks) occurs at 5000 locks. So it is safest to keep it just below 5000, just in case the operation is using Row Locks.

    2. You should not be using SET ROWCOUNT to limit the number of rows that will be modified. There are two issues here:

      1. It has that been deprecated since SQL Server 2005 was released (11 years ago):

        Using SET ROWCOUNT will not affect DELETE, INSERT, and UPDATE statements in a future release of SQL Server. Avoid using SET ROWCOUNT with DELETE, INSERT, and UPDATE statements in new development work, and plan to modify applications that currently use it. For a similar behavior, use the TOP syntax

      2. It can affect more than just the statement you are dealing with:

        Setting the SET ROWCOUNT option causes most Transact-SQL statements to stop processing when they have been affected by the specified number of rows. This includes triggers. The ROWCOUNT option does not affect dynamic cursors, but it does limit the rowset of keyset and insensitive cursors. This option should be used with caution.

      Instead, use the TOP () clause.

    3. There is no purpose in having an explicit transaction here. It complicates the code and you have no handling for a ROLLBACK, which isn't even needed since each statement is its own transaction (i.e. auto-commit).

    4. Assuming you find a reason to keep the explicit transaction, then you do not have a TRY / CATCH structure. Please see my answer on DBA.StackExchange for a TRY / CATCH template that handles transactions:

      Are we required to handle Transaction in C# Code as well as in Store procedure

    I suspect that the real WHERE clause is not being shown in the example code in the Question, so simply relying upon what has been shown, a better model would be:

    DECLARE @Rows INT,
            @BatchSize INT; -- keep below 5000 to be safe
    
    SET @BatchSize = 2000;
    
    SET @Rows = @BatchSize; -- initialize just to enter the loop
    
    BEGIN TRY    
      WHILE (@Rows = @BatchSize)
      BEGIN
          UPDATE TOP (@BatchSize) tab
          SET    tab.Value = 'abc1'
          FROM  TableName tab
          WHERE tab.Parameter1 = 'abc'
          AND   tab.Parameter2 = 123
          AND   tab.Value <> 'abc1' COLLATE Latin1_General_100_BIN2;
          -- Use a binary Collation (ending in _BIN2, not _BIN) to make sure
          -- that you don't skip differences that compare the same due to
          -- insensitivity of case, accent, etc, or linguistic equivalence.
    
          SET @Rows = @@ROWCOUNT;
      END;
    END TRY
    BEGIN CATCH
      RAISERROR(stuff);
      RETURN;
    END CATCH;
    

    By testing @Rows against @BatchSize, you can avoid that final UPDATE query (in most cases) because the final set is typically some number of rows less than @BatchSize, in which case we know that there are no more to process (which is what you see in the output shown in your answer). Only in those cases where the final set of rows is equal to @BatchSize will this code run a final UPDATE affecting 0 rows.

    I also added a condition to the WHERE clause to prevent rows that have already been updated from being updated again.

提交回复
热议问题