【问题标题】:SQL Server batch delete using WHILE loop does not work使用 WHILE 循环的 SQL Server 批量删除不起作用
【发布时间】:2013-03-21 21:04:33
【问题描述】:

我有一个非常大的表,所以我使用以下方法来删除旧条目:

WHILE (@@ROWCOUNT > 0)
BEGIN
    DELETE TOP (5000) FROM myTable
    WHERE date < 20130103
END

我已经使用不同的日期运行了几次。有时它可以正常工作(大约需要 20 分钟),但有时查询会立即完成并且没有任何内容被删除。发生这种情况时,我只需从该表中执行一个简单的 SELECT 语句,然后再次尝试上面的 WHILE 语句,然后它就可以工作了!有谁知道这是为什么?我需要自动执行此查询以定期运行以控制表大小,但我想确保它在运行时实际正确删除。谢谢。

【问题讨论】:

  • 日期不应该写成'2013-01-03'而不是数字吗?
  • 有趣的是,每个答案都完全错过了。
  • 另外请注意,仅仅有一个循环并不一定会减少对日志或并发的影响,具体取决于这是否是单个事务。我将停止使用@@ROWCOUNT 进行控制,在循环中添加事务,并设置一个变量=@@ROWCOUNT。见sqlperformance.com/2013/03/io-subsystem/chunk-deletes
  • 顺便@AndrewMorton,SQL Server 确实理解 '20130103' 与 '2013-01-03' 相同(在使用日期进行布尔逻辑时),但是是的,OP 必须在周围加上引号将其视为日期的数字。让您想知道 OP(以及下面的其他人)如何测试查询,因为当我尝试发布的语法时出现以下错误:“将表达式转换为数据类型日期时间的算术溢出错误”?
  • @AndrewJens Be Wary of Date Formatting in T-SQL 刚刚通过 SQL Server Central 时事通讯引起了我的注意。看起来没有破折号的格式比有破折号的格式更安全。

标签: sql sql-server rowcount


【解决方案1】:

在这段代码之前你在运行什么? @@ROWCOUNT 将设置为执行它的任何语句。如果您事先运行其他命令,它可能是0

相反,您可以强制初始计数为 1

DECLARE @Rows INT
SET @Rows = 1

WHILE (@Rows > 0)
BEGIN
    DELETE TOP (5000) FROM myTable
    WHERE date < 20130103

    SET @Rows = @@ROWCOUNT
END

【讨论】:

    【解决方案2】:

    大概是因为@@ROWCOUNT被初始化为0的值。

    你可以先运行这个查询来设置它:

    select count(*) from myTable where date < 20130103
    

    这会为您的查询增加一点时间,但您会看到被删除的行数。

    你也可以这样做:

    select top 1     * from myTable 
    

    这样会更快。

    【讨论】:

    • 你也可以在循环之前SELECT 1;
    【解决方案3】:

    这是因为有时@@ROWCOUNT 从零开始 - 所以while 循环永远不会执行,因为它会在每次执行之前检查条件,包括第一次执行。

    这是一个自制的 do-while 循环,因为 SQL Server 没有内置循环。

    loop:
       DELETE TOP (5000) FROM myTable
       WHERE date < 20130103
    if @@ROWCOUNT > 0 goto loop
    

    【讨论】:

    • SQL Server 没有WHILEloop?
    • @AaronBertrand 他在谈论do-while 循环。如:DO BEGIN &lt;something&gt; END WHILE &lt;condition&gt;(在检查条件之前总是运行一次“”)。 SQL Server 中不存在 THAT。
    【解决方案4】:

    当我批量删除时,我会添加一个等待延迟(至少 1 或 2 秒)。也是我在循环外创建的第一条语句。顺便说一句,避免使用 ROWCOUNT 作为分隔符 (https://docs.microsoft.com/en-us/sql/t-sql/statements/set-rowcount-transact-sql?view=sql-server-2017)。有两种选择:

    DECLARE @BatchSize BIGINT = 50000
    SET ROWCOUNT @BatchSize
    
    DELETE 
    FROM myTable 
    WHERE 
    date < 20130103
    
    WHILE (@@ROWCOUNT > 0)
    BEGIN
    WAITFOR DELAY '00:00:02'
    DELETE 
    FROM myTable 
    WHERE 
    date < 20130103
    END
    

    或者

    DECLARE @BatchSize BIGINT = 50000
    
    WHILE 1=1
    BEGIN
    
        WAITFOR DELAY '00:00:02'
    
        DELETE TOP(@BatchSize )
        FROM myTable 
        WHERE 
        date < 20130103
    
        IF @@ROWCOUNT < @BatchSize 
            Break
    END
    

    【讨论】:

      【解决方案5】:

      基本上,

      SELECT 0 -- rowcount is 1 
      WHILE (@@ROWCOUNT > 0)
      BEGIN
          DELETE TOP (5000) FROM myTable
          WHERE date < 20130103
      END
      

      或者

      SET ROWCOUNT 5000 -- set row count to 5000
      SELECT 0 -- rowcount is 1 
      WHILE (@@ROWCOUNT > 0)
      BEGIN
          DELETE FROM myTable
          WHERE date < 20130103
      END
      SET ROWCOUNT 0  -- set rowcount to unlimited
      

      【讨论】:

        【解决方案6】:

        你也可以这样写你的查询:

        SET ROWCOUNT 5000; -- set batch size
        WHILE EXISTS (SELECT 1 FROM myTable WHERE date < '2013-01-03')
        BEGIN
            DELETE FROM myTable
            WHERE date < '2013-01-03'
        END;
        SET ROWCOUNT 0; -- set batch size back to "no limit"
        

        无论哪种方式,您都应该正确地格式化您的日期字符串。

        请确保您的删除条件和您的存在子句中的语句相同,否则您可能会遇到无限循环。

        【讨论】:

        • 请注意,在 SQL 2012 之后:“在 SQL Server 的未来版本中使用 SET ROWCOUNT 将不会影响 DELETE、INSERT 和 UPDATE 语句。避免将 SET ROWCOUNT 与 DELETE、INSERT 和 UPDATE 语句一起使用新的开发工作,并计划修改当前使用它的应用程序。对于类似的行为,请使用 TOP 语法。"
        猜你喜欢
        • 1970-01-01
        • 2011-10-19
        • 1970-01-01
        • 2017-08-08
        • 1970-01-01
        • 2010-12-19
        • 1970-01-01
        • 2020-03-01
        • 2013-08-16
        相关资源
        最近更新 更多