SET NOCOUNT ON utilisation

341

Inspiré par cette question où les avis divergent sur SET NOCOUNT ...

Devrions-nous utiliser SET NOCOUNT ON pour SQL Server? Sinon, pourquoi pas?

What it does Edit 6, le 22 juillet 2011

Il supprime le message "xx lignes affectées" après tout DML. Il s'agit d'un jeu de résultats et une fois envoyé, le client doit le traiter. C'est minuscule, mais mesurable (voir les réponses ci-dessous)

Pour les déclencheurs, etc., le client recevra plusieurs "xx lignes affectées", ce qui provoque toutes sortes d'erreurs pour certains ORM, MS Access, JPA, etc. (voir les modifications ci-dessous)

Contexte:

La meilleure pratique généralement acceptée (je pensais jusqu'à cette question) est d'utiliser SET NOCOUNT ONdans les déclencheurs et les procédures stockées dans SQL Server. Nous l'utilisons partout et un rapide google montre que de nombreux MVP SQL Server sont également d'accord.

MSDN indique que cela peut casser un .net SQLDataAdapter .

Maintenant, cela signifie pour moi que le SQLDataAdapter est limité au traitement CRUD tout simplement parce qu'il s'attend à ce que le message "n lignes affectées" corresponde. Donc, je ne peux pas utiliser:

  • SI EXISTE pour éviter les doublons (aucune ligne affectée au message) Remarque: à utiliser avec prudence
  • O NOT IL N'EXISTE PAS (moins de lignes que prévu
  • Filtrer les mises à jour triviales (par exemple, aucune donnée ne change réellement)
  • Faites tout accès à la table avant (comme la journalisation)
  • Masquer la complexité ou la dénormalisation
  • etc

Dans la question marc_s (qui connaît ses trucs SQL) dit de ne pas l'utiliser. Cela diffère de ce que je pense (et je me considère comme quelque peu compétent en SQL aussi).

Il est possible que je manque quelque chose (n'hésitez pas à souligner l'évidence), mais qu'en pensez-vous?

Remarque: cela fait des années que je n'ai pas vu cette erreur car je n'utilise pas SQLDataAdapter de nos jours.

Modifications après commentaires et questions:

Edit: Plus de réflexions ...

Nous avons plusieurs clients: l'un peut utiliser un C # SQLDataAdaptor, un autre peut utiliser nHibernate de Java. Celles-ci peuvent être affectées de différentes manières avec SET NOCOUNT ON.

Si vous considérez les proc stockés comme des méthodes, alors c'est une mauvaise forme (anti-modèle) de supposer que certains traitements internes fonctionnent d'une certaine manière pour vos propres besoins.

Edit 2: un déclencheur brisant la question nHibernate , où SET NOCOUNT ONne peut pas être défini

(et non, ce n'est pas un doublon de cela )

Edit 3: Encore plus d'informations, grâce à mon collègue MVP

Edit 4: 13 mai 2011

Interrompt également Linq 2 SQL lorsqu'il n'est pas spécifié?

Edit 5: 14 juin 2011

Interrompt JPA, proc stocké avec des variables de table: JPA 2.0 prend-il en charge les variables de table SQL Server?

Edit 6: 15 août 2011

La grille de données SSMS "Modifier les lignes" nécessite SET NOCOUNT ON: Mettre à jour le déclencheur avec GROUP BY

Edit 7: 07 mars 2013

Détails plus approfondis de @RemusRusanu:
SET NOCOUNT ON fait-il vraiment une différence de performance

gbn
la source
@AlexKuznetsov: Quelle serait une approche "Threadsafe"? Les lectures effectuées dans les EXISTS seraient-elles toujours incluses dans toute transaction en cours?
AnthonyWJones
2
@Jeremy Seghi: désolé pour la réponse tardive. Le message (#rows affectés) est un outil client interprété par SSMS, etc.: cependant, un paquet est envoyé avec ces informations. Bien sûr, je sais comment fonctionne @@ rowcount, etc., mais ce n'est pas le but de la question ...
gbn
1
Pas de soucis. Personnellement, je suis d'accord avec votre point de vue; Je commentais simplement qu'il n'y a pas de corrélation directe entre les résultats d'une construction IF / WHERE EXISTS et SET NOCOUNT. J'obtiens des résultats cohérents de ces constructions indépendamment de NOCOUNT. Si vous avez autre chose à dire, envoyez-le moi à ma façon.
Jeremy S
1
@Jeremy Seghi: vous avez raison: SET NOCOUNT ON supprime uniquement le paquet de données supplémentaire renvoyé au client. SI, @@ ROWCOUNT, etc. ne sont pas tous affectés. Oh, et ça casse SQLDataAdapters ... :-)
gbn
1
@Kieren Johnstone: avec le recul, c'est une question mal formulée. Je voterais pour fermer si ce n'était pas ma question ...
gbn

Réponses:

246

Ok maintenant j'ai fait mes recherches, voici l'affaire:

Dans le protocole TDS, SET NOCOUNT ONenregistre uniquement 9 octets par requête tandis que le texte "SET NOCOUNT ON" lui-même est un énorme 14 octets. Je pensais que cela 123 row(s) affectedétait retourné du serveur en texte brut dans un paquet réseau séparé, mais ce n'est pas le cas. Il s'agit en fait d'une petite structure appelée DONE_IN_PROCintégrée dans la réponse. Ce n'est pas un paquet réseau séparé, donc aucun aller-retour n'est gaspillé.

Je pense que vous pouvez vous en tenir au comportement de comptage par défaut presque toujours sans vous soucier des performances. Il existe cependant des cas où le calcul du nombre de lignes à l'avance aurait un impact sur les performances, comme un curseur en avant uniquement. Dans ce cas, NOCOUNT pourrait être une nécessité. En dehors de cela, il n'est absolument pas nécessaire de suivre la devise "utiliser NOCOUNT dans la mesure du possible".

Voici une analyse très détaillée de l'insignifiance du SET NOCOUNTparamètre: http://daleburnett.com/2014/01/everything-ever-wanted-know-set-nocount/

Sedat Kapanoglu
la source
En effet. J'utilise SET NOCOUNT ON depuis toujours, mais marc_s a souligné la limitation de SQLDataAdapter dans l'autre question.
gbn
Merci. L'octet ou la taille n'est pas le problème pour moi, mais le client doit le traiter. C'est la dépendance SQLDataAdapter qui me stupéfie toujours ...
gbn
2
Merci pour votre réponse. J'accepterai cela à cause de vos enquêtes, qui ont déclenché plus d'informations et de travail de ma part. Je ne suis pas d'accord sur les frais généraux cependant: cela peut être important comme le montrent d'autres réponses. À la vôtre, gbn
gbn
13
Ce n'est pas le nombre d'octets son délai d'aller-retour sur le fil qui est le tueur de performance
racingsnail
1
Dans le flux de messages TDS, par exemple, lorsque l'on insère uniquement des valeurs dans la table. DONINPROCLe DONEmessage (RPC) ou (BATCH) est diffusé avec l' rowcountensemble des lignes affectées, tandis que l' done_countindicateur l'est true, peu importe s'il l' NO_COUNTest ON. Dépend de l'implémentation de la bibliothèque cliente dans les cas où la requête contient des instructions SELECT ou des appels RPC qui sélectionnent, il peut être nécessaire de désactiver le comptage ... QUAND désactivé, les lignes sont toujours comptées pour l'instruction select, mais l'indicateur DONE_COUNTest défini sur false. Lisez toujours ce que votre bibliothèque client suggère car il interprètera le flux de jetons (message) à votre place
Milan Jaric
87

Il m'a fallu beaucoup de fouilles pour trouver de vrais chiffres de référence autour de NOCOUNT, alors j'ai pensé que je partagerais un bref résumé.

  • Si votre procédure stockée utilise un curseur pour effectuer un grand nombre d'opérations très rapides sans résultat renvoyé, la désactivation de NOCOUNT peut prendre environ 10 fois plus de temps que l'activation. 1 C'est le pire des cas.
  • Si votre procédure stockée n'effectue qu'une seule opération rapide sans résultat renvoyé, la définition de NOCOUNT ON peut générer une augmentation des performances d'environ 3%. 2 Cela serait cohérent avec une procédure d'insertion ou de mise à jour typique. (Voir les commentaires sur cette réponse pour une discussion sur pourquoi cela ne peut pas toujours être plus rapide.)
  • Si votre procédure stockée renvoie des résultats (c'est-à-dire que vous sélectionnez quelque chose), la différence de performances diminuera proportionnellement à la taille de l'ensemble de résultats.
StriplingWarrior
la source
5
+1 pour l'impact sur le curseur, cela est cohérent avec mes observations
zvolkov
Le point 2 n'est pas précis! Je veux dire le blog auquel il fait référence. N'a jamais ete! DONE et DONEPROC et DONEINPROC sont envoyés avec la même taille, que NO_COUNT soit réglé sur ON ou OFF. RowCount est toujours là comme ULONGLONG (64 octets) et l'indicateur DONE_COUNT est toujours là mais la valeur de bit est 0. Le serveur SQL comptera de toute façon le nombre de lignes, même si vous n'êtes pas intéressé à lire la valeur du jeton DONE. Si vous lisez @@ ROWCOUNT, vous avez ajouté plus d'octets au flux de jetons, soit sous forme de jeton de valeur de retour, soit sous la forme d'un autre jeton colmetadata + ligne!
Milan Jaric
@MilanJaric: Merci d'avoir appelé cela. Vous m'avez aidé à réaliser que j'avais lié le mauvais article. Le lien est maintenant mis à jour et l'article présente un argument convaincant pour montrer qu'il peut y avoir une légère amélioration des performances avec SET NOCOUNT ON. Pensez-vous qu'il y a des problèmes avec les méthodes de référence utilisées?
StriplingWarrior
:) toujours inexactes sur SET NOCOUNT OFF / ON, l'erreur est que le deuxième SP n'a pas SET NOCOUNT OFF;et c'est pourquoi ils pensent qu'ils n'obtiennent pas d'octets supplémentaires en réponse. Une référence précise serait d'utiliser SET NOCOUNT ONdans la SET NOCOUNT OFFprocédure stockée à gauche et à droite. De cette façon, vous obtiendrez le package TDS avec DONEINPROC (SET NOCOUNT ...), dix à nouveau DONEINPROC (INSERT statement), puis RETURNVALUE(@@ROWCOUNT), puis RETURNSTATUS 0pour sp et enfin DONPROC. L'erreur est là car le deuxième sp n'a pas SET NOCOUNT OFF dans le corps!
Milan Jaric
Pour reformuler ce qu'ils ont trouvé mais ils ne l'ont pas réalisé, c'est que si vous avez des requêtes de curseur de récupération 1K, faites d'abord une demande pour définir NOCOUNT sur ON ou OFF pour la connexion, puis utilisez la même connexion pour appeler la récupération de curseur 1K fois pour économiser de la bande passante. L'état de connexion réel pour NOCOUNT ON ou OFF n'affectera pas la bande passante, cela peut simplement confondre la bibliothèque client, par exemple ADO.net ou ODBC. Donc "n'utilisez pas SET NOCOUNT <WHATEVER> si vous vous souciez de la bande passante" :)
Milan Jaric
77
  • Lorsque SET NOCOUNT est activé, le nombre (indiquant le nombre de lignes affectées par une instruction Transact-SQL) n'est pas renvoyé. Lorsque SET NOCOUNT est OFF, le compte est renvoyé. Il est utilisé avec n'importe quelle instruction SELECT, INSERT, UPDATE, DELETE.

  • Le paramètre SET NOCOUNT est défini au moment de l'exécution ou de l'exécution et non au moment de l'analyse.

  • SET NOCOUNT ON améliore les performances des procédures stockées (SP).

  • Syntaxe: SET NOCOUNT {ON | OFF}

Exemple de SET NOCOUNT ON:

entrez la description de l'image ici

Exemple de SET NOCOUNT OFF:

entrez la description de l'image ici

Bhaumik Patel
la source
7
Facile et rapide à comprendre avec des captures d'écran. Bon travail. :)
shaijut
35

Je suppose que dans une certaine mesure, c'est un problème entre DBA et développeur.

En tant que développeur, je dirais de ne pas l'utiliser à moins que vous ne deviez absolument le faire - car l'utiliser peut casser votre code ADO.NET (comme documenté par Microsoft).

Et je suppose qu'en tant que DBA, vous seriez plus de l'autre côté - utilisez-le autant que possible, sauf si vous devez vraiment empêcher son utilisation.

De plus, si vos développeurs utilisent jamais le "RecordsAffected" renvoyé par l' ExecuteNonQueryappel de méthode d'ADO.NET , vous avez des problèmes si tout le monde utilise SET NOCOUNT ONcar dans ce cas, ExecuteNonQuery retournera toujours 0.

Consultez également le blog de Peter Bromberg et vérifiez sa position.

Donc, cela se résume vraiment à qui peut fixer les normes :-)

