Question

Laquelle de ces requêtes est la plus rapide?

PAS EXISTE:

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 de la requête 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.

[Modifier]

Je viens de trouver cet article utile: http://weblogs.sqlteam.com/mladenp/archive/2007 /05/18/60210.aspx

Je pense que je vais rester avec NOT EXISTS.

Était-ce utile?

La solution

Je sélectionne toujours par défaut NOT EXISTS.

Les plans d'exécution sont peut-être les mêmes pour le moment, mais si l'une des colonnes est modifiée à l'avenir pour permettre à NULL la version NOT IN devra faire plus de travail (même si aucun Products.ProductID s n'est réellement présent dans les données) et la sémantique de [Order Details].ProductID si [Order Details] s sont présents ne sont probablement pas ceux que vous souhaitez de toute façon.

Lorsque ProductId ou Products.ProductId n'autorisent pas AdventureWorks2008, le NOT NULL sera traité de manière identique à la requête suivante.

SELECT ProductID,
       ProductName
FROM   Products p
WHERE  NOT EXISTS (SELECT *
                   FROM   [Order Details] od
                   WHERE  p.ProductId = od.ProductId) 

Le plan exact peut varier mais pour les exemples de données, je reçois le texte suivant.

Ni NULL

Une idée fausse assez courante semble être que les sous-requêtes corrélées sont toujours & "mauvaises &"; par rapport aux jointures. Cela peut certainement arriver lorsqu'ils imposent un plan de boucles imbriquées (sous-requête évaluée ligne par ligne), mais ce plan comprend un opérateur logique anti-jointure. Les jointures anti-semi ne sont pas limitées aux boucles imbriquées, mais peuvent également utiliser des jointures de hachage ou de fusion (comme dans cet exemple).

/*Not valid syntax but better reflects the plan*/ 
SELECT p.ProductID,
       p.ProductName
FROM   Products p
       LEFT ANTI SEMI JOIN [Order Details] od
         ON p.ProductId = od.ProductId 

Si Sales.SalesOrderDetail.ProductID = <correlated_product_id> est WHERE Sales.SalesOrderDetail.ProductID IS NULL - la requête devient alors

SELECT ProductID,
       ProductName
FROM   Products p
WHERE  NOT EXISTS (SELECT *
                   FROM   [Order Details] od
                   WHERE  p.ProductId = od.ProductId)
       AND NOT EXISTS (SELECT *
                       FROM   [Order Details]
                       WHERE  ProductId IS NULL) 

La raison en est que la sémantique correcte si Sales.SalesOrderDetail contient du ProductID <=> s est de ne renvoyer aucun résultat. Reportez-vous au spool supplémentaire anti-jointure et au nombre de lignes pour vérifier s'il est ajouté au plan.

Un NULL

Si <=> est également modifié pour devenir <=> - la requête devient alors

SELECT ProductID,
       ProductName
FROM   Products p
WHERE  NOT EXISTS (SELECT *
                   FROM   [Order Details] od
                   WHERE  p.ProductId = od.ProductId)
       AND NOT EXISTS (SELECT *
                       FROM   [Order Details]
                       WHERE  ProductId IS NULL)
       AND NOT EXISTS (SELECT *
                       FROM   (SELECT TOP 1 *
                               FROM   [Order Details]) S
                       WHERE  p.ProductID IS NULL) 

Cela s'explique par le fait qu'un <=> <=> ne doit pas être renvoyé dans les résultats sauf si la <=> sous-requête ne renvoie aucun résultat (c.-à-d. le < => la table est vide). Dans ce cas, cela devrait. Dans le plan de mes exemples de données, ceci est implémenté en ajoutant un autre anti-jointure comme indiqué ci-dessous.

Les deux NULL

L'effet de cela est indiqué dans l'article 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 <=> 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 réalité il n'y a pas de <=> lignes dans les données, le reste du plan d'exécution risque de se détériorer de façon catastrophique. S'il s'agit simplement d'une partie d'une requête plus importante, avec des boucles imbriquées inappropriées entraînant l'exécution répétée d'un sous-arbre coûteux, par exemple .

Cependant, ce n'est pas le seul plan d'exécution possible pour une <=> sur une colonne <=> - capable. Cet article en présente un autre. pour une requête sur la <=> base de données.

Pour le <=> sur une colonne <=> ou le <=> par rapport à une colonne nullable ou non nullable, cela donne le plan suivant.

Non existant

Lorsque la colonne devient <=>, le plan <=> ressemble maintenant à

.

pas à - Null

Il ajoute un opérateur de jointure interne supplémentaire au plan. Cet appareil est expliqué ici . Tout est fait pour convertir la recherche d’index unique et corrélée précédente sur <=> en deux recherches par ligne externe. Le supplément est sur <=>.

Comme il s’agit d’un joint anti-semi-joint si celui-ci renvoie des lignes, la seconde recherche n’aura pas lieu Toutefois, si <=> ne contient pas de <=> <=> s, le nombre d'opérations de recherche requises sera doublé.

