Comment sélectionner tous les enregistrements d'une table qui n'existent pas dans une autre table?

470

table1 (id, nom)
table2 (id, nom)

Requete:

SELECT name   
FROM table2  
-- that are not in table1 already
z-boss
la source

Réponses:

844
SELECT t1.name
FROM table1 t1
LEFT JOIN table2 t2 ON t2.name = t1.name
WHERE t2.name IS NULL

Q : Que se passe-t-il ici?

R : Conceptuellement, nous sélectionnons toutes les lignes table1et pour chaque ligne, nous essayons de trouver une ligne table2avec la même valeur pour la namecolonne. S'il n'y a pas une telle ligne, nous laissons simplement la table2partie de notre résultat vide pour cette ligne. Ensuite, nous contraignons notre sélection en ne sélectionnant que les lignes du résultat où la ligne correspondante n'existe pas. Enfin, nous ignorons tous les champs de notre résultat, à l'exception de la namecolonne (celle dont nous sommes sûrs qu'elle existe table1).

Bien que ce ne soit pas la méthode la plus performante possible dans tous les cas, elle devrait fonctionner dans pratiquement tous les moteurs de base de données qui tentent d'implémenter ANSI 92 SQL

Kris
la source
16
@ z-boss: c'est aussi le moins performant sur SQL Server: expliquerextended.com/2009/09/15/…
OMG Ponies
7
@BunkerBoy: Une jointure gauche permet aux lignes de droite de ne pas exister sans que cela affecte l'inclusion de lignes à gauche. Une jointure interne nécessite la présence de lignes à gauche et à droite. Ce que je fais ici, c'est appliquer une logique pour obtenir essentiellement la sélection inverse d'une jointure interne.
Kris
2
omg cela a aidé à visualiser très facilement, d'autres l'avaient dit comme 5 façons différentes mais cela a aidé. simple: vous obtenez d'abord la jointure gauche, tout dans A et tout dans B qui correspond à A. Mais comme cela se produit dans les champs de jointure gauche qui ne se joignent pas, ils sont simplement nuls. Alors vous dites, ok je veux seulement que ce soit nul. De cette façon, vous avez maintenant toutes les lignes en A qui n'ont pas de match En B
Muhammad Umer
7
Il convient de noter que cette solution (acceptée et votée) est la seule, je pense, qui pourrait être éditée pour un scénario où plus d'un champ entre en jeu. Plus précisément, je renvoie le champ, le champ 2, le champ 3 du tableau un où la combinaison du champ et du champ2 n'est pas dans le deuxième tableau. Outre la modification de la jointure dans cette réponse, je ne vois pas de moyen de le faire avec certaines des autres "réponses plus efficaces"
évoquées
1
Assurez-vous simplement d'utiliser "WHERE t2.name IS NULL" et non "AND t2.name IS NULL" car "et" ne donneront pas de résultats corrects. Je ne comprends pas vraiment pourquoi mais c'est un fait, je l'ai testé.
user890332
236

Vous pouvez soit faire

SELECT name
FROM table2
WHERE name NOT IN
    (SELECT name 
     FROM table1)

ou

SELECT name 
FROM table2 
WHERE NOT EXISTS 
    (SELECT * 
     FROM table1 
     WHERE table1.name = table2.name)

Voir cette question pour 3 techniques pour accomplir cela

froadie
la source
38
C'est incroyablement lent avec de grandes quantités de données.
Lightbulb1
Oui, en effet, c'est très lent
sirus
Ne devrait-il pas être "de table1" dans la sous-requête de la requête n'existe pas.
Hound
Très confus de la façon dont cela a eu autant de votes positifs. Je trouve qu'il est très difficile de penser à une raison pour utiliser cela, alors qu'il existe une approche à ce problème qui est incroyablement plus rapide avec à peu près le même nombre de frappes.
searchengine27
Celui-ci a fonctionné pour moi .. Merci
Thameem
81

Je n'ai pas assez de points de rep pour voter sur la 2e réponse. Mais je ne suis pas d'accord avec les commentaires sur la première réponse. La deuxième réponse:

SELECT name
FROM table2
WHERE name NOT IN
    (SELECT name 
     FROM table1)

Est FAR plus efficace dans la pratique. Je ne sais pas pourquoi, mais je le lance contre 800k + records et la différence est énorme avec l'avantage donné à la 2ème réponse affichée ci-dessus. Juste mon 0,02 $

Tan Rezaei
la source
31
Dans la requête NOT IN, la sous-requête n'est effectuée qu'une seule fois, dans la requête EXISTS, la sous-requête est effectuée pour chaque ligne
Carrick
2
vous êtes génial :) de cette façon, je convertis ma requête de 25 secondes en utilisant la jointure gauche à seulement 0,1 seconde
Bassem Shahin
3
les réponses ne sont pas dans un ordre spécifique, donc la deuxième réponse ne signifie pas ce que vous pensiez que cela signifiait.
38

