2009-05-13 5 views
16

I a été mise au point d'une procédure stockée l'autre jour et trouvé quelque chose logique comme ceci:"<>" vs "NOT IN"

SELECT something 
FROM someTable 
WHERE idcode <> (SELECT ids FROM tmpIdTable) 

Rien retourné. Je pensais que cela avait l'air un peu bizarre avec le "<>" alors je l'ai changé pour "NOT IN" et tout a bien fonctionné. Je me demandais pourquoi c'est? C'est un proc assez vieux et je ne suis pas vraiment sûr combien de temps le problème a été autour, mais nous avons récemment changé de SQL Server 2005 à SQL Server 2008 quand cela a été découvert. Quelle est la différence réelle entre "<>" et "NOT IN" et a le comportement changé entre Server2005 et 2008?

Répondre

18
SELECT something 
FROM someTable 
WHERE idcode NOT IN (SELECT ids FROM tmpIdTable) 

vérifie la valeur de la liste. Toutefois, le NOT IN n'est pas NULL-tolérant. Si la sous-requête renvoyait un ensemble de valeurs contenant NULL, aucun enregistrement ne serait renvoyé du tout. (En effet, le NOT IN est optimisé en interne à idcode <> 'foo' AND idcode <> 'bar' AND idcode <> NULL etc., ce qui échouera toujours car toute comparaison avec NULL génère INCONNU, empêchant ainsi l'expression entière de devenir VRAIE.)

Une variante plus agréable, tolérante à la NULL serait ceci:

SELECT something 
FROM someTable 
WHERE NOT EXISTS (SELECT ids FROM tmpIdTable WHERE ids = someTable.idcode) 

EDIT: Je suppose que d'abord ceci:

SELECT something 
FROM someTable 
WHERE idcode <> (SELECT ids FROM tmpIdTable) 

vérifieraient contre la première valeur uniquement. Il se trouve que cette hypothèse est fausse au moins pour SQL Server, où il déclenche réellement son erreur:

Msg 512, Level 16, State 1, Line 1 
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression. 
+1

'WHERE idcode NOT IN (...)' est équivalent à 'WHERE idcode <> ALL (...)' –

7

<> est une opération "singulière" NOT; NOT IN est une opération d'ensemble, il est donc logique que le premier ne fonctionnerait pas. Je n'ai aucune idée si cela peut avoir fait sous une version antérieure de SQL Server, cependant.

+0

Il n'a jamais été fait différent dans SQL Server. – Tomalak

11

essayer cela, peut courir plus vite en raison de l'utilisation des index:

SELECT something 
FROM someTable 
    LEFT OUTER JOIN tmpIdTable ON idcode=ids 
WHERE ids IS NULL 
+0

+1 bonne alternative –

+0

C'est une bonne idée! –

+0

SQL Server normalement (je n'ai pas vu un cas quand il n'a pas) produit des QEP identiques pour une jointure ou une sous-requête, et donc des performances identiques. – pipTheGeek

2

Je ne sais pas pourquoi écririez-vous quelque chose comme WHERE idcode <> (SELECT ids FROM tmpIdTable). Une instruction SELECT renverra un ensemble de tuples et votre code d'identification sera ou NE sera PAS dans cet ensemble. "WHERE idcode NOT IN (SELECT ids FROM tmpIdTable)" est le moyen de le faire.

+0

C'est pour ça que je savais que ça arrangeait, je ne savais pas exactement quelle était la logique. –

+0

L'instruction <> peut avoir un sens si la sous-requête est ordonnée d'une manière ou d'une autre, par ex. "<> la plus grande [chose] dans la liste". – Tomalak

+0

Vous avez raison, au cas où la sous-requête est ordonnée, la notation <> pourrait avoir du sens, mais je l'éviterais de toute façon, car elle est sujette à erreur - facile d'ignorer son intention.Quiconque supprime ou modifie l'ordre de la sous-requête plus tard peut être déconcerté par la raison pour laquelle la requête ne renvoie rien. –

-1

dans certaines versions de SQL ! = doit être utilisé pour une instruction logique "pas égal". Avez-vous essayé?

+2

'<>' et '! =' Sont équivalents sur SQL Server. Il n'y a pas de version qui insiste sur '! ='. Mais '<>' est la manière standard ANSI SQL de le faire, bien que personnellement j'ai tendance à utiliser! = Plus souvent pour une raison quelconque. – Tomalak

4

Ce code est valable si et seulement s'il n'y a pas de lignes ou une seule rangée retournée de tmpIdTable:

SELECT something 
FROM someTable 
WHERE idcode <> (SELECT ids FROM tmpIdTable) 

Si plusieurs lignes sont renvoyées, vous obtiendrez une erreur comme:

Msg 512 , Niveau 16, État 1, Ligne 1 La sous-requête a renvoyé plus de 1 valeur. Ceci n'est pas autorisé lorsque la sous-requête suit =,! =, <, < =,>,> = ou lorsque la sous-requête est utilisée comme expression.

C'est la même erreur que vous obtenez avec scalaire imbriqué produit de façon inattendue plusieurs lignes comme:

SELECT *, (SELECT bla FROM t1 WHERE etc.) de t2

Rien n'a changé dans ce WRT SQL Server dans une décennie, donc je suppose que les hypothèses sur la requête imbriquée dans le code original ont été brisées.

Si aucune ligne n'est renvoyée, le résultat sera vide car <> NULL n'est jamais vrai (supposons ANSI NULLs).

Ce code est valable pour un certain nombre de lignes:

SELECT something 
FROM someTable 
WHERE idcode NOT IN (SELECT ids FROM tmpIdTable) 

Cependant, il peut encore y avoir des problèmes avec NULL.

+0

J'ai eu la même conversation ce matin avec un collègue. C'est similaire, mais pas tout à fait la réponse. –

+0

Conceptuellement, au moins, c'est même faux si la sous-sélection renvoie zéro ou une ligne. Parce que vous demandez si un scalaire, idcode, est égal à une liste à zéro ou à un élément. Le scalaire n'est jamais égal à la liste, même s'il est égal au seul élément d'une liste à un élément. –

+0

En effet, je n'utilise jamais la construction et je pense généralement que c'est une odeur de code. –

2

Si la sous-requête SELECT renvoie zéro ligne, c'est une valeur NULL. Quand NULL est comparé à n'importe quoi, le résultat est toujours UNKNOWN, et jamais TRUE. Confusingly assez, NON INCONNU est égal à INCONNU. J'évite trois fois la logique de valeur (TRUE, FALSE, UNKNOWN) autant que possible. Ce n'est pas si difficile à éviter une fois que vous avez compris.

Si la sous-requête SELECT renvoie exactement une valeur, la comparaison pour l'inégalité doit renvoyer le résultat attendu.

Si la sous-requête SELECT renvoie plus d'une valeur, vous devriez obtenir une erreur. En général, NOT IN renverra le résultat que vous attendez lorsque vous testez la non-appartenance à un ensemble.

Cette réponse chevauche d'autres réponses, mais elle est libellée un peu différemment.

Edité pour ajouter plus de détails sur NOT IN:

Je fait quelques recherches sur les PAS dans Oracle, et j'appris quelque chose que je ne savais pas il y a une demi-heure. NOT IN est NULL sensible. En particulier,

X NOT IN (SELECT ...) 

est-ce pas la même chose que

NOT (X IN SELECT ...)) 

je dois modifier ma réponse plus tôt!

+0

+1 pour indiquer la partie TRUE/FALSE/UNKNOWN. Merci. – Tomalak

Questions connexes