Pourquoi SQL Server renvoie-t-il certaines lignes pendant l’exécution de la requête et parfois pas?

33

Il y a des requêtes où, lorsque nous cliquons sur "execute", certaines lignes apparaissent et continuent de croître, mais la requête n'est pas encore terminée. Pourtant, parfois, il attend la fin de la requête.

Pourquoi cela arrive-t-il? Y a-t-il un moyen de contrôler cela?

Racer SQL
la source

Réponses:

43

La réponse, comme d'habitude (d'accord, la plupart du temps), réside dans le plan d'exécution.

Certains opérateurs exigent que toutes les lignes leur parviennent avant de pouvoir commencer à traiter ces lignes et à les transmettre en aval, par exemple:

  • Hash Join (pour construire la table de hachage)
  • Match de hachage
  • Trier (sauf flux de hachage distinct)

Ils sont appelés bloqueurs ou opérateurs stop and go pour cette raison, et ils sont souvent choisis lorsque l'optimiseur pense qu'il va falloir traiter de nombreuses données pour retrouver vos données.

Il existe d'autres opérateurs capables de commencer la diffusion en continu ou de transmettre immédiatement les lignes trouvées.

  • Boucles imbriquées
  • Index pris en charge les jointures de fusion
  • Agrégats de cours d'eau

Lorsque les requêtes commencent à renvoyer des données immédiatement, mais ne se terminent pas immédiatement, cela indique généralement que l'optimiseur a choisi un plan pour localiser et renvoyer rapidement certaines lignes à l'aide d'opérateurs présentant un coût de démarrage moins élevé.

Cela peut arriver à cause d'objectifs de lignes que vous avez définis ou que vous avez optimisés.

Cela peut également arriver si un mauvais plan est choisi pour une raison quelconque (manque de SARGability, détection de paramètre, statistiques insuffisantes, etc.), mais cela prend plus de temps à comprendre.

Pour plus d'informations, consultez le blog de Rob Farley ici

Et la série de Paul White sur les buts alignés ici , ici , ici et ici .

Il convient également de noter que, si vous parlez de SSMS, les lignes n'apparaissent qu'une fois le tampon complet rempli, et pas seulement à volonté.

Erik Darling
la source
14

Si je comprends ce que vous observez, c'est comment Management Studio rend les lignes, et a peu à voir avec la façon dont SQL Server renvoie les lignes. En fait, lorsque vous renvoyez des résultats volumineux à SSMS et tentez de les restituer dans une grille, SSMS ne peut pas suivre et SQL Server finit par attendre que l'application traite plus de lignes. Dans ce cas, SQL Server accumule les temps d' ASYNC_NETWORK_IOattente.

Vous pouvez le contrôler quelque peu en utilisant Résultats en texte au lieu de Résultats en grille, car SSMS peut dessiner du texte plus rapidement que des grilles, mais vous constaterez probablement que cela peut affecter la lisibilité en fonction du nombre de colonnes et des types de données impliqués. SSMS décide d'écrire les résultats dans ce volet, ce qui dépend du niveau de remplissage de la mémoire tampon de sortie.

Lorsque vous avez plusieurs instructions et que vous souhaitez forcer la mémoire tampon à rendre les résultats de sortie au volet des messages, vous pouvez utiliser une petite astuce d'impression entre les instructions:

RAISERROR('', 0, 1) WITH NOWAIT;

Mais cela n’aidera pas si vous voulez que SSMS rende les lignes plus rapidement lorsque toute la sortie provient d’une seule instruction.

Plus directement, vous pouvez le contrôler en limitant le nombre de résultats affichés dans SSMS. Je vois souvent des gens se plaindre du temps qu’il faut pour retourner un million de lignes sur la grille. Qu'est-ce que tout le monde va faire avec un million de lignes dans une grille SSMS, je n'en ai aucune idée.

Certains hacks sont optimaux OPTION (FAST 100)pour récupérer les 100 premières lignes (ou 100 lignes s’il n’ya pas de lignes extérieures ORDER BY), mais cela peut entraîner une récupération beaucoup plus lente pour le reste des lignes et un plan plus complexe. inefficace dans son ensemble, ce n’est donc pas vraiment une option de choix, à mon humble avis.