Marc

marc_s
la source
Il est sur le CRUD simple cependant: la grille de données qu'il mentionne pourrait utiliser xml pour envoyer plusieurs lignes pour éviter les allers-retours, etc.
gbn
Je suppose que si vous n'utilisez jamais SqlDataAdapters, et que vous ne vérifiez jamais le nombre "d'enregistrements affectés" renvoyé par ExecuteNonQuery (par exemple si vous utilisez quelque chose comme Linq-to-SQL ou NHibernate), vous n'avez probablement aucun problème en utilisant SET NOCOUNT ON dans tous les processus stockés.
marc_s
12

Si vous dites que vous pourriez également avoir des clients différents, il y a des problèmes avec ADO classique si SET NOCOUNT n'est pas activé.

Un que je rencontre régulièrement: si une procédure stockée exécute un certain nombre d'instructions (et donc un certain nombre de messages "xxx lignes affectées" sont renvoyés), ADO ne semble pas gérer cela et génère l'erreur "Impossible de modifier la propriété ActiveConnection d'un objet Recordset qui a un objet Command comme source. "

Je préconise donc généralement de l'activer, sauf s'il existe une très bonne raison de ne pas le faire. vous avez peut-être trouvé la très bonne raison que je dois approfondir.

Chris J
la source
9

