设置使用人数


341

这个问题的启发,在SET NOCOUNT上有不同的看法...

我们是否应该将SET NOCOUNT ON用于SQL Server?如果没有,为什么不呢?

它的作用编辑6,2011年7月22日

它抑制了任何DML之后的“受影响的xx行”消息。这是一个结果集,发送时,客户端必须对其进行处理。它很小,但是可以测量(请参见下面的答案)

对于触发器等,客户端将收到多个“受影响的xx行”,这将导致某些ORM,MS Access,JPA等的各种错误(请参见下面的编辑)

背景:

公认的最佳实践(直到这个问题之前,我一直认为)是SET NOCOUNT ON在SQL Server的触发器和存储过程中使用。我们到处都使用它,一个快速的google也显示出很多SQL Server MVP也同意。

MSDN表示,这可能会破坏.net SQLDataAdapter

现在,这对我来说意味着SQLDataAdapter仅限于完全CRUD处理,因为它希望“ n个受影响的行”消息匹配。因此,我不能使用:

  • 如果存在则避免重复(不影响行的消息)注意:请谨慎使用
  • 不存在(行数少于预期)
  • 过滤掉琐碎的更新(例如,实际上没有数据更改)
  • 之前进行任何表访问(例如记录)
  • 隐藏复杂性或去甲化作用
  • 等等

在问题marc_s(谁知道他的SQL知识)说不要使用它。这与我的想法有所不同(我也认为自己在SQL方面有些能力)。

我可能会遗漏一些东西(随意指出显而易见的地方),但是你们在那里的人们怎么想?

注意:已经有好几年了,因为我现在不使用SQLDataAdapter,所以看到了此错误。

在评论和问题之后进行编辑:

编辑:更多的想法...

我们有多个客户端:一个可以使用C#SQLDataAdaptor,另一个可以使用Java的nHibernate。这些可能会以不同的方式受到影响SET NOCOUNT ON

如果您将存储的procs当作方法,那么以某种方式处理某些内部处理以达到您自己的目的是不好的形式(反模式)。

编辑2:触发打破nHibernate问题SET NOCOUNT ON无法设置在哪里

(不,它不是this的重复)

编辑3:还有更多信息,多亏了我的MVP同事

编辑4:2011年5月13日

如果未指定,是否也会破坏Linq 2 SQL?

编辑5:2011年6月14日

使用表变量破坏JPA,存储的proc:JPA 2.0是否支持SQL Server表变量?

编辑6:2011年8月15日

SSMS“编辑行”数据网格要求SET NOCOUNT ON:使用GROUP BY更新触发器

编辑7:2013年3月7日

@RemusRusanu的更多详细信息:
SET NOCOUNT ON是否真的使性能差异很大


@AlexKuznetsov:什么是“线程安全”方法?当然,在EXISTS中执行的读取是否仍会包含任何未完成的交易?
AnthonyWJones

2
@Jeremy Seghi:抱歉回复晚。(#rows受影响的)消息是由SSMS等解释的客户端工具内容:但是有一个包含此信息的数据包。当然,我知道@@ rowcount的工作原理等,但这不是问题的重点……
gbn

1
别担心。我个人同意您的观点。我只是在评论说,IF / WHERE EXISTS构造的结果和SET NOCOUNT之间没有直接关联。无论NOCOUNT如何,我都能从这些构造中获得一致的结果。如果您有其他疑问,请发送给我。
Jeremy S

1
@Jeremy Seghi:您是正确的:SET NOCOUNT ON 抑制多余的数据包返回给客户端。如果@@ ROWCOUNT等均不受影响。哦,它打破SQLDataAdapters ... :-)
GBN

1
@基里·约翰斯通:事后看来,这是一个措辞不佳的问题。如果不是我的问题,我将投票决定关闭
gbn

Answers:


245

好的,现在我已经完成研究,这是交易:

在TDS协议中,每个查询SET NOCOUNT ON仅保存9个字节,而文本“ 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的局限性。
gbn

谢谢。字节或大小对我来说不是问题,但客户端必须对其进行处理。仍然令我震惊的是SQLDataAdapter依赖...
gbn

2
感谢您的回答。由于您的调查,我会接受这一点,这引发了我的更多信息和工作。不过,我在管理费用上存在分歧:其他答案表明这很重要。欢呼声,数十亿
亿

13
它不是字节数,而是其通过电线的往返延迟,这是性能的杀手
er

1
在TDS消息流中,一个示例是仅将值插入表中。DONINPROC(RPC)或DONE(BATCH)消息将流rowcount设置为受影响的行,而done_count标志为true,无论是否NO_COUNTON。当查询保留确实执行select的SELECT语句或RPC调用时,取决于客户端lib的实现,这可能需要禁用计数...当禁用时,仍对select语句计数行,但将标志DONE_COUNT设置为false。务必阅读客户库的建议,因为它将代替您来解释令牌(消息)流
Milan Jaric

86

