Chaque fois que je dois vérifier l'existence d'une ligne dans une table, j'ai tendance à toujours écrire une condition telle que:
SELECT a, b, c
FROM a_table
WHERE EXISTS
(SELECT * -- This is what I normally write
FROM another_table
WHERE another_table.b = a_table.b
)
Certaines personnes écrivent comme ceci:
SELECT a, b, c
FROM a_table
WHERE EXISTS
(SELECT 1 --- This nice '1' is what I have seen other people use
FROM another_table
WHERE another_table.b = a_table.b
)
Lorsque la condition est NOT EXISTS
remplacée par EXISTS
: Dans certains cas, je peux l'écrire avec une LEFT JOIN
condition supplémentaire (parfois appelée antijointure ):
SELECT a, b, c
FROM a_table
LEFT JOIN another_table ON another_table.b = a_table.b
WHERE another_table.primary_key IS NULL
J'essaie de l'éviter parce que je pense que le sens est moins clair, en particulier lorsque ce qui vous primary_key
manque n'est pas évident, ou lorsque votre clé primaire ou votre condition de jointure est multi-colonne (et vous pouvez facilement oublier l'une des colonnes). Cependant, vous conservez parfois du code écrit par quelqu'un d'autre ... et il est juste là.
Y a-t-il une différence (autre que le style) à utiliser à la
SELECT 1
placeSELECT *
?
Existe-t-il des cas où cela ne se comporte pas de la même manière?Bien que ce que j’ai écrit soit le code SQL (AFAIK) standard: existe-t-il une telle différence pour différentes bases de données / versions antérieures?
Y-a-t-il un avantage à écrire explicitement un anti-jointure?
Les planificateurs / optimistes contemporains le traitent-ils différemment de laNOT EXISTS
clause?
la source
EXISTS (SELECT FROM ...)
.Réponses:
Non, il n'y a pas de différence d'efficacité entre
(NOT) EXISTS (SELECT 1 ...)
et(NOT) EXISTS (SELECT * ...)
dans tous les principaux SGBD. J'ai souvent vu(NOT) EXISTS (SELECT NULL ...)
être utilisé aussi.Dans certains cas, vous pouvez même écrire
(NOT) EXISTS (SELECT 1/0 ...)
et le résultat est le même - sans erreur (division par zéro), ce qui prouve que l'expression n'y est même pas évaluée.A propos de la
LEFT JOIN / IS NULL
méthode anti - jointure, une correction: cela équivaut àNOT EXISTS (SELECT ...)
.Dans ce cas,
NOT EXISTS
vsLEFT JOIN / IS NULL
, vous pouvez obtenir différents plans d’exécution. Dans MySQL par exemple et surtout dans les versions antérieures (antérieures à la version 5.7), les plans seraient assez similaires mais non identiques. Les optimiseurs d'autres SGBD (SQL Server, Oracle, Postgres, DB2) sont, à ma connaissance, plus ou moins capables de réécrire ces 2 méthodes et d'envisager les mêmes plans pour les deux. Néanmoins, il n’existe pas de telle garantie et lors de l’optimisation, il est bon de vérifier les plans de différentes réécritures équivalentes, dans la mesure où il se peut que chaque optimiseur ne réécrit pas (par exemple, des requêtes complexes, avec de nombreuses jointures et / ou des tables dérivées / les sous-requêtes à l'intérieur de la sous-requête, où les conditions de plusieurs tables, les colonnes composites utilisées dans les conditions de jointure) ou les choix et les plans de l'optimiseur sont affectés différemment par les index, les paramètres disponibles, etc.Notez également que
USING
vous ne pouvez pas utiliser tous les SGBD (SQL Server par exemple). Le plus communJOIN ... ON
fonctionne partout.Et les colonnes doivent être préfixées avec le nom de la table / alias dans le
SELECT
pour éviter les erreurs / ambiguïtés lorsque nous avons des jointures.Je préfère également généralement mettre la colonne jointe dans la
IS NULL
vérification (bien que la clé PK ou toute colonne non nullable serait OK, cela pourrait être utile pour l'efficacité lorsque le planLEFT JOIN
utilise un index non clusterisé):Il existe également une troisième méthode pour les anti-jointures,
NOT IN
mais elle utilise une sémantique (et des résultats!) Différents si la colonne de la table interne est nullable. Il peut être utilisé en excluant les lignesNULL
, rendant la requête équivalente aux 2 versions précédentes:Cela donne aussi généralement des plans similaires dans la plupart des SGBD.
la source
[NOT] IN (SELECT ...)
, bien qu'équivalent, ses performances étaient très mauvaises. L'éviter!SELECT *
fait certainement plus de travail. Par souci de simplicité, je vous conseillerais d'utiliserSELECT 1
Il existe une catégorie de cas où
SELECT 1
etSELECT *
qui ne sont pas interchangeables - plus précisément, l'un sera toujours accepté dans ces cas, tandis que l'autre ne le sera généralement pas.Je parle de cas où vous devez vérifier l'existence de lignes d'un ensemble groupé . Si table
T
contient des colonnesC1
etC2
que vous vérifiez l'existence de groupes de lignes correspondant à une condition spécifique, vous pouvez utiliser laSELECT 1
méthode suivante:mais vous ne pouvez pas utiliser
SELECT *
de la même manière.C'est simplement un aspect syntaxique. Lorsque les deux options sont acceptées syntaxiquement, vous n'aurez probablement aucune différence en termes de performances ou de résultats renvoyés, comme cela a été expliqué dans l' autre réponse .
Notes complémentaires à la suite des commentaires
Il semble que peu de produits de base de données supportent cette distinction. Des produits tels que SQL Server, Oracle, MySQL et SQLite accepteront volontiers
SELECT *
la requête ci-dessus sans erreur, ce qui signifie probablement qu'ils traitent un EXISTSSELECT
de manière particulière.PostgreSQL est un SGBDR sur lequel le système
SELECT *
peut échouer, mais peut toujours fonctionner dans certains cas. En particulier, si vous regroupez par PK,SELECT *
tout fonctionnera correctement, sinon le message:la source
GROUP BY
, le concept de*
est sans signification (ou, du moins, pas si clair).Une façon sans doute intéressante de réécrire la
EXISTS
clause qui aboutit à une requête plus claire et peut-être moins trompeuse, du moins dans SQL Server, serait la suivante:La version anti-semi-jointure de cela ressemblerait à ceci:
Les deux sont généralement optimisés sur le même plan que
WHERE EXISTS
ouWHERE NOT EXISTS
, mais l'intention est indéniable, et vous n'avez pas d '"étrange"1
ou*
.Fait intéressant, les problèmes de contrôle nul associés à
NOT IN (...)
sont problématiques pour<> ALL (...)
, alors qu'ilsNOT EXISTS (...)
ne souffrent pas de ce problème. Considérez les deux tables suivantes avec une colonne nullable:Nous allons ajouter des données aux deux, avec des lignes qui correspondent et d'autres qui ne le sont pas:
La
NOT IN (...)
requête:A le plan suivant:
La requête ne renvoie aucune ligne car les valeurs NULL rendent impossible la confirmation de l'égalité.
Cette requête, avec
<> ALL (...)
affiche le même plan et ne renvoie aucune ligne:La variante using
NOT EXISTS (...)
affiche une forme de plan légèrement différente et renvoie des lignes:Le plan:
Les résultats de cette requête:
Cela rend l'utilisation
<> ALL (...)
aussi sujette à des résultats problématiques queNOT IN (...)
.la source
*
pas étrange: je lisEXISTS (SELECT * FROM t WHERE ...)
ASthere is a _row_ in table _t_ that...
. Quoi qu'il en soit, j'aime bien avoir des alternatives, et la vôtre est clairement lisible. Un doute / une mise en garde: comment va-t-il se comporter sib
est annulable? [J'ai eu de mauvaises expériences et quelques nuits courtes en essayant de découvrir un méfait causé par unx IN (SELECT something_nullable FROM a_table)
]La "preuve" qu'ils sont identiques (en MySQL) est à faire
puis répétez avec
SELECT 1
. Dans les deux cas, la sortie "étendue" montre qu'elle a été transforméeSELECT 1
.De même,
COUNT(*)
est transformé enCOUNT(0)
.Autre chose à noter: des améliorations d’optimisation ont été apportées dans les versions récentes. Il peut être intéressant de comparer
EXISTS
vs anti-jointures. Votre version peut faire un meilleur travail avec l'un par rapport à l'autre.la source
Dans certaines bases de données, cette optimisation ne fonctionne pas encore. Comme par exemple dans PostgreSQL La version 9.6 échouera.
Et ça va réussir.
C'est un échec parce que ce qui suit échoue, mais cela signifie qu'il y a toujours une différence.
Vous pouvez trouver plus d'informations sur ce caprice particulier et la violation de la spécification dans la réponse à la question suivante: La spécification SQL requiert-elle un GROUP BY dans EXISTS ()
la source
J'ai toujours utilisé
select top 1 'x'
(SQL Server)Théoriquement,
select top 1 'x'
serait plus efficace queselect *
, puisque le premier serait complet après avoir sélectionné une constante sur l’existence d’une ligne qualificative, alors que le second sélectionnerait tout.CEPENDANT, même si cela a très tôt été pertinent, l’optimisation a rendu la différence non pertinente dans probablement tous les principaux RDBS.
la source
top n
sansorder by
sont une bonne idée.select top 1 'x'
ne devrait pas être plus efficace queselect *
dans uneExist
expression. En pratique, il peut être plus efficace si l'optimiseur fonctionne de manière sous-optimale mais, théoriquement, les deux expressions sont équivalentes.IF EXISTS(SELECT TOP(1) 1 FROM
est une meilleure habitude à long terme et sur toutes les plateformes simplement parce que vous n’avez même pas besoin de vous inquiéter de la qualité de votre plate-forme / version actuelle; et SQL va deTOP n
paramétrableTOP(n)
. Cela devrait être une compétence unique.la source
TOP
n'est même pas valide SQL.TOP (n)
de "SQL" - le langage de requête standard. Il y en a un sur T-SQL qui est le dialecte utilisé par Microsoft SQL Server.