如何批量执行 SQL UPDATE,例如 Update Top?

How can I do a SQL UPDATE in batches, like an Update Top?

提问人:Erik Dekker 提问时间:1/11/2012 更新时间:9/3/2020 访问量:59605

问:

是否可以向 SQL Update 语句添加 TOP 或某种分页?

我有一个查询,可以归结为这样:UPDATE

UPDATE XXX SET XXX.YYY = #TempTable.ZZZ
FROM XXX
INNER JOIN (SELECT SomeFields ... ) #TempTable ON XXX.SomeId=#TempTable.SomeId
WHERE SomeConditions

这次更新会影响数百万条记录,我需要分批进行。就像当时的 100.000 一样(排序无关紧要)

最简单的方法是什么?

sql-server-2008 t-sql

评论


答:

23赞 Eric Petroelje 1/11/2012 #1

是的,我相信您可以在更新语句中使用 TOP,如下所示:

UPDATE TOP (10000) XXX SET XXX.YYY = #TempTable.ZZZ
FROM XXX
INNER JOIN (SELECT SomeFields ... ) #TempTable ON XXX.SomeId=#TempTable.SomeId
WHERE SomeConditions

评论

10赞 Martin Smith 1/11/2012
TOP按照什么命令?第二批如何从第一批完成的地方开始?
0赞 ean5533 1/11/2012
一种可能性是在更新时标记每条记录,然后添加另一个子句,忽略已更新的记录。这需要临时向表中添加一列(除非有一些有效的方法来判断记录是否已通过更新本身进行更新)
0赞 Erik Dekker 1/11/2012
呃,这很简单..我用 UPDATE TOP 100000 进行了测试,但这给出了一个 ,Update TOP (100000) 似乎有效。@MartinSmith:“第二批”在我的情况下并不重要。当第一批更新时,它将不再与 where 条件匹配。Incorrect syntax near 100000
5赞 Eric Petroelje 1/11/2012
@Martin - 运算符不需要 ,因此大概是按聚簇索引顺序排列的。至于让第二批从第一批中断的地方继续,您需要在子句中添加一些内容来过滤掉已经更新的记录。很难确切地说那会是什么样子,但如果不知道有关他的查询的更多信息。TOPORDER BYWHERE
1赞 Eric Petroelje 1/11/2012
@Erik - 是的,奇怪的是,在更新中使用时需要括号(但在选择中不需要..)TOP
3赞 user806549 1/11/2012 #2

根据您更改表数据结构的能力,我建议您在表中添加一个可以容纳某种批处理标识器的字段。即。如果您每天这样做,它可以是日期戳、增加值或基本上可以为您的批次设置唯一的任何值。如果采用增量方法,则更新将为:

UPDATE TOP (100000) XXX SET XXX.BATCHID = 1, XXX.YYY = ....
...
WHERE XXX.BATCHID < 1 
  AND (rest of WHERE-clause here).

下次,您将设置 BATCHID = 2 和WHERE XXX.BATCHID < 2

如果要重复执行此操作,则可以在 BATCHID 上设置索引并减少服务器上的负载。

5赞 Michał Powaga 1/11/2012 #3

您可以使用 SET ROWCOUNT { number | @number_var } 来限制在停止特定查询之前处理的行数,示例如下:

SET ROWCOUNT 10000 -- define maximum updated rows at once

UPDATE XXX SET 
    XXX.YYY = #TempTable.ZZZ
FROM XXX
INNER JOIN (SELECT SomeFields ... ) #TempTable ON XXX.SomeId = #TempTable.SomeId
WHERE XXX.YYY <> #TempTable.ZZZ and OtherConditions

-- don't forget about bellow 
-- after everything is updated
SET ROWCOUNT 0

我已添加 to 子句,以确保您不会更新已更新的值两次。XXX.YYY <> #TempTable.ZZZwhere

设置为关闭限制 - 不要忘记它ROWCOUNT0

评论

0赞 Erik Dekker 1/12/2012
也是一个很好的有效答案,但我只能接受一个。另一个答案是做我想做的事的最短和最快的方法。
2赞 Jim 7/11/2013
不推荐使用 SET ROWCOUNT 来限制 UPDATE、INSERT 和 DELETE 语句。从 SQL 2005 开始,应该使用 TOP。
0赞 jacoblambert 10/5/2016
以上评论是有帮助的;以下是对此的一些支持: sqlstudies.com/2013/10/07/use-top-instead-of-set-rowcount
4赞 W. Nema 8/26/2016 #4

您可以执行如下操作

declare @i int = 1
while @i <= 10 begin

    UPDATE  top (10) percent
            masterTable set colToUpdate = lt.valCol
    from    masterTable as mt
            inner join lookupTable as lt
                    on mt.colKey = lt.colKey
    where colToUpdate is null

    print @i
    set @i += 1
end

--one final update without TOP (assuming lookupTable.valCol is mostly not null)
UPDATE  --top (10) percent
        masterTable set colToUpdate = lt.valCol
from    masterTable as mt
        inner join lookupTable as lt
                on mt.colKey = lt.colKey            
where colToUpdate is null
1赞 Ani 9/3/2020 #5
DECLARE @updated_Rows INT;
SET @updated_Rows = 1;
WHILE (@updated_Rows > 0)
BEGIN
UPDATE top(10000) XXX SET XXX.YYY = #TempTable.ZZZ FROM XXX
INNER JOIN  #TempTable ON XXX.SomeId=#TempTable.SomeId
WHERE SomeConditions
SET @updated_Rows = @@ROWCOUNT;
END