Aaron Bertrand
la source
1

Votre question ne concerne pas SQLServer en tant que tel, mais:

  • Serveur SQL
  • réseau
  • SSMS en tant qu'application client

Y a-t-il un moyen de contrôler cela?

Réponse courte :

  1. Essayez sqlcmdau lieu de ssmsou sqlcmd-mode dessms
  2. Vérifiez vos paramètres de connexion et de session

Réponse longue :

Bien sûr! Mais pas un - prob

  1. Exécutez votre requête avec sqlcmdou en sqlcmdmode en ssms.
  2. Si vous souhaitez exclure le rôle du réseau, exécutez votre requête sur un serveur avec une connexion à mémoire partagée.
  3. Si les performances des requêtes ne sont pas satisfaisantes même avec une connexion à mémoire partagée, analysez vos plans d'exécution. Si la requête fonctionne mal sur le réseau, appelez l'administrateur du réseau pour obtenir de l'aide. Si votre requête ne fonctionne que dans SSMS, lisez la suite.
  4. Maintenant, nous sommes sûrs que les problèmes sont du côté client (ssms dans ce cas). Examinez les paramètres de connexion et de session dans SSMS. Ne croyez pas l'interface ssms et vérifiez avec SQL Profiler: trouvez votre connexion spidet obtenez la liste complète des paramètres de session. Comparez avec les paramètres de sqlcmdsession. Si rien ne clique, copiez tous les paramètres de session du profileur dans votre script de requête, exécutez-les en sqlcmdmode et , en changeant progressivement les paramètres, vous trouverez votre coupable.

Bonne chance!

Alex Yu
la source
-2

Pour ajouter à la réponse de sp_BlitzErik, prenons l'exemple en utilisant a NOT IN ()avec une sous-sélection. Afin de déterminer si un élément est dans le résultat de la requête imbriquée, il est (généralement) nécessaire de récupérer le résultat complet.

Donc, un moyen simple, que j'ai trouvé pour améliorer les performances de telles requêtes, est de les réécrire comme LEFT OUTER JOINcondition avec où la condition de RIGHTside est nulle (vous pouvez la retourner, bien sûr, mais qui l'utilise RIGHT OUTER JOINS?). Cela permet aux résultats de commencer à revenir immédiatement.

JimmyJames
la source
Je ne pense pas. Si les colonnes comparées ne sont pas nullables, les résultats doivent être identiques et les plans - généralement - identiques, pour les 3 versions d’un antijoin (NOT IN, NOT EXISTS, LEFT JOIN / IS NULL). Il n'est pas nécessaire de récupérer le résultat complet.
Ypercubeᵀᴹ
Si la sous-sélection est vraiment complexe et que la requête générée doit évaluer l'ensemble de la sous-sélection avant de vérifier la condition NOT IN WHERE t.x IN (<complex SELECT subquery>), l'équivalent LEFT JOIN, LEFT JOIN (<complex SELECT subquery>) AS r ON r.x = t.x .... WHERE r.x IS NULLla sous-requête doit également être évaluée (le même plan complexe avec le paramètre NOT Version IN).
Ypercubeᵀᴹ
@ ypercubeᵀᴹ Cela a fonctionné pour moi dans le passé. J'ai vu des requêtes prendre de quelques minutes à revenir à une seconde.
JimmyJames
@ ypercubeᵀᴹ J'ai mis en place un exemple simple sous Oracle (désolé, je n'ai pas accès à SQLServer pour le moment) et ils avaient des plans d'explication différents. Peut-être n’étaient-ils pas des différences significatives, mais elles sont très différentes.
JimmyJames
@ JimmyJames: ce n'est pas un moyen simple si vous voulez des performances stables et de telles "optimisations" sont très sensibles pour la version de SQLServer. Et ne faites pas d'erreur en faisant appel à Oracle (quelle version?). Historiquement, SQLServer était préférable à NOT EXISTSOracle NOT INdans les requêtes. Mais aujourd'hui, cela doit être considéré comme une erreur dans le générateur de plans
Alex Yu