171 votes

Requête SQL pour trouver un enregistrement dont l'ID ne se trouve pas dans une autre table

J'ai deux tables avec des clés primaires liées dans la base de données et je souhaite trouver un ensemble disjoint entre elles. Par exemple,

  • Table1 a des colonnes ( ID, Name ) et des données échantillons : (1 ,John), (2, Peter), (3, Mary)
  • Table2 a des colonnes ( ID, Address ) et des données échantillons : (1, address2), (2, address2)

Alors comment puis-je créer une requête SQL afin de récupérer la ligne avec l'ID à partir de table1 qui n'est pas dans table2 . Dans ce cas, (3, Mary) devrait être renvoyé ?

PS : L'ID est la clé primaire de ces deux tables.

3 votes

Un conseil pour les questions futures : définissez toujours le système de base de données (et la version de cette base) que vous utilisez. SQL est juste le Langage d'interrogation structuré utilisé par la plupart des systèmes de bases de données - cela ne nous aide pas beaucoup ... souvent, les bases de données ont des extensions et des fonctionnalités qui vont bien au-delà de la norme ANSI/ISO SQL et qui permettent de résoudre facilement le problème - mais pour cela, vous devez nous dire quelle base de données vous utilisez.

7 votes

@marc_s : Et s'ils recherchent une solution agnostique au niveau du langage, parce qu'ils doivent prendre en charge plusieurs systèmes de base de données sous-jacents, ou que l'implémentation de la base de données est abstraite ?

0 votes

Bonjour @marc_s, j'utilise PostgreSQL dans ce cas. Merci de me le rappeler.

266voto

Prince Jea Points 3341

Essayez ceci

SELECT ID, Name 
FROM   Table1 
WHERE  ID NOT IN (SELECT ID FROM Table2)

8 votes

@PrinceJea En fait, cela dépend. Voir ici pour une clarification

0 votes

Quand j'ai 20 données, cela fonctionne, mais quand j'ai 20000 données, cela ne fonctionne pas, je suis confus maintenant.

1 votes

Je ne sais pas pourquoi mais ça ne fonctionne pas. J'ai environ 10000 lignes dans la table. Dans mon cas, la solution de @JohnWoo a bien fonctionné.

138voto

John Woo Points 132738

Utilisez LEFT JOIN

SELECT  a.*
FROM    table1 a
            LEFT JOIN table2 b
                on a.ID = b.ID
WHERE   b.id IS NULL

6 votes

Je pense que c'est l'approche la plus rapide pour une très grande base de données.

1 votes

J'ai environ 500 000 lignes dans mes tables et cela a fonctionné instantanément. Avant de trouver ceci, j'ai naïvement essayé la solution acceptée et je n'avais toujours pas fini de fonctionner après 10 minutes.

42voto

Leo Holanda Points 755

Il y a essentiellement 3 approches pour cela : not exists , not in y left join / is null .

JOINT GAUCHE avec IS NULL

SELECT  l.*
FROM    t_left l
LEFT JOIN
        t_right r
ON      r.value = l.value
WHERE   r.value IS NULL

PAS DANS

SELECT  l.*
FROM    t_left l
WHERE   l.value NOT IN
        (
        SELECT  value
        FROM    t_right r
        )

N'EXISTE PAS

SELECT  l.*
FROM    t_left l
WHERE   NOT EXISTS
        (
        SELECT  NULL
        FROM    t_right r
        WHERE   r.value = l.value
        )

Laquelle est la meilleure ? Pour répondre à cette question, il serait peut-être préférable de s'adresser aux principaux fournisseurs de SGBDR. D'une manière générale, il faut éviter d'utiliser select ... where ... in (select...) lorsque l'ordre de grandeur du nombre d'enregistrements dans la sous-requête est inconnu. Certains fournisseurs peuvent limiter la taille. Oracle, par exemple, a une limite de 1 000 . La meilleure chose à faire est d'essayer les trois et de montrer le plan d'exécution.

Spécifiquement sous PostgreSQL, le plan d'exécution de NOT EXISTS y LEFT JOIN / IS NULL sont les mêmes. Je préfère personnellement le NOT EXISTS option car elle montre mieux l'intention. Après tout, la sémantique est que vous voulez trouver des enregistrements dans A que son pk n'existent pas en B .

Vieux mais toujours doré, spécifique à PostgreSQL cependant : https://explainextended.com/2009/09/16/not-in-vs-not-exists-vs-left-join-is-null-postgresql/

0 votes

3 votes

J'ai attaché ces trois-là dans ma procédure stockée. La première suggestion (LEFT JOIN avec IS NULL) était de loin la plus rapide.

11voto

polvoazul Points 104

Alternative rapide

J'ai effectué quelques tests (sur postgres 9.5) en utilisant deux tables avec ~2M de lignes chacune. La requête ci-dessous a donné des résultats au moins 5* meilleurs que les autres requêtes proposées :

-- Count
SELECT count(*) FROM (
    (SELECT id FROM table1) EXCEPT (SELECT id FROM table2)
) t1_not_in_t2;

-- Get full row
SELECT table1.* FROM (
    (SELECT id FROM table1) EXCEPT (SELECT id FROM table2)
) t1_not_in_t2 JOIN table1 ON t1_not_in_t2.id=table1.id;

2 votes

Ce n'est pas plus rapide que la solution de @Jhon Woo. J'utilise Postgres 9.6 et avec la solution de Jhon, le temps d'exécution est d'environ 60 ms. J'ai utilisé cette solution après 120 secondes, sans résultat.

5voto

CaseyR Points 320

En gardant à l'esprit les points soulevés dans le commentaire/lien de @John Woo ci-dessus, voici comment je m'y prendrais en général :

SELECT t1.ID, t1.Name 
FROM   Table1 t1
WHERE  NOT EXISTS (
    SELECT TOP 1 NULL
    FROM Table2 t2
    WHERE t1.ID = t2.ID
)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X