Au risque de compliquer les choses, j'encourage une règle légèrement différente de toutes celles que je vois ci-dessus:

  • Toujours mettre NOCOUNT ONau - dessus d'un proc, avant de faire des travaux dans le proc, mais aussi toujours à SET NOCOUNT OFFnouveau, avant de retourner les enregistrements de la procédure stockée.

Donc, "gardez généralement nocount activé, sauf lorsque vous retournez réellement un jeu de résultats". Je ne sais pas comment cela peut casser n'importe quel code client, cela signifie que le code client n'a jamais besoin de savoir quoi que ce soit sur les processus internes, et ce n'est pas particulièrement onéreux.

Tao
la source
Merci. Vous pouvez obtenir le nombre de lignes à partir du DataSet ou du conteneur consommateur bien sûr, mais cela pourrait être utile. Nous ne pouvons pas avoir de déclencheurs sur SELECT, ce serait donc sûr: la plupart des erreurs client sont causées par des messages parasites sur les modifications de données.
gbn
Le problème avec cette règle est simplement qu'il est plus difficile à tester que "SET NOCOUNT ON est-il en haut du proc?"; Je me demande si des outils d'analyse SQL comme Sql Enlight peuvent tester ce genre de chose ... L'ajouter à ma liste de tâches à long terme pour mon projet de formateur SQL :)
Tao
5

