简体   繁体   中英

Batch update in T-SQL

I am using following script to delete entries from database in small batches:

SET @r = 1;
WHILE @r > 0
BEGIN 
  DELETE TOP (100000)
    [doc].[Document]
    WHERE Id IN (SELECT d.Id FROM @documentIds d); 
  SET @r = @@ROWCOUNT;
END

how can I update the table in same manner? I do not have LIMIT and OFFSET in T-SQL . I am also considering performance aspects.

You can update from the temporary table and then delete (or invalidate) rows in that table. Something like this:

SET @r = 1;
WHILE @r > 0
BEGIN 
    UPDATE d
        SET col = dd.col
        FROM doc.Document d JOIN
             (SELECT TOP 10000 FROM @documents dd ORDER BY id) dd
             ON d.id = dd.id;

    DELETE d TOP 10000 FROM (SELECT TOP 10000 @documents ORDER BY id) d;

    SET @r = @@ROWCOUNT;
END;

Try this, use start and end values and increment it in batches

DECLARE @Batch  INT
        ,@StartId BIGINT
        ,@EndId BIGINT
        ,@r     INT

SELECT  @Batch  = 10000
        ,@StartId = 1
        ,@EndId = 0
        ,@r     = 1

WHILE @r > 0
BEGIN 
    SET @StartId = @EndId + 1
    SET @EndId = @EndId + @Batch

    UPDATE d
        SET col = dd.col
        FROM doc.Document d 
        INNER JOIN @documents dd ON d.id = dd.id
            AND dd.id BETWEEN @StartId AND @EndId

    SET @r = @@ROWCOUNT

END

above approach work only when you have ids in sequence, otherwise use this approach to generate batches beforehand and use it, this will make sure that each time 10000 record get updated.

DECLARE @Batch  INT
        ,@StartId BIGINT
        ,@EndId BIGINT
        ,@Cnt INT
        ,@TotalIds INT

DECLARE @Docs TABLE 
(
        StartId BIGINT, 
        EndId BIGINT, 
        BatchID INT
)

SELECT  @Batch  = 10000
        ,@StartId = 1
        ,@EndId = 0
        ,@Cnt = 1
        ,@TotalIds = 0

;WITH CTE_Docs AS
(   SELECT TOP (100) PERCENT id, ROW_NUMBER() OVER (ORDER BY id) as RowID   -- Give seq numbers to each row
    FROM @documentIds d 
)       
-- create batches and batch start and end point
INSERT INTO @Docs(StartId, EndId, BatchId ) 
SELECT  MIN(id) StartID, 
        MAX(id) EndID, 
        (RowID/@Batch)+1 AS BatchID             
FROM CTE_Docs 
GROUP BY RowID/@Batch
ORDER BY BatchID

-- get counter to loop through
SELECT @TotalIds = MAX(BatchID)
FROM @Docs

WHILE @Cnt <= @TotalIds BEGIN   

    SELECT  @StartID    = StartID,
            @EndID      = EndID
    FROM @Docs
    WHERE BatchID = @Cnt

    UPDATE d
        SET col = dd.col
        FROM doc.Document d 
        INNER JOIN @documents dd ON d.id = dd.id
            AND dd.id BETWEEN @StartId AND @EndId

    SET @Cnt = @Cnt + 1

END

Hope this helps.

Do this. This will delete 1000 records every 300 milliseconds. But the good thing what I do here, is that I release the transaction, and allow another transaction to complete. Because there could be another CRUD statement. This query will not block transactions, I use this to DELETE/UPDATE millions of records on a production server. Because the answers I see, will still block other transactions, because the process will still be attached to a transaction which will cause high CPU and DISK IO. Also I set the DEADLOCK PRIORITY to low so another transaction has higher importance. It does take longer. But this is SAFER for Server costs and no blocked transactions.

SET DEADLOCK_PRIORITY -10

DECLARE @r = 1;

WHILE @r > 0 > 0
BEGIN 
    DELETE TOP (1000)
    [doc].[Document]
    WHERE Id IN (SELECT d.Id FROM @documentIds d); 
    SET @r = @@ROWCOUNT;

    WAITFOR DELAY '00:00:00.300'

END

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM