【问题标题】:Batch update in T-SQLT-SQL 中的批量更新
【发布时间】:2016-11-02 10:26:56
【问题描述】:

我正在使用以下脚本从数据库中小批量删除条目:

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

如何以同样的方式更新表格?我在T-SQL 中没有LIMITOFFSET。我也在考虑性能方面。

【问题讨论】:

标签: sql sql-server tsql batch-processing


【解决方案1】:

您可以从临时表中更新,然后删除(或无效)该表中的行。像这样的:

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;

【讨论】:

    【解决方案2】:

    试试这个,使用开始和结束值并分批递增

    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
    

    上述方法只有在你有序列的id时才有效,否则使用这种方法预先生成批次并使用它,这将确保每次更新10000条记录。

    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
    

    希望这会有所帮助。

    【讨论】:

    • Id 不是连续的。它们之间可以有空格
    • 我为非顺序 ID 添加了另一种方法
    【解决方案3】:

    这样做。这将每 300 毫秒删除 1000 条记录。但我在这里所做的好事是我释放了事务,并允许另一个事务完成。因为可能有另一个 CRUD 语句。这个查询不会阻塞事务,我用它来删除/更新生产服务器上的数百万条记录。因为我看到的答案仍然会阻止其他事务,因为该进程仍将附加到会导致高 CPU 和磁盘 IO 的事务。此外,我将 DEADLOCK PRIORITY 设置为低,因此另一个事务具有更高的重要性。确实需要更长的时间。但这对于服务器成本和没有阻塞的交易来说更安全。

    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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-05-05
      • 1970-01-01
      • 1970-01-01
      • 2014-01-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多