如何批量更新SQL表?

如何批量更新SQL表?,sql,tsql,Sql,Tsql,我已经花了一些时间试图弄明白这一点,但我仍然有点卡住了,我不能真正找到在线解决方案,因为我认为我错过了关键字 我想批量更新一个SQL表,这意味着我有几百万个条目,并想一步一步地更新索引0-999、1000-1999,以避免巨大的数据库锁定 这就是我发现的: DECLARE @Rows INT, @BatchSize INT; SET @BatchSize = 2500; SET @Rows = @BatchSize; WHILE (@Rows = @BatchSize) BE

我已经花了一些时间试图弄明白这一点,但我仍然有点卡住了,我不能真正找到在线解决方案,因为我认为我错过了关键字

我想批量更新一个SQL表,这意味着我有几百万个条目,并想一步一步地更新索引0-999、1000-1999,以避免巨大的数据库锁定

这就是我发现的:

DECLARE @Rows INT,
        @BatchSize INT;

SET @BatchSize = 2500;
SET @Rows = @BatchSize;

WHILE (@Rows = @BatchSize)
BEGIN
    UPDATE TOP(@BatchSize) db1
    SET db1.attr = db2.attr
    FROM DB1 db1
    LEFT JOIN DB2 db2
    ON db1.attr2 = db2.attr2

    SET @Rows = @@ROWCOUNT;
END;
正如你们所看到的,我把我的陈述简化了一点,但我是如何处理整个问题的,这一点应该仍然很清楚

然而,这个东西永远循环,当查看输出时,它更改的行比数据库中的行多得多

稍后,我用select语句检查了同一个循环,发现它似乎只是不断地选择表的第一个@BatchSize行,尽管我认为每次迭代都会在索引中进行


我如何改变这一点,使其在每次迭代中都通过@BatchSize索引而不是每次都简单地针对相同的行来进行实际操作?

您需要一些限制因素来决定每个循环中哪些行被命中。通常,您将使用id字段。有很多方法可以解决这个问题,但这里有一种方法:

DECLARE @MinID int = 1;
DECLARE @MaxID int = 2500;
DECLARE @Rows int = 1;
DECLARE @Batchsize int = 2500;

WHILE (@Rows > 1)
BEGIN
       UPDATE db1
       SET db1.attr = db2.attr
       FROM DB1 db1
       LEFT JOIN DB2 db2 ON db1.attr2 = db2.attr2
       WHERE db1.ID BETWEEN @MinID AND MaxID

SET @Rows = @@ROWCOUNT
SET @MinID = MinID + @Batchsize
SET @MaxID = MaxID + @Batchsize

END
将db1.ID替换为表架构中效果最好的字段

请注意,如果在update查询中有某种WHERE子句阻止返回相同的行,那么您的方法将起作用


例如,更新表集合id=1,其中id=2不会在第二次执行中提取相同的行

您只是在更新相同的行。需要一个和

左会合?如果您真的想分配空值,那么使用单独的更新

DECLARE @Rows INT,
        @BatchSize INT;

SET @BatchSize = 2500;
SET @Rows = @BatchSize;

WHILE (@Rows = @BatchSize)
BEGIN
    UPDATE TOP(@BatchSize) db1
    SET db1.attr = db2.attr
    FROM DB1 db1
    JOIN DB2 db2
      ON db1.attr2 = db2.attr2 
     AND db1.attr <> db2.attr

    SET @Rows = @@ROWCOUNT;
END;
你可以这样做:

select 1
WHILE (@@ROWCOUNT > 0)
BEGIN
    UPDATE TOP(2000) db1
    SET db1.attr = db2.attr
    FROM DB1 db1
    JOIN DB2 db2
      ON db1.attr2 = db2.attr2 
     AND db1.attr <> db2.attr
END;

一种方法是使用带有行号的cte:

DECLARE @BatchSize int = 2500,
        @LastRowUpdated int = 0;
        @Count int


SELECT @Count = COUNT(*) FROM db1;


;WITH CTE AS
(
    SELECT  attr, 
            attr2,
            ROW_NUMBER() OVER(ORDER BY attr, atrr2) As RN
    FROM db1
)

WHILE @LastRowUpdated < @Count
BEGIN

    UPDATE c
    SET attr = db2.atrr
    FROM CTE c
    LEFT JOIN DB2 ON c.attr2 = db2.attr2
    WHERE c.RN > @LastRowUpdated
    AND c.RN < (@LastRowUpdated +1) * @BatchSize

    SELECT @LastRowUpdated += 1

END

这将在循环的每个步骤更新2500条记录。

mysql tsql..由于代码看起来像tsql,我删除了mysql标记。TOP N和no order by,可以选择相同的TOP N-更新后,没有任何内容会改变该行以将其从TopN的下一次迭代中排除,我可以看到第一次迭代0到2500,第二次迭代,2500到5000-第2500行上有一个双行,因为中间是包含在内的,虽然我认为这对这个示例不重要,但值得注意。