En ce qui concerne les déclencheurs brisant NHibernate, j'ai eu cette expérience de première main. Fondamentalement, lorsque NH effectue une MISE À JOUR, il attend un certain nombre de lignes affectées. En ajoutant SET NOCOUNT ON aux déclencheurs, vous obtenez le nombre de lignes à ce que NH attendait, ce qui résout le problème. Alors oui, je recommanderais certainement de le désactiver pour les déclencheurs si vous utilisez NH.

En ce qui concerne l'utilisation dans les SP, c'est une question de préférence personnelle. J'avais toujours désactivé le décompte des lignes, mais là encore, il n'y a pas vraiment d'arguments solides dans les deux cas.

Sur une note différente, vous devriez vraiment envisager de vous éloigner de l'architecture basée sur SP, alors vous n'aurez même pas cette question.

zvolkov
la source
1
Je suis en désaccord avec l'éloignement des procs stockés. Cela signifierait que nous devons avoir le même SQL dans 2 bases de code client différentes et faire confiance à nos codeurs clients. Nous sommes développeurs DBA. Et vous ne voulez pas dire "SET NOCOUNT ON "?
gbn
@CodeBlend: il suffit de le rechercher sur Google pour plus que jamais. Cependant ... stackoverflow.com/a/4040466/27535
gbn
3

