在UPDATE可靠地测量*匹配*行之后是@@ ROWCOUNT吗?

时间:2014-05-30 12:21:45

标签: sql-server tsql

@@ROWCOUNT是否可靠地告诉您WHERE匹配 UPDATE子句的行数,而不是实际更改的数量< / em>由它?

the documentation for @@ROWCOUNT中说:

  

数据操作语言(DML)语句将@@ROWCOUNT值设置为查询的受影响行数,并将该值返回给客户端。

(我的重点。)

但如果我有

CREATE TABLE [Foo] ([a] INT, [b] INT)
GO
INSERT INTO [Foo] ([a], [b]) VALUES (1, 1),(1, 2),(1, 3),(2, 2)
GO
UPDATE [Foo] SET [b] = 1 WHERE [a] = 1
SELECT @@ROWCOUNT
GO

...我看到3(匹配[a] = 1的行数),而不是2(由UPDATE修改的行数 - 三个中的一个行已经具有1的值b。这似乎是“受影响”的一个奇怪的定义(不是错误的,与我通常使用这个词的方式不一致 - 事实上它实际上非常方便我想做的事。) / p>

(例如,类似的MySQL ROW_COUNT函数在这种情况下会返回2。)

这种可靠的行为,理想情况下记录在某些我还没有找到的地方吗?或者有奇怪的边缘情况......

要明确:我不会问3是否是正确的答案。我问的是它是否是可靠答案,或者是否存在SQL Server将遗漏匹配但不需要更改的行的边缘情况。

更新:有几个人问过(或暗示)我担心的“可靠性”问题。事实上,他们非常模糊,但是,不知道,复制?交易?分区?它可以用来避免寻找行的索引,因为它知道b已经1,所以它会跳过那些? ...?

更新:我希望有人能够更多地了解SQL Server如何回答这个问题的“内部”视图,但它看起来像触发器示例(以及我玩过的其他人) by xacinay尽可能接近我们。它似乎相当坚实;如果它在正常情况下表现如此,并且尽管有分区或whatsit它没有表现出来,正如有人说的那样,肯定会有资格作为一个bug。这只是经验而非学术。

4 个答案:

答案 0 :(得分:11)

documentation for @@ROWCOUNT告诉你真相,因为3行将可靠受影响而不是MySQL's ROW_COUNT()

  

不是2(由UPDATE修改的行数 - 三者中的一个   对于b),行已经具有值1。

对于UPDATE,如果新值和以前的值相同,则不重要。它只是做了它告诉的内容:找到数据源,根据提供的条件过滤行,并应用&#39; set&#39;更改已过滤的行。

这就是SQL Server无需任何预留的方式。 MySQL可能会有所不同。行计数过程不是SQL标准的一部分。因此,每当您从一个RDBMS切换到另一个RDBMS时,您必须先看看这些类型的文物。

有些触发器可以看到实际的更新行为:

CREATE TRIGGER [dbo].[trgFooForUpd]
ON [dbo].[Foo]
FOR UPDATE 
AS begin declare @id int;
      select @id = [a] from INSERTED;
      select * from INSERTED; end;
GO
CREATE TRIGGER [dbo].[trgFooAfterUpd]
ON [dbo].[Foo]
AFTER UPDATE 
AS print 'update done for ' + cast(coalesce( @@ROWCOUNT, -1) as varchar )+'rows'

答案 1 :(得分:8)

扩展xacinay的答案,因为他是正确的。

您有3行已更改,因此@@ROWCOUNT准确无误。 SQL Server会更改所有行,在更改之前它不会验证值实际上是不同的,因为这需要在更新命令上花费大量开销。 只是想象必须检查VARCHAR(MAX)是否实际改变了值。

说明这一点的最简单方法是将yor UPDATE查询实际更改为以下内容:

UPDATE [Foo]  SET [b] = 1
OUTPUT INSERTED.b
WHERE [a] = 1

它将输出3行INSERTED,这是“伪”表,它保存给定更新/插入命令的新值。 事实上,在一个实例中,该值实际上已经是b = 1并不重要。

如果您想要这一点,您需要将其包含在WHERE条款中:

UPDATE [Foo]  SET [b] = 1
WHERE [a] = 1 AND [b] <> 1
SELECT @@ROWCOUNT

或者,作为执行此检查的更一般方法,您可以创建一个触发器,并将DELETED表中的值/字段与INSERTED表中的值进行比较,并将其用作行是否实际上“已更改”的基础。

所以 - 3是准确的数字,因为您更新了3行,因为[a] = 1触及了3行

答案 2 :(得分:3)

我认为文档是正确的,因为无论您的示例中的哪一行已经将{1}作为[b]中的值,该行仍然满足WHERE子句中的条件,因此价值已“更新”

我们可以通过扩展您的示例并在this SQLFiddle中包含TIMESTAMP列来查看证明。更新后,TIMESTAMP cluase匹配的所有列上的WHERE已更改,表明该行本身已更改,而不仅仅是评估和丢弃,因为目标值与已存在的目标值匹配。 / p>

答案 3 :(得分:0)

总之,您在询问@@ rowcount是否具有确定性。真的,如果它是非确定性的,你不会认为你会在文档中看到它吗?假设确定性是合理的。 @@ VERSION和@@ MAX_PRECISION也没有记录为确定性的,因为你在质疑它们不确定的边缘情况。我严重怀疑有一个边缘情况,它失败但它确实然后微软将接受它作为一个错误。他们不会回来@@ rowcount是不确定的 - 文档没有明确说明确定性。

许多例子&#34;影响&#34;在MMSQL中,TSQL将一行设置为相同的值(它只关心where) 你有一些例子,MSSQL确实分配了相同的值(时间戳)
你断言但SQL可以关心,所以我怎么知道我没有一个边缘案例 因为那是不合理的行为
任何程序都应产生一致的输出 没有排序的订单不能保证,但行是相同的 - 这是由Microsoft和SQL特别记录为不确定的 如果@@ rowcount是不确定的,那么我相信微软会记录这一点 假设@@ rowcount是确定性的,这是合理的 C#和Java并不总是具有相同的行为
这不是我怀疑C#和Java不可靠的原因

查看MSSQL中的查询计划
[a]上没有谓词 [b]

有一个谓词

如果您将查询更改为

UPDATE [Foo] SET [b] = 1 WHERE [a] <> 1 and [b] = 1;

然后你会在[a]和[b]

上看到谓词

查询优化器将决定如何最有效地处理查询,它不会更改查询 在第一个查询中在[b]上引入谓词正在改变查询 一个合适的数据库就是不这样做。

我非常怀疑在MySQL中,如果查看查询计划,第一个查询中的[a]会有一个谓词。

而不是要求可靠的证据创造一个证明它不可靠的边缘情况 如果你可以创建一个不可靠的情况我提交微软会接受它作为一个错误。

什么是商业案例?
如果您有业务案例,必须使用相同的值更新值,那么根据定义,您需要测试一些内容 我可以做的唯一例子是时间戳或触发器 如果您确实需要对其进行更新,那么您需要测量一些内容 您是否有证据表明某个值未更新为相同值? 如果您仍然不信任它,那么将其包装在交易中。

如果您不需要更新相同的值,那么为什么不添加[b]&lt;&gt; 1。 这更有效率。

SO是针对具体的编程问题 什么是编程问题?
证明MSSQL可靠不是一个编程问题。