Il s'agit d'une pure théorie des ensembles que vous pouvez réaliser avec l' minusopération.

select id, name from table1
minus
select id, name from table2
Hiver
la source
Pensez-vous que c'est beaucoup plus efficace que la jointure gauche?
euh
Ça devrait être. La commande moins est conçue pour cette situation exacte. Bien sûr, la seule façon de juger d'un ensemble de données particulier est de l'essayer dans les deux sens et de voir lequel s'exécute plus rapidement.
Hiver
9
Dans T-SQL, l'opérateur d'ensemble est "sauf". C'est très pratique pour moi et n'a provoqué aucun ralentissement.
2
Dans SQLite, l'opérateur "moins" est également "excepté".
lifjoy
MySQL ne prend pas en charge l'opérateur MINUS.
Muhammad Azeem
16

Attention aux pièges. Si le champ Nameen Table1contient des valeurs NULL vous de surprises. Mieux vaut:

SELECT name
FROM table2
WHERE name NOT IN
    (SELECT ISNULL(name ,'')
     FROM table1)
user4872693
la source
1
COALESCE> ISNULL (ISNULL est un ajout T-SQL inutile au langage qui ne fait rien de nouveau ou de meilleur que COALESCE)
Kris
14

Voici ce qui a fonctionné le mieux pour moi.

SELECT *
FROM @T1
EXCEPT
SELECT a.*
FROM @T1 a
JOIN @T2 b ON a.ID = b.ID

C'était plus de deux fois plus rapide que toute autre méthode que j'ai essayée.

Bob
la source
Merci, cela fonctionne bien avec une grande quantité de données aussi! Mais je m'interroge simplement sur le terme «Sauf».
PatsonLeaner
7

Ce travail est net pour moi

SELECT * 
FROM [dbo].[table1] t1
LEFT JOIN [dbo].[table2] t2 ON t1.[t1_ID] = t2.[t2_ID]
WHERE t2.[t2_ID] IS NULL
David Fawzy
la source
1

Voir requête:

SELECT * FROM Table1 WHERE
id NOT IN (SELECT 
        e.id
    FROM
        Table1 e
            INNER JOIN
        Table2 s ON e.id = s.id);

Conceptuellement, ce serait: récupérer les enregistrements correspondants dans la sous-requête, puis dans la requête principale, récupérer les enregistrements qui ne sont pas dans la sous-requête.

jawahar
la source
0

Je vais republier (car je ne suis pas encore assez cool pour commenter) dans la bonne réponse ... au cas où quelqu'un d'autre aurait pensé qu'il fallait mieux l'expliquer.

SELECT temp_table_1.name
FROM original_table_1 temp_table_1
LEFT JOIN original_table_2 temp_table_2 ON temp_table_2.name = temp_table_1.name
WHERE temp_table_2.name IS NULL

Et j'ai vu la syntaxe dans FROM nécessitant des virgules entre les noms de table dans mySQL mais dans sqlLite, il semblait préférer l'espace.

En fin de compte, lorsque vous utilisez des noms de variable incorrects, cela laisse des questions. Mes variables devraient avoir plus de sens. Et quelqu'un devrait expliquer pourquoi nous avons besoin d'une virgule ou pas de virgule.

Adrian Roth
la source
0

Si vous souhaitez sélectionner un utilisateur spécifique

SELECT tent_nmr FROM Statio_Tentative_Mstr
WHERE tent_npk = '90009'
AND
tent_nmr NOT IN (SELECT permintaan_tent FROM Statio_Permintaan_Mstr)

La tent_npkest une clé primaire d'un utilisateur

Fragmantedbin
la source