Autres conseils

Sachez également que NOT IN n'est pas équivalent à NOT EXISTS en ce qui concerne la valeur null.

Ce message explique très bien

http://sqlinthewild.co.za/ index.php / 2010/02/18 / pas-existe-vs-pas-dans /

  

Lorsque la sous-requête renvoie même une valeur null, NOT IN ne correspond à aucune   rangées.

     

La raison de ceci peut être trouvée en regardant les détails de ce que le   NE PAS UTILISER signifie en réalité.

     

Laissez & # 8217; s dire, à des fins d'illustration, qu'il y a 4 lignes dans le   table appelée t, il y a & # 8217; s une colonne appelée ID avec les valeurs 1..4

WHERE SomeValue NOT IN (SELECT AVal FROM t)
     

est équivalent à

WHERE SomeValue != (SELECT AVal FROM t WHERE ID=1)
AND SomeValue != (SELECT AVal FROM t WHERE ID=2)
AND SomeValue != (SELECT AVal FROM t WHERE ID=3)
AND SomeValue != (SELECT AVal FROM t WHERE ID=4)
     

Laissons & # 8217; s encore dire qu'AVal est NULL où ID = 4. D'où ça! =   La comparaison renvoie INCONNU. La table de vérité logique pour les états AND   que INCONNU et VRAI est INCONNU, INCONNU et FAUX est FAUX. Il y a   aucune valeur pouvant être AND & # 8217; d avec UNKNOWN pour produire le résultat TRUE

     

Par conséquent, si une ligne de cette sous-requête renvoie NULL, l'intégralité de NOT IN   l'opérateur évaluera soit FALSE soit NULL et aucun enregistrement ne sera   retourné

Si le planificateur d'exécution dit qu'ils sont identiques, ils sont identiques. Utilisez celui qui rendra votre intention plus évidente - dans ce cas, le second.

En fait, je pense que ce serait le plus rapide:

SELECT ProductID, ProductName 
    FROM Northwind..Products p  
          outer join Northwind..[Order Details] od on p.ProductId = od.ProductId)
WHERE od.ProductId is null

J'ai une table qui contient environ 120 000 enregistrements et je n'ai besoin de sélectionner que 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 concernées avoir un index unique sur la colonne Varchar concernée.

NOT IN a pris environ 10 minutes, NOT EXISTS 4 secondes.

J'ai une requête récursive qui pourrait avoir une section non syntonisée qui aurait pu contribuer aux 10 minutes, mais l'autre option prenant 4 secondes m'explique, au moins pour moi que IN est bien meilleur ou du moins que EXISTS et <=> ne sont pas exactement les mêmes et méritent toujours une vérification avant d'aller de l'avant avec le code.

Dans votre exemple spécifique, ils sont identiques, car l'optimiseur a déterminé que ce que vous essayez de faire est identique dans les deux exemples. Mais il est possible que l'optimiseur ne le fasse pas dans des exemples non triviaux, et dans ce cas, il y a des raisons de préférer l'une à 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'instruction NOT EXISTS peut être évaluée au début de l'exécution et la table temporaire peut être vérifiée par rapport à chaque valeur de la sélection externe, plutôt que de réexécuter la sous-sélection à chaque fois comme requis avec l'option <= > déclaration.

Si la sous-requête doit être corrélée à la sélection externe, alors <=> peut être préférable, dans la mesure où l'optimiseur peut détecter une simplification empêchant la création de tables temporaires pour exécuter la même fonction.

J'utilisais

SELECT * from TABLE1 WHERE Col1 NOT IN (SELECT Col1 FROM TABLE2)

et a constaté que cela donnait des résultats erronés (Par faux, je veux dire pas de résultats). Comme il y avait un NULL dans TABLE2.Col1.

Lors du changement de requête en

SELECT * from TABLE1 T1 WHERE NOT EXISTS (SELECT Col1 FROM TABLE2 T2 WHERE T1.Col1 = T2.Col2)

m'a donné les bons résultats.

Depuis lors, j'ai commencé à utiliser NOT EXISTS partout.

Ils sont très similaires mais pas vraiment les mêmes.

En termes d'efficacité, j'ai constaté que l'instruction la jointure gauche est nulle était plus efficace (lorsqu'une abondance de lignes devait être sélectionnée)

Si l'optimiseur dit qu'ils sont identiques, tenez compte du facteur humain. Je préfère voir NOT EXISTS:)

Cela dépend ..

SELECT x.col
FROM big_table x
WHERE x.key IN( SELECT key FROM really_big_table );

ne serait pas relativement lent, il n’ya pas beaucoup à limiter la taille de ce que la requête vérifie pour voir si la clé est dedans. EXISTS serait préférable dans ce cas.

Mais, selon l'optimiseur du SGBD, cela pourrait ne pas être différent.

Par exemple, quand EXISTS vaut mieux

SELECT x.col
FROM big_table x
WHERE EXISTS( SELECT key FROM really_big_table WHERE key = x.key);
  AND id = very_limiting_criteria
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top