DEFINIR NOCOUNT ON use

341

Inspirado por esta pergunta, em que existem diferentes pontos de vista sobre SET NOCOUNT ...

Devemos usar SET NOCOUNT ON para SQL Server? Se não, por que não?

O que faz Edit 6, on 22 Jul 2011

Suprime a mensagem "xx linhas afetadas" após qualquer DML. Este é um conjunto de resultados e, quando enviado, o cliente deve processá-lo. É pequeno, mas mensurável (veja as respostas abaixo)

Para gatilhos, etc, o cliente receberá várias "linhas xx afetadas" e isso causa todos os tipos de erros para alguns ORMs, MS Access, JPA etc. (veja as edições abaixo)

Fundo:

A melhor prática geral aceita (pensei até essa pergunta) é usar SET NOCOUNT ONgatilhos e procedimentos armazenados no SQL Server. Nós o usamos em qualquer lugar e um rápido google mostra muitos MVPs do SQL Server também.

MSDN diz que isso pode quebrar um .net SQLDataAdapter .

Agora, isso significa para mim que o SQLDataAdapter está limitado ao processamento de CRUD simplesmente porque espera que a mensagem "n linhas afetadas" corresponda. Então, eu não posso usar:

  • SE EXISTE para evitar duplicatas (nenhuma linha afetou a mensagem) Nota: use com cuidado
  • ONDE NÃO EXISTE (menos linhas do que o esperado
  • Filtrar atualizações triviais (por exemplo, nenhum dado realmente muda)
  • Faça qualquer acesso à tabela antes (como o log)
  • Ocultar complexidade ou denormlisation
  • etc

Na pergunta marc_s (quem conhece o seu material SQL) diz que não o usa. Isso difere do que penso (e também me considero um pouco competente em SQL).

É possível que eu esteja perdendo alguma coisa (sinta-se à vontade para apontar o óbvio), mas o que vocês acham?

Nota: já faz anos que não vejo esse erro porque não uso o SQLDataAdapter atualmente.

Edita após comentários e perguntas:

Edit: Mais pensamentos ...

Temos vários clientes: um pode usar um C # SQLDataAdaptor, outro pode usar o nHibernate do Java. Estes podem ser afetados de diferentes maneiras com SET NOCOUNT ON.

Se você considerar procs armazenados como métodos, é uma má forma (antipadrão) supor que algum processamento interno funcione de certa maneira para seus próprios propósitos.

Edit 2: uma questão nHibernate de trigger trigger , onde SET NOCOUNT ONnão pode ser definida

(e não, não é uma duplicata disso )

Edit 3: Mais informações, graças ao meu colega MVP

Edit 4: 13 de maio de 2011

Quebra Linq 2 SQL também quando não especificado?

Edit 5: 14 Jun 2011

Quebra JPA, processo armazenado com variáveis ​​de tabela: O JPA 2.0 suporta variáveis ​​de tabela do SQL Server?

Edit 6: 15 de agosto de 2011

A grade de dados "Editar linhas" do SSMS requer SET NOCOUNT ON: atualize o gatilho com GROUP BY

Edit 7: 07 Mar 2013

Detalhes mais detalhados do @RemusRusanu:
O SET NOCOUNT ON realmente faz muita diferença de desempenho

gbn
fonte
@AlexKuznetsov: O que seria uma abordagem "Threadsafe"? Certamente as leituras realizadas nos EXISTS ainda seriam incluídas em qualquer transação pendente?
AnthonyWJones
2
@ Jeremy Seghi: desculpe pela resposta tardia. A mensagem (#rows afetadas) é uma ferramenta cliente interpretada pelo SSMS etc: no entanto, há um pacote enviado com essas informações. Claro, estou ciente de como @@ rowcount funciona etc, mas este não é o ponto da questão ...
gbn
11
Não se preocupe. Pessoalmente, concordo com o seu ponto de vista; Eu estava apenas comentando que não há uma correlação direta entre os resultados de uma construção IF / WHERE EXISTS e SET NOCOUNT. Eu obtenho resultados consistentes com essas construções, independentemente de NOCOUNT. Se você tem algo a dizer, envie-o para mim.
Jeremy S
11
@ Jeremy Seghi: você está correto: SET NOCOUNT ON apenas suprime o pacote extra de dados de volta ao cliente. SE, @@ ROWCOUNT etc não são afetados. Ah, e ele quebra SQLDataAdapters ... :-)
gbn
11
@ Kieren Johnstone: em retrospectiva, é uma pergunta mal formulada. Eu votaria para perto se isso não foi a minha pergunta ...
GBN

Respostas:

245

Ok, agora eu fiz minha pesquisa, eis o negócio:

No protocolo TDS, SET NOCOUNT ONsalva apenas 9 bytes por consulta, enquanto o próprio texto "SET NOCOUNT ON" possui 14 bytes. Eu costumava pensar que 123 row(s) affectedera retornado do servidor em texto sem formatação em um pacote de rede separado, mas esse não é o caso. Na verdade, é uma pequena estrutura chamada DONE_IN_PROCincorporada na resposta. Não é um pacote de rede separado, portanto não são desperdiçadas ida e volta.

Eu acho que você pode manter o comportamento padrão de contagem quase sempre sem se preocupar com o desempenho. Existem alguns casos em que o cálculo antecipado do número de linhas afetaria o desempenho, como um cursor somente para frente. Nesse caso, NOCOUNT pode ser uma necessidade. Fora isso, não há absolutamente nenhuma necessidade de seguir o lema "use NOCOUNT, sempre que possível".

Aqui está uma análise muito detalhada sobre a insignificância da SET NOCOUNTconfiguração: http://daleburnett.com/2014/01/everything-ever-wanted-know-set-nocount/

Sedat Kapanoglu
fonte
De fato. Eu tenho usado SET NOCOUNT ON desde sempre, mas marc_s apontou a limitação do SQLDataAdapter na outra pergunta.
gbn 10/10/09
Obrigado. Os bytes ou o tamanho não são o problema para mim, mas o cliente precisa processá-lo. É a dependência SQLDataAdapter que ainda me surpreende embora ...
GBN
2
Obrigado pela sua resposta. Aceito isso por causa de suas investigações, o que provocou mais informações e trabalho de mim. Mas eu discordo das despesas gerais: isso pode importar como outras respostas mostram. Cheers, gbn
gbn 16/10/09
13
Não é o número de bytes seu atraso de ida e volta ao longo do fio que é o assassino de desempenho
racingsnail
11
No fluxo de mensagens TDS, e exemplo é quando se está inserindo apenas valores na tabela. DONINPROC(RPC) ou DONE(BATCH) são transmitidas com rowcountas linhas afetadas, enquanto o done_countsinalizador é true, independentemente de NO_COUNTser ON. Depende da implementação da lib do cliente nos casos em que a consulta contém instruções SELECT ou chamadas RPC que selecionam, pode ser necessário desativar a contagem ... QUANDO desativada, as linhas ainda são contadas para a instrução select, mas o sinalizador DONE_COUNTestá definido como false. Sempre leia o que o seu lib cliente sugere uma vez que irá interpretar símbolo (mensagem) transmitir ao invés de você
Milan Jaric
86

Demorei bastante para encontrar números de referência reais em torno de NOCOUNT, então achei que compartilharia um resumo rápido.

  • Se o seu procedimento armazenado usar um cursor para executar muitas operações muito rápidas sem resultados retornados, ter NOCOUNT OFF pode demorar cerca de 10 vezes mais do que ON. 1 Esse é o pior cenário.
  • Se o procedimento armazenado realizar apenas uma operação rápida sem resultados, a configuração de NOCOUNT ON poderá gerar um aumento de desempenho de 3%. 2 Isso seria consistente com um procedimento típico de inserção ou atualização. (Veja os comentários sobre esta resposta para uma discussão sobre por que isso nem sempre é mais rápido.)
  • Se o seu procedimento armazenado retornar resultados (por exemplo, você SELECT algo), a diferença de desempenho diminuirá proporcionalmente com o tamanho do conjunto de resultados.
StriplingWarrior
fonte
5
+1 para o impacto sobre o cursor, isso é consistente com minhas observações
zvolkov
O ponto 2 não é exato! Quero dizer o blog que está se referindo. Nunca foi! DONE e DONEPROC e DONEINPROC são enviados com o mesmo tamanho, independentemente se NO_COUNT estiver definido como ON ou OFF. RowCount ainda está lá como ULONGLONG (64 bytes) e o sinalizador DONE_COUNT ainda está lá, mas o valor do bit é 0. O SQL Server contará o número de linhas de qualquer maneira, mesmo que você não esteja interessado em ler o valor do token DONE. Se você ler @@ ROWCOUNT, adicionou mais bytes ao fluxo do token na forma de token returnvalue ou como outro colmetadata + tokens de linha!
Milan Jaric 01/08/19
@MilanJaric: Obrigado por chamar isso. Você me ajudou a perceber que eu havia vinculado o artigo errado. O link está atualizado agora e o artigo apresenta um argumento convincente para mostrar que pode haver uma ligeira melhora no desempenho com SET NOCOUNT ON. Você acha que há problemas com os métodos de benchmark usados?
StriplingWarrior
:) ainda imprecisos sobre SET NOCOUNT OFF / ON, o erro é que o segundo SP não possui SET NOCOUNT OFF;e é por isso que eles acham que não estão recebendo bytes extras em resposta. O benchmark preciso seria usar SET NOCOUNT ONno SET NOCOUNT OFFprocedimento armazenado à esquerda e à direita. Dessa forma, você obterá o pacote TDS com DONEINPROC (SET NOCOUNT ...), dez novamente DONEINPROC (INSERT statement)e RETURNVALUE(@@ROWCOUNT), em seguida, RETURNSTATUS 0para sp e finalmente DONPROC. Ocorreu um erro porque o segundo sp não possui SET NOCOUNT OFF no corpo!
Milan Jaric 2/08/19
Para reformular o que eles encontraram, mas não perceberam, é que, se você tiver 1K solicitando cursor, primeiro faça uma solicitação para definir NOCOUNT como ON ou OFF para conexão e, em seguida, use a mesma conexão para chamar o cursor buscar 1K vezes para economizar largura de banda. O estado real da conexão para NOCOUNT ON ou OFF não afetará a largura de banda; pode apenas confundir a lib do cliente, por exemplo, ADO.net ou ODBC. Assim, "não use SET NOCOUNT <whatever> Se você se preocupa com largura de banda" :)
Milan Jaric
77
  • Quando SET NOCOUNT está ativado, a contagem (indicando o número de linhas afetadas por uma instrução Transact-SQL) não é retornada. Quando SET NOCOUNT está desativado, a contagem é retornada. É usado com qualquer instrução SELECT, INSERT, UPDATE, DELETE.

  • A configuração de SET NOCOUNT é definida no tempo de execução ou execução e não no tempo de análise.

  • SET NOCOUNT ON melhora o desempenho do procedimento armazenado (SP).

  • Sintaxe: SET NOCOUNT {ON | FORA }

