Laquelle de ces requêtes est la plus rapide?
N'EXISTE PAS:
SELECT ProductID, ProductName
FROM Northwind..Products p
WHERE NOT EXISTS (
SELECT 1
FROM Northwind..[Order Details] od
WHERE p.ProductId = od.ProductId)
Ou PAS DANS:
SELECT ProductID, ProductName
FROM Northwind..Products p
WHERE p.ProductID NOT IN (
SELECT ProductID
FROM Northwind..[Order Details])
Le plan d'exécution des requêtes indique qu'ils font tous les deux la même chose. Si tel est le cas, quelle est la forme recommandée?
Ceci est basé sur la base de données NorthWind.
[Éditer]
Je viens de trouver cet article utile: http://weblogs.sqlteam.com/mladenp/archive/2007/05/18/60210.aspx
Je pense que je vais m'en tenir à PAS EXISTE.
sql
sql-server
notin
ilitirit
la source
la source
NOT IN
requête:SELECT "A".* FROM "A" WHERE "A"."id" NOT IN (SELECT "B"."Aid" FROM "B" WHERE "B"."Uid" = 2)
est presque 30 fois plus rapide que celaNOT EXISTS
:SELECT "A".* FROM "A" WHERE (NOT (EXISTS (SELECT 1 FROM "B" WHERE "B"."user_id" = 2 AND "B"."Aid" = "A"."id")))
Réponses:
J'ai toujours par défaut
NOT EXISTS
.Les plans d'exécution peuvent être les mêmes pour le moment, mais si l'une ou l'autre colonne est modifiée à l'avenir pour permettre à
NULL
s, laNOT IN
version devra faire plus de travail (même si aucunNULL
s n'est réellement présent dans les données) et la sémantique deNOT IN
siNULL
s sont présents ne sont probablement pas ceux que vous voulez de toute façon.Lorsque ni
Products.ProductID
ou[Order Details].ProductID
permettreNULL
de leNOT IN
seront traités de manière identique à la requête suivante.Le plan exact peut varier, mais pour mes données d'exemple, j'obtiens ce qui suit.
Une idée fausse assez courante semble être que les sous-requêtes corrélées sont toujours «mauvaises» par rapport aux jointures. Ils peuvent certainement l'être lorsqu'ils forcent un plan de boucles imbriquées (sous-requête évaluée ligne par ligne), mais ce plan inclut un opérateur logique anti-semi-jointure. Les semi-jointures anti ne sont pas limitées aux boucles imbriquées mais peuvent également utiliser des jointures de hachage ou de fusion (comme dans cet exemple).
Si
[Order Details].ProductID
estNULL
-able la requête devient alorsLa raison en est que la sémantique correcte si
[Order Details]
contient desNULL
ProductId
s est de ne renvoyer aucun résultat. Consultez la bobine anti-jointure supplémentaire et le nombre de lignes pour vérifier ce qui est ajouté au plan.Si
Products.ProductID
est également modifié pour devenirNULL
-able, la requête devient alorsLa raison en est que
NULL
Products.ProductId
ne doit pas être renvoyé dans les résultats, sauf si laNOT IN
sous-requête ne doit renvoyer aucun résultat (c'est-à[Order Details]
- dire que la table est vide). Dans ce cas, cela devrait. Dans le plan de mes exemples de données, cela est implémenté en ajoutant un autre anti-jointure comme ci-dessous.L'effet de cela est montré dans le billet de blog déjà lié par Buckley . Dans cet exemple, le nombre de lectures logiques passe d'environ 400 à 500 000.
De plus, le fait qu'un seul
NULL
puisse réduire le nombre de lignes à zéro rend l'estimation de la cardinalité très difficile. Si SQL Server suppose que cela se produira mais qu'en fait il n'y avait pas deNULL
lignes dans les données, le reste du plan d'exécution peut être catastrophiquement pire, s'il ne s'agit que d'une partie d'une plus grande requête, avec des boucles imbriquées inappropriées provoquant l'exécution répétée d'un sous-système coûteux arbre par exemple .Ce n'est cependant pas le seul plan d'exécution possible pour une colonne
NOT IN
surNULL
-able. Cet article en montre un autre pour une requête sur laAdventureWorks2008
base de données.Pour le
NOT IN
sur uneNOT NULL
colonne ou leNOT EXISTS
contre une colonne nullable ou non nullable, il donne le plan suivant.Lorsque la colonne devient
NULL
-able, leNOT IN
plan ressemble maintenant àIl ajoute un opérateur de jointure interne supplémentaire au plan. Cet appareil est expliqué ici . Tout est là pour convertir la recherche d'index unique corrélée précédente
Sales.SalesOrderDetail.ProductID = <correlated_product_id>
en deux recherches par ligne externe. Celui supplémentaire est activéWHERE Sales.SalesOrderDetail.ProductID IS NULL
.Comme c'est sous une anti semi jointure si celle-ci retourne des lignes, la deuxième recherche ne se produira pas. Cependant, s'il
Sales.SalesOrderDetail
ne contient aucunNULL
ProductID
s, il doublera le nombre d'opérations de recherche requises.la source
NOT EXISTS
fonctionne de la façon dont je m'attendsNOT IN
à fonctionner (ce qui n'est pas le cas).Sachez également que NOT IN n'est pas équivalent à NOT EXISTS lorsqu'il s'agit de null.
Ce post l'explique très bien
http://sqlinthewild.co.za/index.php/2010/02/18/not-exists-vs-not-in/
la source
Si le planificateur d'exécution dit que ce sont les mêmes, ce sont les mêmes. Utilisez celui qui rendra votre intention plus évidente - dans ce cas, le second.
la source
En fait, je pense que ce serait le plus rapide:
la source
J'ai une table qui a environ 120 000 enregistrements et je dois sélectionner uniquement ceux qui n'existent pas (correspondant à une colonne varchar) dans quatre autres tables avec un nombre de lignes d'environ 1500, 4000, 40000, 200. Toutes les tables impliquées ont un index unique sur la
Varchar
colonne concernée .NOT IN
a pris environ 10 minutes, aNOT EXISTS
pris 4 secondes.J'ai une requête récursive qui pourrait avoir une section non accordée qui aurait pu contribuer aux 10 minutes, mais l'autre option prenant 4 secondes explique, au moins pour moi, c'est
NOT EXISTS
bien mieux ou du moins celaIN
etEXISTS
ne sont pas exactement les mêmes et valent toujours un vérifiez avant d'aller de l'avant avec le code.la source
Dans votre exemple spécifique, ils sont identiques, car l'optimiseur a compris que ce que vous essayez de faire est le même dans les deux exemples. Mais il est possible que dans des exemples non triviaux l'optimiseur ne le fasse pas, et dans ce cas, il y a des raisons de préférer l'un à l'autre à l'occasion.
NOT IN
devrait être préféré si vous testez plusieurs lignes dans votre sélection externe. La sous-requête à l'intérieur de l'NOT IN
instruction peut être évaluée au début de l'exécution, et la table temporaire peut être vérifiée par rapport à chaque valeur dans la sélection externe, plutôt que de réexécuter la sous-sélection à chaque fois comme cela serait requis avec l'NOT EXISTS
instruction.Si la sous-requête doit être corrélée avec la sélection externe, cela
NOT EXISTS
peut être préférable, car l'optimiseur peut découvrir une simplification qui empêche la création de tables temporaires pour exécuter la même fonction.la source
J'utilisais
et a constaté qu'il donnait de mauvais résultats (par mauvais je veux dire aucun résultat). Comme il y avait un NULL dans TABLE2.Col1.
Lors de la modification de la requête en
m'a donné les bons résultats.
Depuis lors, j'ai commencé à utiliser NOT EXISTS partout.
la source
Ils sont très similaires mais pas vraiment les mêmes.
En termes d'efficacité, j'ai trouvé que la jointure gauche est une déclaration nulle plus efficace (quand une abondance de lignes doit être sélectionnée, c'est-à-dire)
la source
Si l'optimiseur dit qu'ils sont identiques, alors considérez le facteur humain. Je préfère voir PAS EXISTE :)
la source
Modèle de table de base de données
Supposons que nous avons les deux tables suivantes dans notre base de données, qui forment une relation de table un-à-plusieurs.
La
student
table est le parent et lastudent_grade
table enfant car elle possède une colonne Student_id Foreign Key faisant référence à la colonne id Primary Key dans la table student.Le
student table
contient les deux enregistrements suivants:Et, la
student_grade
table stocke les notes que les étudiants ont reçues:SQL EXISTS
Disons que nous voulons obtenir tous les élèves qui ont reçu une note de 10 en classe de mathématiques.
Si nous ne sommes intéressés que par l'identifiant de l'élève, nous pouvons exécuter une requête comme celle-ci:
Mais, l'application est intéressée à afficher le nom complet d'un
student
, pas seulement l'identifiant, nous avons donc également besoin des informations de lastudent
table.Afin de filtrer les
student
enregistrements qui ont une note de 10 en mathématiques, nous pouvons utiliser l'opérateur SQL EXISTS, comme ceci:Lors de l'exécution de la requête ci-dessus, nous pouvons voir que seule la ligne Alice est sélectionnée:
La requête externe sélectionne les
student
colonnes de lignes que nous souhaitons renvoyer au client. Cependant, la clause WHERE utilise l'opérateur EXISTS avec une sous-requête interne associée.L'opérateur EXISTS renvoie true si la sous-requête renvoie au moins un enregistrement et false si aucune ligne n'est sélectionnée. Le moteur de base de données n'a pas à exécuter entièrement la sous-requête. Si un seul enregistrement correspond, l'opérateur EXISTS renvoie true et l'autre ligne de requête associée est sélectionnée.
La sous-requête interne est corrélée car la colonne student_id de la
student_grade
table est comparée à la colonne id de la table student externe.SQL N'EXISTE PAS
Considérons que nous voulons sélectionner tous les élèves qui n'ont pas de note inférieure à 9. Pour cela, nous pouvons utiliser NOT EXISTS, ce qui annule la logique de l'opérateur EXISTS.
Par conséquent, l'opérateur NOT EXISTS renvoie true si la sous-requête sous-jacente ne renvoie aucun enregistrement. Cependant, si un seul enregistrement correspond à la sous-requête interne, l'opérateur NOT EXISTS renvoie false et l'exécution de la sous-requête peut être arrêtée.
Pour faire correspondre tous les enregistrements d'étudiant qui n'ont pas de student_grade associé avec une valeur inférieure à 9, nous pouvons exécuter la requête SQL suivante:
Lors de l'exécution de la requête ci-dessus, nous pouvons voir que seul l'enregistrement Alice est mis en correspondance:
Ainsi, l'avantage d'utiliser les opérateurs SQL EXISTS et NOT EXISTS est que l'exécution de la sous-requête interne peut être arrêtée tant qu'un enregistrement correspondant est trouvé.
la source
Ça dépend..
ne serait pas relativement lent, il n'y a pas grand-chose pour limiter la taille de ce que la requête vérifie pour voir si elles sont entrées. EXISTS serait préférable dans ce cas.
Mais, selon l'optimiseur du SGBD, cela ne pourrait pas être différent.
Comme exemple de quand EXISTS est meilleur
la source
IN
etEXISTS
obtenez le même plan dans SQL Server . La question est sur le pointNOT IN
vs deNOT EXISTS
toute façon.