SQL “select lorsqu'elle n'est pas sous-requête” ne retourne aucun résultat
Avertissement: j'ai compris le problème (je pense), mais je voulais ajouter ce sujet pour Stack Overflow car je ne pouvais pas (facilement) le trouver n'importe où. Aussi, quelqu'un pourrait avoir une meilleure réponse que moi.
J'ai une base de données où une table "Communes" est référencé par plusieurs autres tables. Je voulais voir ce que les enregistrements dans la table Commune étaient des orphelins (c'est à dire, n'avait pas de références de toutes les autres tables).
J'ai couru 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 pas de dossiers ont été retournés. Pourquoi pas?
(C'est SQL Server, si il le faut.)
- Cette stackoverflow.com/a/129152/1667619 les réponses à la question POURQUOI assez bien.
Vous devez vous connecter pour publier un commentaire.
Mise à jour:
Ces articles dans mon blog décrire les différences entre les méthodes en plus de détail:
PAS
au lieu den'EXISTE PAS
au lieu deLEFT JOIN /NULL
:SQL Server
PAS
au lieu den'EXISTE PAS
au lieu deLEFT JOIN /NULL
:PostgreSQL
PAS
au lieu den'EXISTE PAS
au lieu deLEFT JOIN /NULL
:Oracle
PAS
au lieu den'EXISTE PAS
au lieu deLEFT JOIN /NULL
:MySQL
Il y a 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érente, puisqueIN
(et, par conséquent,NOT IN
) retourNULL
lorsqu'une valeur ne correspond pas à quoi que ce soit dans une liste contenant unNULL
.Cela peut être déroutant, mais peut devenir plus évident si nous nous rappelons la syntaxe alternative à cela:
Le résultat de cette condition est un booléen produit de toutes les comparaisons à l'intérieur de la liste. Bien sûr, un seul
NULL
valeur les rendements de laNULL
résultat qui rend la totalité du résultatNULL
trop.Nous n'avons jamais ne peut pas dire certainement que
common_id
n'est pas égal à quoi que ce soit de cette liste, depuis au moins l'une des valeurs estNULL
.Supposons que nous disposons de ces données:
LEFT JOIN /IS NULL
etNOT EXISTS
sera de retour3
,NOT IN
sera de retour rien (car il sera toujours correspondre à laFALSE
ouNULL
).Dans
MySQL
, en cas de non-colonne nullable,LEFT JOIN /IS NULL
etNOT IN
sont un peu (quelques pour cent) plus efficace queNOT EXISTS
. Si la colonne est nullable,NOT EXISTS
est le plus efficace (encore une fois, pas beaucoup).Dans
Oracle
, tous les trois requêtes de rendement mêmes plans (unANTI JOIN
).Dans
SQL Server
,NOT IN
/NOT EXISTS
sont plus efficaces, carLEFT JOIN /IS NULL
ne peut pas être optimisé pour uneANTI JOIN
par son optimiseur.Dans
PostgreSQL
,LEFT JOIN /IS NULL
etNOT EXISTS
sont plus efficaces que lesNOT IN
, sinus, ils sont optimisés pour uneAnti Join
, tandis queNOT IN
utilisehashed subplan
(ou même un simplesubplan
si la sous-requête est trop grande pour le hachage)NOT EXISTS
partie faire dans la deuxième solution? A quoi sert laSELECT NULL
partie servir ?SELECT NULL
pourrait ainsi êtreSELECT *
ouSELECT 1
ou quoi que ce soit d'autre, leNOT EXISTS
prédicat ne regarde pas les lignes de valeurs, ne compte qu'eux.Si vous voulez que le monde d'être une valeur booléenne place, vous devez empêcher les nul (le tiers de la valeur) cas vous-même.
Ne pas écrire DANS les clauses qui autorise les valeurs null dans la liste de côté. Filtre à!
common_id not in
, nous pouvons encore avoircommon_id
valeurNULL
. N'est pas le problème de n'avoir aucun résultats persistent ?Table1 ou Table2 a des valeurs null pour common_id. Utilisez cette requête à la place:
Juste à côté de ma tête...
J'ai couru quelques tests et ici mes résultats.w.r.t. @patmortech la réponse de @rexem commentaires.
Si Table1 ou Table2 n'est pas indexée sur commonID, vous obtenez une analyse de la table, mais @patmortech de la requête est encore deux fois plus rapide (pour un 100K ligne de la table de maître).
Si ni sont indexés sur commonID, vous obtenez deux analyses de la table et la différence est négligeable.
Si les deux sont indexés sur commonID, le "n'existe pas" requête s'exécute dans 1/3 du temps.
Supposons que ces valeurs pour common_id:
Nous voulons la ligne dans la Commune de revenir, car il n'existe pas dans les autres tables. Toutefois, la valeur null jette dans un monkey wrench.
Avec ces valeurs, la requête est équivalente à:
Qui est équivalent à:
C'est là que le problème commence. Lors de la comparaison avec une valeur null, la réponse est inconnue. Ainsi la requête réduit à
faux ou inconnu inconnu:
vrai et pas inconnu est également inconnu:
Où la condition de ne pas renvoyer des enregistrements où le résultat est inconnu, nous obtenons donc pas de documents.
Une façon de traiter cette question est d'utiliser le existe opérateur plutôt que dans. Existe ne retourne jamais inconnu parce qu'il fonctionne sur des lignes plutôt que des colonnes. (Une ligne existe ou qu'il n'en est rien; rien de tout cela nulle ambiguïté au niveau de la ligne!)
cela a fonctionné pour moi 🙂
NOT IN
y produire ?