Je voulais vérifier moi-même que «SET NOCOUNT ON» ne sauvegarde pas un paquet réseau ni un aller-retour

J'ai utilisé un test SQLServer 2017 sur un autre hôte (j'ai utilisé une 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 Puis j'ai tracé des paquets sur le port 1433 avec l'outil 'Wireshark': bouton 'capture filter' -> 'port 1433'

exec procNoCount

c'est le paquet de réponse: 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

c'est le paquet de réponse: 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

À la ligne 40, je peux voir «07», qui est le nombre de «lignes affectées». Il est inclus dans le paquet de réponse. Aucun paquet supplémentaire.

Il a cependant 13 octets supplémentaires qui pourraient être enregistrés, mais cela ne vaut probablement pas mieux que de réduire les noms de colonnes (par exemple, "ManagingDepartment" en "MD")

Je ne vois donc aucune raison de l'utiliser pour la performance

MAIS Comme d'autres l'ont mentionné, cela peut casser ADO.NET et j'ai également rencontré un problème en utilisant python: MSSQL2008 - Pyodbc - Le SQL précédent n'était pas une requête

Alors probablement une bonne habitude ...

phil_w
la source
1
SET NOCOUNT ON;

Cette ligne de code est utilisée en SQL pour ne pas renvoyer le nombre de lignes affectées lors de l'exécution de la requête. Si nous n'avons pas besoin du nombre de lignes affectées, nous pouvons l'utiliser car cela aiderait à économiser l'utilisation de la mémoire et augmenterait la vitesse d'exécution de la requête.

user1509
la source
2
Notez que @@ ROWCOUNT est toujours défini. SET NOCOUNT ON supprime toute réponse supplémentaire que SQL Server envoie au client. Voir la réponse acceptée ci-dessus s'il vous plaît
gbn
1

ACTIVER NOCOUNT; Le code ci-dessus arrêtera le message généré par le moteur de serveur SQL dans la fenêtre de résultats frontale après l'exécution de la commande DML / DDL.

Pourquoi on le fait? Comme le moteur de serveur SQL prend des ressources pour obtenir l'état et générer le message, il est considéré comme une surcharge du moteur de serveur SQL. Nous activons donc le message non compté.

Subhransu Panda
la source
1

Un endroit qui SET NOCOUNT ONpeut vraiment aider est l'endroit où vous effectuez des requêtes dans une boucle ou un curseur. Cela peut représenter beaucoup de trafic réseau.

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