我花了很多时间才能找到围绕NOCOUNT的真实基准数据,因此我想分享一个简短的摘要。

  • 如果您的存储过程使用游标执行许多非常快速的操作而没有返回结果,则将NOCOUNT OFF设置为ON所需的时间大约是其10倍。1这是最坏的情况。
  • 如果您的存储过程仅执行一次快速操作而没有返回结果,则将NOCOUNT设置为ON 可能会提高3%的性能。2这将与典型的插入或更新过程一致。(请参阅此答案的注释,以获取有关为何此方法可能并不总是更快的一些讨论。)
  • 如果您的存储过程返回结果(即,您选择SELECT),则性能差异将与结果集的大小成比例地减小。

5
+1对光标的影响,这与我的观察一致
zvolkov 2011年

第二点不准确!我的意思是它所指的博客。从来不是!无论NO_COUNT设置为ON还是OFF,都以相同的大小发送DONE和DONEPROC和DONEINPROC。RowCount仍然存在,因为ULONGLONG(64字节)和DONE_COUNT标志仍然存在,但是位值为0。SQLServer仍将对行数进行计数,即使您不希望从DONE令牌中读取值也是如此。如果您阅读@@ ROWCOUNT,则您将更多字节以返回值令牌的形式或作为另一个colmetadata +行令牌的形式添加到令牌流中!
米兰·加里奇

@MilanJaric:感谢您提出来。您帮助我意识到我链接了错误的文章。该链接现在已更新,并且本文提出了引人注目的论点,以表明使用SET NOCOUNT ON可以稍微改善性能。您认为所使用的基准测试方法存在问题吗?
StriplingWarrior

:)关于SET NOCOUNT OFF / ON仍然不准确,错误是第二个SP没有SET NOCOUNT OFF;,这就是为什么他们认为他们没有得到额外的字节作为响应。准确的基准将用于SET NOCOUNT ON左侧和SET NOCOUNT OFF存储过程右存储过程。这样,您将得到TDS包DONEINPROC (SET NOCOUNT ...)再次,十DONEINPROC (INSERT statement),然后RETURNVALUE(@@ROWCOUNT),再RETURNSTATUS 0对SP和finaly DONPROC。因为第二个sp的主体中没有SET NOCOUNT OFF,所以存在错误!
米兰·加里奇

改写他们发现的内容,但他们没有意识到,如果您有1K个提取游标请求,请首先发出一个将NOCOUNT设置为ON或OFF进行连接的请求,然后使用相同的连接调用游标提取1K次以节省一些带宽。NOCOUNT ON或OFF的实际连接状态不会影响带宽,它可能会使客户端库(例如ADO.net或ODBC)感到困惑。因此,“如果您关心带宽,请不要使用SET NOCOUNT <WHATEVER>” :)
Milan Jaric

77
  • 当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的示例:

在此处输入图片说明


7
通过屏幕快照轻松快速地理解。做得好。:)
shaijut

35

我猜在某种程度上,这是DBA与开发人员的问题。

大多数情况下,我想说除非您绝对肯定要使用它,否则不要使用它-因为使用它会破坏您的ADO.NET代码(如Microsoft所述)。

我想作为一名DBA,您会更多地处于另一面-除非您确实必须阻止使用它,否则请尽可能使用它。

另外,如果您的开发人员曾经使用过ADO.NET的ExecuteNonQuery方法调用返回的“ RecordsAffected” ,那么如果每个人都使用它,就会麻烦,SET NOCOUNT ON因为在这种情况下,ExecuteNonQuery将始终返回0。

另请参阅Peter Bromberg的博客文章,并查看他的位置。

因此,实际上归结为谁来制定标准:-)


不过,他只是在谈论简单的CRUD:他提到的数据网格可以使用xml发送多行以避免往返等
gbn

我想如果您从不使用SqlDataAdapters,并且从不检查并依赖ExecuteNonQuery返回的“受影响的记录”数字(例如,如果您使用类似Linq-to-SQL或NHibernate的东西),那么您可能没有任何问题在所有存储的过程中使用SET NOCOUNT ON。
marc_s

12

如果您说自己也可能有其他客户端,那么如果未将SET NOCOUNT设置为ON,则经典ADO会出现问题。

我经常遇到的一个问题:如果存储过程执行了许多语句(因此返回了许多“受影响的xxx行”消息),ADO似乎无法处理该问题并抛出错误 “无法更改Recordset对象的ActiveConnection属性”其中以Command对象为源。”

因此,除非有确凿的充分理由,否则我通常主张将其设置为ON 。您可能已经找到了我需要去阅读的真正非常好的理由。


9

冒着使事情变得更复杂的风险,我鼓励采用与上述所有规则稍有不同的规则:

  • NOCOUNT ON在proc中进行任何工作之前,请始终将其设置在proc的顶部,但在从存储的proc返回任何记录集之前,也请 始终SET NOCOUNT OFF将其设置在proc的顶部。

因此,“通常保持不计数,除非您实际返回结果集时”。我不知道这会破坏任何客户端代码的任何方式,这意味着客户端代码永远不需要了解proc内部的任何知识,并且它也不是特别繁琐。