Exemplo de SET NOCOUNT ON:

insira a descrição da imagem aqui

Exemplo de SET NOCOUNT OFF:

insira a descrição da imagem aqui

Bhaumik Patel
fonte
7
Fácil e rápido de entender com capturas de tela. Bom trabalho. :)
shaijut
35

Eu acho que até certo ponto é um problema DBA vs. desenvolvedor.

Como desenvolvedor, eu diria que não o use, a menos que seja absolutamente necessário - porque usá-lo pode quebrar seu código ADO.NET (conforme documentado pela Microsoft).

E eu acho que como DBA, você estaria mais do outro lado - use-o sempre que possível, a menos que você realmente deva impedir seu uso.

Além disso, se seus desenvolvedores usam o "RecordsAffected" retornado pela ExecuteNonQuerychamada de método do ADO.NET , você terá problemas se todo mundo usarSET NOCOUNT ON pois nesse caso, ExecuteNonQuery sempre retornará 0.

Veja também a publicação no blog de Peter Bromberg e confira sua posição.

Então, realmente se resume a quem define os padrões :-)

Marc

marc_s
fonte
Ele está em cerca de CRUD simples, porém: a grade de dados, ele menciona poderia usar xml para enviar várias linhas para evitar idas etc
GBN
Eu acho que se você nunca usar SqlDataAdapters e nunca procurar e confiar no número de "registros afetados" retornado pelo ExecuteNonQuery (por exemplo, se você usar algo como Linq-to-SQL ou NHibernate), provavelmente não terá problemas. usando SET NOCOUNT ON em todos os procs armazenados.
27909 marc_s
12