Activer les statistiques client dans SSMS, une exécution de EXEC NoCountOnet EXEC NoCountOffmontre qu'il y avait un trafic supplémentaire de 390 Ko sur celui NoCountOff:

statistiques clients

Probablement pas idéal pour faire des requêtes en boucle ou en curseur, mais nous ne vivons pas non plus dans un monde idéal :)

bcoughlan
la source
0

Je sais que c'est une assez vieille question. mais juste pour la mise à jour.

La meilleure façon d'utiliser "SET NOCOUNT ON" est de le mettre en tant que première instruction dans votre SP et de le désactiver à nouveau juste avant la dernière instruction SELECT.

KRules
la source
0

SET NOCOUNT ON permet même d'accéder aux lignes affectées comme ceci:

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

Résultats

lignes affectées

1

messages

Les commandes ont abouti.

Heure de fin: 2020-06-18T16: 20: 16.9686874 + 02: 00

user__42
la source
-1

Je ne sais pas comment tester SET NOCOUNT ON entre le client et SQL, j'ai donc testé un comportement similaire pour une autre commande SET "SET TRANSACTION ISOLATION LEVEL READ UNCIMMITTED"

J'ai envoyé une commande à partir de ma connexion en modifiant le comportement par défaut de SQL (READ COMMITTED), et elle a été modifiée pour les commandes suivantes. Lorsque j'ai changé le niveau d'ISOLATION dans une procédure stockée, cela n'a pas changé le comportement de connexion pour la commande suivante.

Conclusion actuelle,

  1. La modification des paramètres dans la procédure stockée ne modifie pas les paramètres par défaut de la connexion.
  2. La modification des paramètres en envoyant des commandes à l'aide d'ADOCOnnection modifie le comportement par défaut.

Je pense que cela concerne d'autres commandes SET telles que "SET NOCOUNT ON"

Rabia Mansour
la source
Votre point 1 ci-dessus implique-t-il que vous n'avez pas vraiment besoin de désactiver le compte à la fin car cela n'affecte pas l'environnement global?
funkymushroom
Je ne sais pas si c'est ce qu'il voulait dire avec le point 1, mais dans mes tests oui, apparemment, l'environnement global n'est pas affecté par SET NOCOUNT ON dans une procédure stockée.
Doug
Ce n'était pas un bon choix de comparaison, car le niveau d'isolement est explicitement lié à une transaction particulière, donc il n'y a aucune raison particulière de s'attendre à ce qu'elle soit cohérente avec un paramètre commeNOCOUNT
IMSoP
-1

if (pas de décompte == off)

{alors il conservera des données sur le nombre d'enregistrements affectés, donc réduisez les performances} else {il ne suivra pas l'enregistrement des modifications, améliorant ainsi les performances}}

Shubham Sharma
la source
-1

Parfois, même les choses les plus simples peuvent faire la différence. L'un de ces éléments simples qui devraient faire partie de chaque procédure stockée est SET NOCOUNT ON. Cette seule ligne de code, placée en haut d'une procédure stockée, désactive les messages que SQL Server renvoie au client après l'exécution de chaque instruction T-SQL. Ceci est effectué pour tous SELECT, INSERT, UPDATEet DELETEdéclarations. La possession de ces informations est pratique lorsque vous exécutez une instruction T-SQL dans une fenêtre de requête, mais lorsque des procédures stockées sont exécutées, il n'est pas nécessaire que ces informations soient transmises au client.

En supprimant cette surcharge supplémentaire du réseau, elle peut considérablement améliorer les performances globales de votre base de données et de votre application.

Si vous devez toujours obtenir le nombre de lignes affectées par l'instruction T-SQL en cours d'exécution, vous pouvez toujours utiliser l' @@ROWCOUNToption. En émettant une SET NOCOUNT ONfonction, cette fonction ( @@ROWCOUNT) fonctionne toujours et peut toujours être utilisée dans vos procédures stockées pour identifier le nombre de lignes affectées par l'instruction.

Vinayak Savale
la source