Question

Existe-t-il une différence dans les performances des trois instructions SQL suivantes?

SELECT * FROM tableA WHERE EXISTS (SELECT * FROM tableB WHERE tableA.x = tableB.y)

SELECT * FROM tableA WHERE EXISTS (SELECT y FROM tableB WHERE tableA.x = tableB.y)

SELECT * FROM tableA WHERE EXISTS (SELECT 1 FROM tableB WHERE tableA.x = tableB.y)

Ils devraient tous fonctionner et renvoyer le même jeu de résultats. Mais est-ce important que le SELECT interne sélectionne tous les champs de la tableB, un champ ou juste une constante?

Existe-t-il une meilleure pratique lorsque toutes les déclarations se comportent de la même manière?

Était-ce utile?

La solution

La vérité sur la clause EXISTS est que la clause SELECT n'est pas évaluée dans une clause EXISTS - vous pouvez essayer:

SELECT * 
  FROM tableA 
 WHERE EXISTS (SELECT 1/0 
                 FROM tableB 
                WHERE tableA.x = tableB.y)

... et devrait s'attendre à une erreur de division par zéro, mais vous ne le ferez pas car elle n'est pas évaluée. C’est pourquoi j’ai l’habitude de spécifier NULL dans un fichier EXISTS pour démontrer que le SELECT peut être ignoré:

SELECT * 
  FROM tableA 
 WHERE EXISTS (SELECT NULL
                 FROM tableB 
                WHERE tableA.x = tableB.y)

Tout ce qui compte dans une clause EXISTS, ce sont les clauses FROM et au-delà - WHERE, GROUP BY, HAVING, etc.

Cette question ne visait pas une base de données à l’esprit, et elle le devrait parce que les fournisseurs gèrent les choses différemment - alors testez et vérifiez les plans explication / exécution pour confirmer. Il est possible que le comportement change entre les versions ...

Autres conseils

Certainement # 1. Ça "regarde" effrayant, mais réalisez que l'optimiseur fera la bonne chose et exprime l'intention. Il y a aussi un léger bonus de frappe si on pense accidentellement EXISTE mais tapez IN. # 2 est acceptable mais pas expressif. La troisième option pue à mon avis pas si humble. C'est trop près de dire "si" aucune valeur "n'existe " pour le confort.

En général, il est important de ne pas avoir peur d'écrire du code qui semble inefficace s'il présente d'autres avantages et n'affecte pas réellement les performances.

En d’autres termes, l’optimiseur exécutera presque toujours votre sorcière complexe de jointure / sélection / regroupement pour enregistrer une sous-requête EXISTS / simple de la même manière.

Après vous être remis bravo pour avoir réécrit intelligemment ce vilain OU d'une jointure, vous finirez par réalisez que l’optimiseur utilisait toujours le même plan d’exécution de merde pour résoudre la requête beaucoup plus simple à comprendre avec le OU intégré de toute façon.

La morale de l'histoire est de connaître votre optimiseur de plateformes. Essayez différentes choses et voyez ce qui est réellement fait, car les hypothèses sournoises sur l’optimisation des requêtes "décoratives" sont presque toujours incorrectes et non pertinentes par rapport à mon expérience.

Je réalise qu'il s'agit d'un ancien message, mais j'estimais qu'il était important de préciser pourquoi on pourrait choisir un format plutôt qu'un autre.

Premièrement, comme d'autres l'ont souligné, le moteur de base de données est supposé ignorer la clause Select. Chaque version de SQL Server a / fait, Oracle, MySQL, etc. Dans beaucoup de lunes de développement de bases de données, je n'ai jamais rencontré qu'un seul SGBD qui n'ignorait pas correctement la clause Select: Microsoft Access. Plus précisément, les anciennes versions de MS Access (je ne peux pas parler des versions actuelles).

Avant de découvrir cette "fonctionnalité", j'avais l'habitude d'utiliser Exists (Select * ... . Cependant, j'ai découvert que MS Access transmettait en continu toutes les colonnes de la sous-requête, puis jetez-les ( Select 1/0 ne fonctionnerait pas non plus). Cela m'a convaincu de passer à Select 1 . Si même un SGBD était stupide, un autre pourrait exister.

L’écriture de existe (sélectionnez 1 ... pour indiquer clairement son intention (c’est franchement ridicule de dire "c’est trop proche de dire" si aucune valeur n’existe "pour plus de confort. . ") et rend presque impossible la probabilité qu’un SGBD fasse quelque chose de stupide avec l’instruction Select. Select Null servirait le même but mais nécessiterait simplement plus de caractères à écrire.

Je suis passé à Exists (sélectionnez 1 pour vous assurer que le SGBD ne pourrait pas être stupide. Cependant, c'était il y a plusieurs lunes et aujourd'hui, je m'attendrais à ce que la plupart des développeurs s'attendent à voir Existe (Sélectionnez * qui fonctionnera exactement de la même manière.

Cela dit, je peux fournir une bonne raison d'éviter existant (Sélectionnez * même si votre SGBD l'évalue correctement. Il est beaucoup plus facile de rechercher et de contourner toutes les utilisations de . Sélectionner * si vous n'avez pas à ignorer chaque instance de son utilisation dans une clause Exists.

Dans SQL Server au moins,

La plus petite quantité de données pouvant être lue à partir du disque est une seule "page". de l'espace disque. Dès que le processeur lit un enregistrement qui satisfait les prédicats de sous-requête, il peut s'arrêter. La sous-requête n'est pas exécutée comme si elle était autonome, puis incluse dans la requête externe, elle est exécutée dans le cadre du plan de requête complet pour l'ensemble. Ainsi, lorsqu'il est utilisé en tant que sous-requête, le contenu de la clause Select n'a pas d'importance, rien n'est renvoyé " à la requête externe quand même, sauf un booléen pour indiquer si un seul enregistrement a été trouvé ou non ...

Tous les trois utilisent exactement le même plan d'exécution

J'utilise toujours [Select * From ...] comme je pense qu'il se lit mieux, en n'impliquant pas que je veuille renvoyer quelque chose de particulier à partir de la sous-requête.

EDIT: De dave costa comment ... Oracle utilise également le même plan d’exécution pour les trois options

C’est l’une de ces questions qui amorce une sorte de guerre sainte.

Il existe une assez bonne discussion à ce sujet ici .

Je pense que la solution consiste probablement à utiliser la troisième option, mais l'augmentation de la vitesse est tellement infinitésimale qu'il ne vaut vraiment pas la peine de s'inquiéter. C’est facilement le type de requête que SQL Server peut de toute façon optimiser en interne, de sorte que toutes les options sont équivalentes.

Le EXISTS renvoie des données booléennes non réelles, indiquant que la meilleure pratique consiste à utiliser # 3.

Plan d'exécution . Apprenez-le, utilisez-le, aimez-le

Il n’existe aucun moyen de deviner, vraiment.

En plus de ce que d’autres ont dit, la pratique consistant à utiliser SELECT 1 provient de l’ancien Microsoft SQL Server (avant 2005). table pour SELECT * . À ma connaissance, aucun autre SGBD n’a cette déficience.

Les tests EXISTS vérifient la présence de lignes, pas ce qu’elles contiennent, donc, mis à part certaines bizarreries d’optimiseur similaires à celles décrites ci-dessus, le contenu de la liste SELECT n’a pas vraiment d'importance.

Le SELECT * semble être l’habitude, mais d’autres sont également acceptables.

# 3 devrait être la meilleure solution, car vous n’avez de toute façon pas besoin des données renvoyées. Apporter les champs ne fera qu’ajouter une surcharge supplémentaire

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top