Se você está dizendo que também pode ter clientes diferentes, há problemas com o ADO clássico se SET NOCOUNT não estiver ativado.

Um que eu experimento regularmente: se um procedimento armazenado executa várias instruções (e, portanto, são retornadas várias mensagens "xxx linhas afetadas"), o ADO parece não lidar com isso e gera o erro "Não é possível alterar a propriedade ActiveConnection de um objeto Recordset que tem um objeto de comando como fonte ".

Então, geralmente defendo ativá-lo, a menos que haja realmente uma boa razão para não fazê-lo . você pode ter encontrado a realmente boa razão pela qual preciso ler e ler mais.

Chris J
fonte
9

Correndo o risco de tornar as coisas mais complicadas, incentivo uma regra ligeiramente diferente de todas as que vejo acima:

  • Sempre defina NOCOUNT ONna parte superior de um proc, antes de executar qualquer trabalho no proc, mas também sempre SET NOCOUNT OFFnovamente, antes de retornar quaisquer conjuntos de registros do proc armazenado.

Portanto, "geralmente mantenha a quantidade limitada, exceto quando você estiver retornando um conjunto de resultados". Não conheço nenhuma maneira de quebrar esse código de cliente, significa que o código do cliente nunca precisa saber nada sobre os procedimentos internos do proc, e não é particularmente oneroso.

