Avertissement: j'ai compris le problème (je pense), mais je voulais ajouter ce problème à Stack Overflow car je ne pouvais (facilement) le trouver nulle part. De plus, quelqu'un pourrait avoir une meilleure réponse que moi.
J'ai une base de données où une table "commune" est référencée par plusieurs autres tables. Je voulais voir quels enregistrements de la table commune étaient orphelins (c'est-à-dire qu'ils n'avaient aucune référence à partir des autres tables).
J'ai exécuté cette requête:
select *
from Common
where common_id not in (select common_id from Table1)
and common_id not in (select common_id from Table2)
Je sais qu'il existe des enregistrements orphelins, mais aucun enregistrement n'a été retourné. Pourquoi pas?
(Il s'agit de SQL Server, si cela compte.)
sql
sql-server
tsql
Jeremy Stein
la source
la source
Réponses:
Mettre à jour:
Ces articles de mon blog décrivent plus en détail les différences entre les méthodes:
NOT IN
vsNOT EXISTS
vsLEFT JOIN / IS NULL
:SQL Server
NOT IN
vsNOT EXISTS
vsLEFT JOIN / IS NULL
:PostgreSQL
NOT IN
vsNOT EXISTS
vsLEFT JOIN / IS NULL
:Oracle
NOT IN
vsNOT EXISTS
vsLEFT JOIN / IS NULL
:MySQL
Il existe trois façons de faire une telle requête:
LEFT JOIN / IS NULL
:NOT EXISTS
:NOT IN
:Quand
table1.common_id
n'est pas nullable, toutes ces requêtes sont sémantiquement identiques.Quand il est nullable,
NOT IN
est différent, puisqueIN
(et, par conséquent,NOT IN
) retourneNULL
quand une valeur ne correspond à rien dans une liste contenant unNULL
.Cela peut être déroutant mais peut devenir plus évident si nous rappelons la syntaxe alternative pour ceci:
Le résultat de cette condition est un produit booléen de toutes les comparaisons dans la liste. Bien sûr, une seule
NULL
valeur donne leNULL
résultat qui rend également le résultat entierNULL
.Nous ne pouvons jamais dire avec certitude que ce
common_id
n'est égal à rien de cette liste, car au moins une des valeurs l'estNULL
.Supposons que nous ayons ces données:
LEFT JOIN / IS NULL
etNOT EXISTS
retournera3
,NOT IN
ne retournera rien (car il sera toujours évalué à l'unFALSE
ou l' autreNULL
).Dans
MySQL
, en cas de colonne non nullable,LEFT JOIN / IS NULL
etNOT IN
sont un peu plus efficaces (plusieurs pour cent) queNOT EXISTS
. Si la colonne est nullable,NOT EXISTS
c'est le plus efficace (encore une fois, pas beaucoup).Dans
Oracle
, les trois requêtes génèrent les mêmes plans (anANTI JOIN
).Dans
SQL Server
,NOT IN
/NOT EXISTS
sont plus efficaces, carLEFT JOIN / IS NULL
ne peuvent pas être optimisés à unANTI JOIN
par son optimiseur.Dans
PostgreSQL
,LEFT JOIN / IS NULL
etNOT EXISTS
sont plus efficaces queNOT IN
, sine, ils sont optimisés pour unAnti Join
, whileNOT IN
useshashed subplan
(ou même un plainsubplan
si la sous-requête est trop grande pour le hachage)la source
NOT EXISTS
valeur TRUE si la requête à l'intérieur renvoie des lignes.SELECT NULL
pourrait aussi bien êtreSELECT *
ouSELECT 1
ou quoi que ce soit d'autre, leNOT EXISTS
prédicat ne regarde pas les valeurs des lignes, les compte seulement.Si vous voulez que le monde soit un lieu booléen à deux valeurs, vous devez éviter vous-même le cas nul (troisième valeur).
N'écrivez pas de clauses IN qui autorisent les valeurs nulles du côté liste. Filtrez-les!
la source
common_id not in
, nous pouvons encore avoir unecommon_id
valeur qui estNULL
. Le problème de l'absence de résultats ne persiste-t-il donc pas?Table1 ou Table2 a des valeurs nulles pour common_id. Utilisez plutôt cette requête:
la source
la source
Juste du haut de ma tête ...
J'ai fait quelques tests et voici mes résultats avec la réponse de @ patmortech et les commentaires de @ rexem.
Si Table1 ou Table2 n'est pas indexé sur commonID, vous obtenez une analyse de table mais la requête de @ patmortech est toujours deux fois plus rapide (pour une table maître de 100K lignes).
Si aucun des deux n'est indexé sur commonID, vous obtenez deux analyses de table et la différence est négligeable.
Si les deux sont indexés sur commonID, la requête "n'existe pas" s'exécute dans 1/3 du temps.
la source
la source
Supposons ces valeurs pour common_id:
Nous voulons que la ligne en commun renvoie, car elle n'existe dans aucune des autres tables. Cependant, le nul jette une clé à molette.
Avec ces valeurs, la requête équivaut à:
Cela équivaut à:
C'est là où commence le problème. Lors de la comparaison avec un nul, la réponse est inconnue . Ainsi, la requête se réduit à
faux ou inconnu est inconnu:
true and not unkown est également inconnu:
La condition where ne renvoie pas les enregistrements dont le résultat est inconnu, nous ne récupérons donc aucun enregistrement.
Une façon de résoudre ce problème consiste à utiliser l'opérateur exist plutôt que dans. Exists ne renvoie jamais unkown car il opère sur des lignes plutôt que des colonnes. (Une ligne existe ou non; aucune de cette ambiguïté nulle au niveau de la ligne!)
la source
cela a fonctionné pour moi :)
la source
NOT IN
produira-t-il?la source
J'ai eu un exemple où je recherchais et parce qu'une table contenait la valeur comme un double, l'autre comme une chaîne, ils ne correspondraient pas (ou ne correspondraient pas sans un cast). Mais seulement PAS IN . Comme SELECT ... IN ... a fonctionné. Bizarre, mais je pensais que je partagerais au cas où quelqu'un d'autre rencontrerait cette solution simple.
la source
Veuillez suivre l'exemple ci-dessous pour comprendre le sujet ci-dessus:
Vous pouvez également visiter le lien suivant pour connaître Anti join
Mais si nous utilisons
NOT IN
dans ce cas, nous n'obtenons aucune donnée.Cela se produit lorsque (
select department_id from hr.employees
) renvoie une valeur nulle et la requête entière est évaluée comme fausse. Nous pouvons le voir si nous modifions légèrement le SQL comme ci-dessous et gérons les valeurs nulles avec la fonction NVL.Maintenant, nous obtenons des données:
Encore une fois, nous obtenons des données car nous avons traité la valeur nulle avec la fonction NVL.
la source