谢谢。您当然可以从DataSet或使用容器中获取行数,但可能很有用。我们无法在SELECT上使用触发器,因此这是安全的:大多数客户端错误是由数据更改中的虚假消息引起的。
gbn

该规则的问题在于,要比“在进程顶部设置NOCOUNT ON是否更难”进行测试;我不知道SQL分析工具,如SQL光线是否可以测试之类的事情......它添加到我的长期待办事项列表我的SQL格式化项目:)

5

关于破坏NHibernate的触发器,我有第一手的经验。基本上,当NH执行UPDATE时,它期望受影响的行数是一定的。通过将SET NOCOUNT ON添加到触发器,您可以将行数恢复到NH期望值,从而解决了该问题。是的,如果您使用NH,我绝对建议您将其关闭以用于触发器。

关于SP中的用法,这是个人喜好问题。我总是关闭行计数,但是再一次,这两种方式都没有真正的强力论据。

另一方面,您应该真正考虑脱离基于SP的体系结构,那么您根本就不会有这个问题。


1
我不同意离开存储过程。这意味着我们必须在2个不同的客户端代码库中使用相同的SQL,并信任我们的客户端编码器。我们是开发人员DBA。而且,您的意思不是“ SET NOCOUNT ON ”吗?
gbn

@CodeBlend:仅使用Google即可获得比您需要的更多的东西。但是... stackoverflow.com/a/4040466/27535
gbn 2013年

3

我想证明自己“ 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个额外的字节可以保存,但比减少列名(例如,将'ManagingDepartment'更改为'MD')更值得

所以我认为没有理由使用它来表现

但是,正如其他人提到的那样,它可能会破坏ADO.NET,我也偶然发现了使用python的问题: MSSQL2008-Pyodbc-以前的SQL不是查询

所以大概还是个好习惯...


1
SET NOCOUNT ON;

这行代码在SQL中用于不返回查询执行中受影响的行数。如果我们不需要受影响的行数,则可以使用它,因为这将有助于节省内存使用量并增加查询执行的速度。


2
请注意,@@ ROWCOUNT仍被设置。SET NOCOUNT ON禁止SQL Server发送到客户端的任何额外响应。请参阅上方接受的答案
gbn

1

设置NOCOUNT ON;上面的代码将在DML / DDL命令执行后将sql服务器引擎生成的消息停止到前端结果窗口。

我们为什么这样做?由于SQL Server引擎需要一些资源来获取状态并生成消息,因此它被认为是Sql Server引擎的重载。因此,我们将noncount消息设置为on。


1

一个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 NoCountOnEXEC NoCountOff显示NoCountOff上的额外390KB流量:

客户统计

在循环或游标中执行查询可能不理想,但我们也不生活在理想世界中:)


0

我知道这是一个很老的问题。但仅用于更新。

使用“ SET NOCOUNT ON”的最佳方法是将其设置为SP中的第一条语句,然后在最后一条SELECT语句之前再次将其设置为OFF。


-1

我不知道如何在客户端和SQL之间测试SET NOCOUNT ON,所以我测试了其他SET命令“ SET TRANSACTION ISOLATION LEVEL READ IMMITMITTED”的类似行为

我从连接中发送了一条命令,更改了SQL的默认行为(“读取已提交”),并且在下一个命令中更改了该命令。当我在存储过程中更改ISOLATION级别时,它没有更改下一个命令的连接行为。

当前结论

  1. 在存储过程中更改设置不会更改连接默认设置。
  2. 通过使用ADOCOnnection发送命令来更改设置会更改默认行为。

我认为这与其他SET命令有关,例如“ SET NOCOUNT ON”


您上面的第1点是否表示您真的不需要在末尾设置NOCOUNT OFF,因为它不会影响全局环境?
funkymushroom 2014年

我不确定这是否是他对第1点的意思,但是在我的测试中,是的,显然全局环境不受存储过程中SET NOCOUNT ON的影响。
道格

这是一个比较差的选择,因为“隔离级别”与特定交易明确相关,因此没有特殊理由期望它与诸如NOCOUNT
IMSoP

-1

如果(不设置计数==关闭)

{然后它将保留受影响的记录数的数据,从而降低性能}否则{它不会跟踪更改的记录,从而提高性能}}


-1

有时候,即使是最简单的事情也会有所作为。这些简单项目之一应该是每个存储过程的一部分SET NOCOUNT ON。放在存储过程顶部的这一行代码关闭了每个T-SQL语句执行后SQL Server发送回客户端的消息。这是对所有人执行的SELECTINSERTUPDATE,和DELETE语句。在查询窗口中运行T-SQL语句时,拥有此信息很方便,但是在运行存储过程时,无需将该信息传递回客户端。

通过消除网络上的这些额外开销,可以大大提高数据库和应用程序的整体性能。

如果仍然需要获取受正在执行的T-SQL语句影响的行数,则仍可以使用该@@ROWCOUNT选项。通过发出SET NOCOUNT ONthis函数(@@ROWCOUNT)仍然有效,并且仍可以在您的存储过程中使用,以标识该语句影响了多少行。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.