Tao
fonte
Obrigado. Você pode obter o número de linhas do DataSet ou consumir o contêiner, é claro, mas pode ser útil. Não podemos ter gatilhos nos SELECTs, portanto isso seria seguro: a maioria dos erros do cliente é causada por mensagens falsas nas alterações de dados.
gbn 22/07
O problema com esta regra é que é mais difícil testar do que "SET NOCOUNT ON está no topo do processo?"; Pergunto-me se ferramentas de análise SQL como o SQL Enlight pode testar para esse tipo de coisa ... acrescentando que a minha lista de tarefas de longo prazo para o meu SQL formatador projeto :)
Tao
5

Com relação aos gatilhos que quebram o NHibernate, eu tive essa experiência em primeira mão. Basicamente, quando o NH faz um UPDATE, ele espera um certo número de linhas afetadas. Ao adicionar SET NOCOUNT ON aos gatilhos, você obtém o número de linhas de volta ao que a NH esperava, corrigindo o problema. Então, sim, eu recomendaria definitivamente desativá-lo para gatilhos se você usar o NH.

Em relação ao uso em SPs, é uma questão de preferência pessoal. Eu sempre desliguei a contagem de linhas, mas, novamente, não há argumentos realmente fortes de qualquer maneira.

Em uma nota diferente, você realmente deve considerar se afastar da arquitetura baseada em SP e não terá essa pergunta.

zvolkov
fonte
11
Não concordo em me afastar dos procs armazenados. Isso significa que precisamos ter o mesmo SQL em duas bases de código de cliente diferentes e confiar em nossos codificadores de cliente. Somos DBAs de desenvolvedores. E você não quer dizer "SET NOCOUNT ON "?
gbn 10/10/09
@ CodeBlend: basta pesquisar no Google por mais do que você precisa. No entanto ... stackoverflow.com/a/4040466/27535
gbn
3

Eu queria verificar se 'SET NOCOUNT ON' não salva um pacote de rede nem uma ida e volta

Usei um teste SQLServer 2017 em outro host (usei uma 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 Em seguida, localizei pacotes na porta 1433 com a ferramenta 'Wireshark': botão 'capture filter' -> 'port 1433'

exec procNoCount

este é o pacote de resposta: 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

este é o pacote de resposta: 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

Na linha 40, vejo '07', que é o número de 'linhas afetadas'. Está incluído no pacote de resposta. Nenhum pacote extra.

No entanto, possui 13 bytes extras que podem ser salvos, mas provavelmente não valem mais a pena do que reduzir os nomes das colunas (por exemplo, 'ManagingDepartment' para 'MD')

Portanto, não vejo razão para usá-lo para desempenho

MAS, como outros mencionaram, ele pode quebrar o ADO.NET e eu também deparei com um problema usando o python: MSSQL2008 - Pyodbc - SQL anterior não era uma consulta

Então, provavelmente, um bom hábito ainda ...

phil_w
fonte
1
SET NOCOUNT ON;

Essa linha de código é usada no SQL para não retornar o número de linhas afetadas na execução da consulta. Se não exigirmos o número de linhas afetadas, podemos usá-lo, pois isso ajudará a economizar o uso de memória e a aumentar a velocidade de execução da consulta.

user1509
fonte
2
Observe que @@ ROWCOUNT ainda está definido. SET NOCOUNT ON suprime qualquer resposta extra que o SQL Server envia ao cliente. Veja a resposta aceita acima, por favor
gbn
1

DEFINIR NOCOUNT ON; O código acima interromperá a mensagem gerada pelo mecanismo do servidor sql na janela de resultado frontal após a execução do comando DML / DDL.

Por que fazemos isso? Como o mecanismo do servidor SQL utiliza algum recurso para obter o status e gerar a mensagem, ele é considerado uma sobrecarga para o mecanismo do servidor Sql.

Subhransu Panda
fonte
1

Um lugar que SET NOCOUNT ONpode realmente ajudar é onde você está fazendo consultas em um loop ou cursor. Isso pode adicionar muito tráfego de rede.

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

Ativando as estatísticas do cliente no SSMS, uma execução EXEC NoCountOne EXEC NoCountOffmostra que havia um tráfego extra de 390 KB no NoCountOff:

estatísticas do cliente

Provavelmente não é o ideal para fazer consultas em loop ou cursor, mas também não vivemos no mundo ideal :)

