【问题标题】:Initial value of @@ROWCOUNT varies across databases and servers@@ROWCOUNT 的初始值因数据库和服务器而异
【发布时间】:2016-03-07 22:32:30
【问题描述】:

我们正在尝试在WHILE 循环内运行DELETE 语句(以避免大量行的大型事务日志),如下所示:

WHILE (@@ROWCOUNT > 0)
    BEGIN
        DELETE TOP (250000)
        FROM
            MYDATABASE.MYSCHEMA.MYTABLE
        WHERE
            MYDATABASE.MYSCHEMA.MYTABLE.DATE_KEY = 20160301
    END

当在我们的开发环境中的新 SQL Server Management Studio 连接中执行此命令时,它会删除 250K 块中的行,这是预期的行为。

当这个命令在我们的test服务器上以同样的方式执行时,我们会得到消息

Command completed successfully

即语句运行时没有进入WHILE循环。

经过一些额外的调查,我们发现行为也会因我们连接的数据库而异。因此,如果在 SQL Server Management Studio 连接到 MYDATABASE 时运行代码(在我们的 test 环境中),则 DELETE 语句不会运行。如果我们在连接到SOME_OTHER_DATABASE 时运行代码,它会运行。

我们部分怀疑@@ROWCOUNT 的值不可靠,并且可能因连接不同而不同。但是,当我们为每个数据库和服务器组合多次运行代码时,我们会看到 100% 一致的行为。所以@@ROWCOUNT 的随机初始值似乎无法解释问题。

关于这里可能发生的事情有什么建议吗?感谢您的帮助!

编辑#1

对于那些询问@@ROWCOUNT 的初始值以及它来自何处的人,我们不确定。但在某些情况下,@@ROWCOUNT 肯定会被初始化为高于零的某个值,因为代码原样在新连接上工作。

编辑#2

对于那些提议声明我们自己的变量的人,对于我们的特定应用程序,我们通过编程语言包装器执行 SQL 命令,该包装器一次只允许执行一条语句(即一个分号)。

我们之前曾尝试通过在循环前执行一条删除语句来确定@@ROWCOUNT 的值:

声明 #1:

DELETE TOP (250000)
FROM
    MYDATABASE.MYSCHEMA.MYTABLE
WHERE
    MYDATABASE.MYSCHEMA.MYTABLE.DATE_KEY = 20160301

声明 #2(@@ROWCOUNT 现在大概是 250,000):

WHILE (@@ROWCOUNT > 0)
    BEGIN
        DELETE TOP (250000)
        FROM
            MYDATABASE.MYSCHEMA.MYTABLE
        WHERE
            MYDATABASE.MYSCHEMA.MYTABLE.DATE_KEY = 20160301
    END

然而,任何导致@@ROWCOUNT 在启动时呈现不同值的因素也会影响命令之间的值。所以在某些情况下,第二条语句永远不会执行。

【问题讨论】:

    标签: sql sql-server rowcount


    【解决方案1】:

    在设置变量值之前,您不应该使用变量。这同样适用于系统变量。

    您拥有的代码非常危险。有人可以在delete 之后添加类似SELECT 'Here I am in the loop' 的内容,它会中断。

    更好的方法?使用你自己的变量:

    DELCARE @RC int;
    WHILE (@RC > 0 OR @RC IS NULL)
        BEGIN
            DELETE TOP (250000)
            FROM MYDATABASE.MYSCHEMA.MYTABLE
            WHERE MYDATABASE.MYSCHEMA.MYTABLE.DATE_KEY = 20160301;
    
            SET @RC = @@ROWCOUNT;
        END;
    

    【讨论】:

      【解决方案2】:

      您的初始@@ROWCOUNT 是从哪里获得的?我的意思是,你永远不会进入那个区块,因为 @@ROWCOUNT 应该是零,所以你永远不会进入循环。此外,以 250K 批次删除不会改变您的事务日志的大小 - 如果您正在记录,所有删除都将被记录,因此在循环中执行此操作没有任何好处(和一些惩罚)。

      【讨论】:

      • 我的印象是在每个BEGIN ... END 块之后清除事务日志。对于涉及数千万条记录的开销较低的删除语句,您建议采用哪种方法?
      • 不。保留事务日志,以便您可以在完整备份的基础上进行构建。执行完整备份会告诉 SQL Server 您有一个新的起点,因此它可以清空该事务日志。如果您不想要交易,您可以truncate log,但……我认为这不是一个好主意。
      【解决方案3】:

      您是否跟踪过会话?由于@@ROWCOUNT 返回受会话中先前语句影响的行数,我猜想要么作为建立会话的一部分而执行的最后一个查询 SSMS 在两个环境中返回不同的行数,要么您有登录在最后一条语句返回不同行数的一个或其他环境中触发。无论哪种方式,跟踪都应该告诉您行为不同的确切原因。

      不过,从根本上说,在运行您有兴趣获取计数的语句之前引用@@ROWCOUNT 是没有意义的。使用变量很容易解决这个问题

      DECLARE cnt integer = -1;
      WHILE (cnt != 0)
      BEGIN
        DELETE TOP (250000)
          FROM MYDATABASE.MYSCHEMA.MYTABLE
         WHERE MYDATABASE.MYSCHEMA.MYTABLE.DATE_KEY = 20160301;
        SET cnt = @@ROWCOUNT;
      END
      

      【讨论】:

        猜你喜欢
        • 2013-04-23
        • 1970-01-01
        • 2021-02-12
        • 2019-04-23
        • 2011-04-02
        • 2017-06-10
        • 1970-01-01
        • 2014-01-03
        • 1970-01-01
        相关资源
        最近更新 更多