SET NOCOUNT ON 使用情况
Posted
技术标签:
【中文标题】SET NOCOUNT ON 使用情况【英文标题】:SET NOCOUNT ON usage 【发布时间】:2010-12-01 19:24:01 【问题描述】:受this question 启发,对 SET NOCOUNT 有不同的看法...
我们应该对 SQL Server 使用 SET NOCOUNT ON 吗?如果没有,为什么不呢?
它的作用编辑 6,2011 年 7 月 22 日
它会在任何 DML 之后抑制“xx 行受影响”消息。这是一个结果集,在发送时,客户端必须对其进行处理。它很小,但可以测量(请参阅下面的答案)
对于触发器等,客户端将收到多个“受影响的 xx 行”,这会导致某些 ORM、MS Access、JPA 等出现各种错误(请参阅下面的编辑)
背景:
普遍接受的最佳实践(直到这个问题我才想到)是在 SQL Server 的触发器和存储过程中使用SET NOCOUNT ON
。我们在任何地方都使用它,快速的谷歌显示很多 SQL Server MVP 也同意。
MSDN 说这会破坏.net SQLDataAdapter。
现在,这对我来说意味着 SQLDataAdapter 仅限于完全简单的 CRUD 处理,因为它希望匹配“n 行受影响”消息。所以,我不能使用:
IF EXISTS 以避免重复(不影响行消息)注意:谨慎使用 WHERE NOT EXISTS(少于预期的行数 过滤掉琐碎的更新(例如,实际上没有数据更改) 之前进行任何表访问(例如日志记录) 隐藏复杂性或去规范化 等在问题中 marc_s(谁知道他的 SQL 知识)说不要使用它。这与我的想法不同(我认为自己在 SQL 方面也有一定的能力)。
我可能遗漏了一些东西(请随意指出明显的),但是你们那里的人怎么看?
注意:我已经好几年没有看到这个错误了,因为我现在不使用 SQLDataAdapter。
在 cmets 和问题之后编辑:
编辑:更多想法...
我们有多个客户端:一个可能使用 C# SQLDataAdaptor,另一个可能使用来自 Java 的 nHibernate。 SET NOCOUNT ON
可能会以不同的方式影响这些内容。
如果您将存储过程视为方法,那么假设某些内部处理以某种方式为您自己的目的工作是不好的形式(反模式)。
编辑2:trigger breaking nHibernate question,其中SET NOCOUNT ON
无法设置
(不,它不是 this 的副本)
编辑 3:更多信息,感谢我的 MVP 同事
KB 240882,导致 SQL 2000 及更早版本断开连接的问题 Demo of performance gain编辑 4:2011 年 5 月 13 日
Breaks Linq 2 SQL too when not specified?
编辑 5:2011 年 6 月 14 日
破坏 JPA,使用表变量存储过程:Does JPA 2.0 support SQL Server table variables?
编辑 6:2011 年 8 月 15 日
SSMS“编辑行”数据网格需要 SET NOCOUNT ON:Update trigger with GROUP BY
编辑 7:2013 年 3 月 7 日
来自@RemusRusanu 的更多详细信息:Does SET NOCOUNT ON really make that much of a performance difference
【问题讨论】:
@AlexKuznetsov:什么是“线程安全”方法?在 EXISTS 中执行的读取肯定会包含在任何未完成的事务中吗? @Jeremy Seghi:抱歉回复晚了。 (#rows 受影响)消息是由 SSMS 等解释的客户端工具:但是有一个带有此信息的数据包发送。当然,我知道@@rowcount 是如何工作的等等,但这不是问题的重点...... 不用担心。我个人同意你的观点;我只是评论说 IF/WHERE EXISTS 构造的结果与 SET NOCOUNT 的结果之间没有直接关联。无论 NOCOUNT 如何,我都能从这些构造中得到一致的结果。如果您有其他意见,请按我的方式发送。 @Jeremy Seghi:你是对的:SET NOCOUNT ON only 抑制返回给客户端的额外数据包。 IF、@@ROWCOUNT 等均不受影响。哦,它破坏了 SQLDataAdapters... :-) @Kieren Johnstone:事后看来,这是一个措辞不当的问题。如果这不是我的问题,我会投票关闭...... 【参考方案1】:我想在某种程度上这是 DBA 与开发人员的问题。
作为开发人员,我想说除非你绝对必须这样做,否则不要使用它——因为使用它会破坏你的 ADO.NET 代码(如 Microsoft 所记录的那样)。
我猜作为一名 DBA,你会更多地站在另一边 - 尽可能使用它,除非你真的必须阻止它的使用。
此外,如果您的开发人员曾经使用 ADO.NET 的 ExecuteNonQuery
方法调用返回的“RecordsAffected”,那么如果每个人都使用 SET NOCOUNT ON
,您就会遇到麻烦,因为在这种情况下,ExecuteNonQuery 将始终返回 0。
另请参阅 Peter Bromberg 的 blog post 并查看他的位置。
所以这真的归结为谁来设定标准:-)
马克
【讨论】:
不过,他正在讨论简单的 CRUD:他提到的数据网格可以使用 xml 发送多行以避免往返等 我猜如果您从不使用 SqlDataAdapters,并且您从不检查并依赖 ExecuteNonQuery 返回的“受影响的记录”数(例如,如果您使用 Linq-to-SQL 或 NHibernate 之类的东西),那么在所有存储过程中使用 SET NOCOUNT ON 可能没有任何问题。【参考方案2】:好的,现在我已经完成了我的研究,这是交易:
在 TDS 协议中,SET NOCOUNT ON
只保存9-bytes per query,而文本“SET NOCOUNT ON”本身就高达 14 个字节。我曾经认为123 row(s) affected
是从服务器以纯文本形式在单独的网络数据包中返回的,但事实并非如此。它实际上是嵌入响应中的一个名为DONE_IN_PROC
的小结构。它不是一个单独的网络数据包,因此不会浪费往返。
我认为您几乎总是可以坚持默认计数行为而不必担心性能。但在某些情况下,预先计算行数会影响性能,例如只进游标。在这种情况下,NOCOUNT 可能是必要的。除此之外,绝对没有必要遵循“尽可能使用 NOCOUNT”的座右铭。
这里有一个关于SET NOCOUNT
设置无意义的非常详细的分析:http://daleburnett.com/2014/01/everything-ever-wanted-know-set-nocount/
【讨论】:
确实如此。我一直在使用 SET NOCOUNT ON,但是 marc_s 在另一个问题中指出了 SQLDataAdapter 的局限性。 谢谢。字节或大小对我来说不是问题,但客户端必须处理它。仍然让我震惊的是 SQLDataAdapter 依赖项...... 感谢您的回答。由于您的调查,我会接受这一点,这引发了我更多的信息和工作。不过,我不同意开销:正如其他答案所示,这可能很重要。干杯,gbn 性能杀手不是它在线路上的往返延迟的字节数 在 TDS 消息流中,例如当一个人只向表中插入值时。DONINPROC
(RPC) 或 DONE
(BATCH) 消息流式传输,rowcount
设置为受影响的行,而 done_count
标志为 true
,无论 NO_COUNT
是否为 ON
。在查询包含 SELECT 语句或执行 select 的 RPC 调用的情况下,取决于客户端 lib 实现,它可能需要禁用计数...当禁用时,仍会为 select 语句计算行,但标志 DONE_COUNT
设置为 false
.始终阅读您的客户端库建议的内容,因为它将解释令牌(消息)流而不是您【参考方案3】:
关于破坏 NHibernate 的触发器,我亲身体验过。基本上,当 NH 执行 UPDATE 时,它期望一定数量的行受到影响。通过将 SET NOCOUNT ON 添加到触发器,您可以将行数恢复到 NH 的预期值,从而解决问题。所以,是的,如果您使用 NH,我肯定会建议将其关闭以触发触发器。
关于 SP 中的用法,这是个人喜好问题。我总是关闭行计数,但话又说回来,无论哪种方式都没有真正的有力论据。
另一方面,您真的应该考虑放弃基于 SP 的架构,那么您甚至不会有这个问题。
【讨论】:
我不同意放弃存储过程。这意味着我们必须在 2 个不同的客户端代码库中拥有相同的 SQL,并信任我们的客户端编码器。我们是开发人员 DBA。你不是说“SET NOCOUNT ON”吗? @CodeBlend:只需在 Google 上搜索即可获得超出您所需的更多信息。但是...***.com/a/4040466/27535【参考方案4】:如果您说您也可能有不同的客户端,那么如果 SET NOCOUNT 未设置为 ON,则经典 ADO 会出现问题。
我经常遇到的一个问题:如果一个存储过程执行了许多语句(因此返回了许多“受影响的 xxx 行”消息),ADO 似乎没有处理这个问题并抛出错误 “无法更改以 Command 对象为源的 Recordset 对象的 ActiveConnection 属性。"
所以我通常主张将其设置为 ON,除非有一个真的很好的理由不这样做。您可能已经找到了我需要阅读更多内容的真正好理由。
【讨论】:
【参考方案5】:我花了很多时间才找到关于 NOCOUNT 的真正基准数据,所以我想我会分享一个简短的总结。
如果您的存储过程使用游标执行许多非常快速的操作而没有返回结果,则关闭 NOCOUNT 所花费的时间大约是打开时的 10 倍。 1 这是最坏的情况。 如果您的存储过程只执行一个快速操作而没有返回结果,则将 NOCOUNT 设置为可能会产生大约 3% 的性能提升。 2 这与典型的插入或更新过程一致。 (有关为什么这可能并不总是更快的一些讨论,请参阅此答案上的 cmets。) 如果您的存储过程返回结果(即您选择某些内容),则性能差异将随着结果集的大小成比例减小。【讨论】:
+1 表示对光标的影响,这与我的观察一致 第2点不准确!我的意思是它所指的博客。从来不是!无论 NO_COUNT 设置为 ON 还是 OFF,都以相同的大小发送 DONE 和 DONEPROC 和 DONEINPROC。 RowCount 仍然存在,因为 ULONGLONG(64 字节)和标志 DONE_COUNT 仍然存在,但位值为 0。SQL 服务器无论如何都会计算行数,即使您不感兴趣从 DONE 令牌中读取值。如果您阅读@@ROWCOUNT,那么您以返回值令牌或另一个 colmetadata + 行令牌的形式向令牌流添加了更多字节! @MilanJaric:感谢您指出这一点。你帮助我意识到我链接了错误的文章。该链接现已更新,该文章提出了一个令人信服的论点,表明使用 SET NOCOUNT ON 可以稍微提高性能。您认为使用的基准方法有问题吗? :) 关于 SET NOCOUNT OFF/ON 仍然不准确,错误是第二个 SP 没有SET NOCOUNT OFF;
,这就是为什么他们认为他们没有得到额外的字节作为响应。准确的基准是在左侧使用SET NOCOUNT ON
,在右侧存储过程中使用SET NOCOUNT OFF
。这样,您将获得带有DONEINPROC (SET NOCOUNT ...)
的 TDS 包,再有十个 DONEINPROC (INSERT statement)
,然后是 RETURNVALUE(@@ROWCOUNT)
,然后是 RETURNSTATUS 0
for sp,最后是 DONPROC
。出现错误是因为第二个 sp 在正文中没有 SET NOCOUNT OFF!
重新表述他们发现但他们没有意识到的是,如果您有 1K 获取游标请求,首先发出一个请求将 NOCOUNT 设置为 ON 或 OFF 以进行连接,然后使用相同的连接来调用游标获取 1K 次以节省一些带宽。 NOCOUNT ON 或 OFF 的实际连接状态不会影响带宽,它可能只会混淆客户端库,例如ADO.net 或 ODBC。所以“如果您关心带宽,请不要使用 SET NOCOUNT 冒着让事情变得更复杂的风险,我鼓励使用与我在上面看到的所有规则略有不同的规则:
始终在 proc 的顶部设置NOCOUNT ON
,在您在 proc 中执行任何工作之前,但也在从存储的 proc 返回任何记录集之前再次再次设置SET NOCOUNT OFF
。
所以“通常不计较,除非您实际返回结果集”。我不知道这会破坏任何客户端代码的任何方法,这意味着客户端代码永远不需要了解 proc 内部的任何内容,而且它并不是特别繁重。
【讨论】:
谢谢。当然,您可以从 DataSet 或消费容器中获取行数,但可能很有用。我们不能在 SELECT 上设置触发器,因此这是安全的:大多数客户端错误是由数据更改的虚假消息引起的。 这条规则的问题在于它比“在 proc 的顶部是否 SET NOCOUNT ON?”更难测试;我想知道像 Sql Enlight 这样的 SQL 分析工具是否可以测试这类事情......将其添加到我的 SQL 格式化程序项目的长期待办事项列表中:)【参考方案7】:我不知道如何在客户端和 SQL 之间测试 SET NOCOUNT ON,所以我测试了其他 SET 命令“SET TRANSACTION ISOLATION LEVEL READ UNCIMMITTED”的类似行为
我从我的连接中发送了一个命令,更改了 SQL 的默认行为(已提交读),并且为下一个命令进行了更改。 当我更改存储过程中的 ISOLATION 级别时,它并没有更改下一个命令的连接行为。
目前的结论,
-
更改存储过程中的设置不会更改连接默认设置。
通过使用 ADOCOnnection 发送命令来更改设置会更改默认行为。
我认为这与其他 SET 命令有关,例如“SET NOCOUNT ON”
【讨论】:
你上面的第1点是否暗示你真的不需要在最后设置NOCOUNT OFF,因为它不会影响全球环境? 我不确定这是否是他对第 1 点的意思,但在我的测试中是的,显然全局环境不受存储过程中的 SET NOCOUNT ON 的影响。 这是一个糟糕的比较选择,因为隔离级别与特定事务显式相关,因此没有特别的理由期望它与NOCOUNT
之类的设置一致【参考方案8】:
SET NOCOUNT ON;
这行代码在 SQL 中用于不返回查询执行中受影响的行数。如果我们不需要受影响的行数,我们可以使用它,因为这将有助于节省内存使用并提高查询的执行速度。
【讨论】:
请注意@@ROWCOUNT 仍然设置。 SET NOCOUNT ON 禁止 SQL Server 发送给客户端的任何额外响应。请参阅上面接受的答案【参考方案9】:当 SET NOCOUNT 为 ON 时,不返回计数(指示受 Transact-SQL 语句影响的行数)。当 SET NOCOUNT 为 OFF 时,返回计数。它与任何 SELECT、INSERT、UPDATE、DELETE 语句一起使用。
SET NOCOUNT 的设置是在执行或运行时设置的,而不是在解析时设置的。
SET NOCOUNT ON 提高了存储过程 (SP) 的性能。
语法:SET NOCOUNT ON |关闭
SET NOCOUNT ON 示例:
SET NOCOUNT OFF 示例:
【讨论】:
通过屏幕截图轻松快速地理解。做得好。 :)【参考方案10】:如果(设置无计数==关闭)
然后它将保留受影响记录的数据 所以降低性能 别的 它不会跟踪更改记录 因此提高性能
【讨论】:
【参考方案11】:我知道这是个很老的问题。但只是为了更新。
使用“SET NOCOUNT ON”的最佳方式是将其作为您的 SP 中的第一条语句,并在最后一条 SELECT 语句之前再次将其设置为 OFF。
【讨论】:
【参考方案12】:设置无计数; 上面的代码会在 DML/DDL 命令执行后停止 sql server 引擎生成的消息到前端的结果窗口。
我们为什么要这样做? 由于 SQL server 引擎需要一些资源来获取状态并生成消息,这被认为是 Sql server 引擎的过载。所以我们将 noncount 消息设置为 on。
【讨论】:
【参考方案13】:有时,即使是最简单的事情也能有所作为。 SET NOCOUNT ON
是每个存储过程的一部分,这些简单的项目之一。放在存储过程顶部的这一行代码会在执行每个 T-SQL 语句后关闭 SQL Server 发送回客户端的消息。这对所有SELECT
、INSERT
、UPDATE
和DELETE
语句都执行。当您在查询窗口中运行 T-SQL 语句时,拥有这些信息很方便,但是在运行存储过程时,不需要将此信息传递回客户端。
通过从网络中消除这种额外的开销,它可以大大提高您的数据库和应用程序的整体性能。
如果您仍然需要获取受正在执行的 T-SQL 语句影响的行数,您仍然可以使用@@ROWCOUNT
选项。通过发出 SET NOCOUNT ON
,此函数 (@@ROWCOUNT
) 仍然有效,并且仍然可以在您的存储过程中使用,以确定受语句影响的行数。
【讨论】:
【参考方案14】:我想验证自己“SET NOCOUNT ON”不会保存网络数据包或往返
我在另一台主机上使用了测试 SQLServer 2017(我使用了 VM)
create table ttable1 (n int);
insert into ttable1 values (1),(2),(3),(4),(5),(6),(7)
go
create procedure procNoCount
as
begin
set nocount on
update ttable1 set n=10-n
end
create procedure procNormal
as
begin
update ttable1 set n=10-n
end
然后我使用“Wireshark”工具在端口 1433 上跟踪数据包:
“捕获过滤器”按钮->“端口 1433”
exec procNoCount
这是响应包:
0000 00 50 56 c0 00 08 00 0c 29 31 3f 75 08 00 45 00
0010 00 42 d0 ce 40 00 40 06 84 0d c0 a8 32 88 c0 a8
0020 32 01 05 99 fe a5 91 49 e5 9c be fb 85 01 50 18
0030 02 b4 e6 0e 00 00 04 01 00 1a 00 35 01 00 79 00
0040 00 00 00 fe 00 00 e0 00 00 00 00 00 00 00 00 00
exec procNormal
这是响应包:
0000 00 50 56 c0 00 08 00 0c 29 31 3f 75 08 00 45 00
0010 00 4f d0 ea 40 00 40 06 83 e4 c0 a8 32 88 c0 a8
0020 32 01 05 99 fe a5 91 49 e8 b1 be fb 8a 35 50 18
0030 03 02 e6 1b 00 00 04 01 00 27 00 35 01 00 ff 11
0040 00 c5 00 07 00 00 00 00 00 00 00 79 00 00 00 00
0050 fe 00 00 e0 00 00 00 00 00 00 00 00 00
在第 40 行,我可以看到“07”,即“受影响的行数”。 它包含在响应数据包中。没有额外的数据包。
然而,它有 13 个额外的字节可以保存,但可能并不比减少列名更值得(例如“管理部门”到“MD”)
所以我认为没有理由将其用于性能
但是 正如其他人提到的那样,它可以破坏 ADO.NET 我还偶然发现了一个使用 python 的问题: MSSQL2008 - Pyodbc - Previous SQL was not a query
所以可能仍然是一个好习惯......
【讨论】:
【参考方案15】:SET NOCOUNT ON
真正可以帮助的一个地方是您在循环或游标中执行查询的地方。这可能会增加大量网络流量。
CREATE PROCEDURE NoCountOn
AS
set nocount on
DECLARE @num INT = 10000
while @num > 0
begin
update MyTable SET SomeColumn=SomeColumn
set @num = @num - 1
end
GO
CREATE PROCEDURE NoCountOff
AS
set nocount off
DECLARE @num INT = 10000
while @num > 0
begin
update MyTable SET SomeColumn=SomeColumn
set @num = @num - 1
end
GO
在 SSMS 中打开客户端统计信息,EXEC NoCountOn
和 EXEC NoCountOff
的运行显示 NoCountOff 的流量增加了 390KB:
在循环或游标中进行查询可能并不理想,但我们也不生活在理想世界中:)
【讨论】:
【参考方案16】:SET NOCOUNT ON 甚至允许像这样访问受影响的行:
SET NOCOUNT ON
DECLARE @test TABLE (ID int)
INSERT INTO @test
VALUES (1),(2),(3)
DECLARE @affectedRows int = -99
DELETE top (1)
FROM @test
SET @affectedRows = @@rowcount
SELECT @affectedRows as affectedRows
结果
affectedRows
1
消息
命令成功完成。
完成时间:2020-06-18T16:20:16.9686874+02:00
【讨论】:
【参考方案17】: SET NOCOUNT ON- 它将显示“命令已成功完成”。 SET NOCOUNT OFF - 它将显示“(受影响的行数)”。【讨论】:
以上是关于SET NOCOUNT ON 使用情况的主要内容,如果未能解决你的问题,请参考以下文章