bcoughlan
fonte
0

Eu sei que é uma pergunta muito antiga. mas apenas para atualização.

A melhor maneira de usar "SET NOCOUNT ON" é colocá-lo como primeira instrução no seu SP e desativá-lo novamente pouco antes da última instrução SELECT.

KRules
fonte
-1

Não sei como testar SET NOCOUNT ON entre cliente e SQL, por isso testei um comportamento semelhante para outro comando SET "SET TRANSACTION ISOLATION LEVEL LEIT UNCIMMITTED"

Enviei um comando da minha conexão alterando o comportamento padrão do SQL (READ COMMITTED) e ele foi alterado para os próximos comandos. Quando alterei o nível ISOLATION dentro de um procedimento armazenado, ele não alterou o comportamento da conexão para o próximo comando.

Conclusão atual,

  1. Alterar as configurações dentro do procedimento armazenado não altera as configurações padrão da conexão.
  2. Alterar a configuração enviando comandos usando o ADOCOnnection altera o comportamento padrão.

Eu acho que isso é relevante para outro comando SET, como "SET NOCOUNT ON"

Rabia Mansour
fonte
O seu ponto 1 acima implica que você realmente não precisa DESCOBERTA DE CONTA NO final, porque isso não afeta o ambiente global?
funkymushroom
Não tenho certeza se é isso que ele quis dizer com o ponto 1, mas, em meus testes, sim, aparentemente o ambiente global não é afetado pelo SET NOCOUNT ON dentro de um procedimento armazenado.
Doug
Essa foi uma péssima escolha de comparação, porque o Nível de isolamento está explicitamente relacionado a uma transação específica, portanto, não há razão específica para esperar que seja consistente com uma configuração comoNOCOUNT
IMSoP
-1

if (definir sem contagem == desativado)

{então manterá os dados de quantos registros foram afetados para reduzir o desempenho}, caso contrário {não rastreará o registro de alterações e, portanto, melhorará o desempenho}}

Shubham Sharma
fonte
-1

Às vezes, mesmo as coisas mais simples podem fazer a diferença. Um desses itens simples que devem fazer parte de todo procedimento armazenado é SET NOCOUNT ON. Essa linha de código, colocada na parte superior de um procedimento armazenado, desativa as mensagens que o SQL Server envia de volta ao cliente após a execução de cada instrução T-SQL. Isto é realizado para todos SELECT, INSERT, UPDATE, eDELETE declarações. Ter essas informações é útil quando você executa uma instrução T-SQL em uma janela de consulta, mas quando os procedimentos armazenados são executados, não é necessário que essas informações sejam passadas de volta ao cliente.

Ao remover essa sobrecarga extra da rede, ele pode melhorar significativamente o desempenho geral do seu banco de dados e aplicativo.

Se você ainda precisar obter o número de linhas afetadas pela instrução T-SQL que está sendo executada, ainda poderá usar a @@ROWCOUNTopção Ao emitir uma, SET NOCOUNT ONessa função ( @@ROWCOUNT) ainda funciona e ainda pode ser usada nos procedimentos armazenados para identificar quantas linhas foram afetadas pela instrução.

Vinayak Savale
fonte