362 votes

Trouver des enregistrements d'une table qui n'existent pas dans une autre.

J'ai les deux tables suivantes (en MySQL) :

Phone_book
+----+------+--------------+
| id | name | phone_number |
+----+------+--------------+
| 1  | John | 111111111111 |
+----+------+--------------+
| 2  | Jane | 222222222222 |
+----+------+--------------+

Call
+----+------+--------------+
| id | date | phone_number |
+----+------+--------------+
| 1  | 0945 | 111111111111 |
+----+------+--------------+
| 2  | 0950 | 222222222222 |
+----+------+--------------+
| 3  | 1045 | 333333333333 |
+----+------+--------------+

Comment puis-je savoir quels appels ont été passés par des personnes dont phone_number n'est pas dans le Phone_book ? Le résultat souhaité serait :

Call
+----+------+--------------+
| id | date | phone_number |
+----+------+--------------+
| 3  | 1045 | 333333333333 |
+----+------+--------------+

525voto

Alnitak Points 143355

Il existe plusieurs façons de procéder, avec une efficacité variable, en fonction de la qualité de votre optimiseur de requêtes et de la taille relative de vos deux tables :

Il s'agit de la déclaration la plus courte, et peut être la plus rapide si votre annuaire téléphonique est très court :

SELECT  *
FROM    Call
WHERE   phone_number NOT IN (SELECT phone_number FROM Phone_book)

alternativement (grâce à Alterlife )

SELECT *
FROM   Call
WHERE  NOT EXISTS
  (SELECT *
   FROM   Phone_book
   WHERE  Phone_book.phone_number = Call.phone_number)

ou (grâce à WOPR)

SELECT * 
FROM   Call
LEFT OUTER JOIN Phone_Book
  ON (Call.phone_number = Phone_book.phone_number)
  WHERE Phone_book.phone_number IS NULL

(en ignorant que, comme d'autres l'ont dit, il est normalement préférable de sélectionner uniquement les colonnes que vous voulez, et non ' * ')

4 votes

Évitez IN, utilisez EXISTS - l'indice est dans le titre de la question

33 votes

La jointure externe gauche est probablement la plus rapide dans le cas général, car elle évite l'exécution répétée de la sous-requête.

0 votes

Not to be picky, but the subquery on my suggestion returns <code>select 'x'</code> and not <code>select *</code>

107voto

WOPR Points 2374
SELECT Call.ID, Call.date, Call.phone_number 
FROM Call 
LEFT OUTER JOIN Phone_Book 
  ON (Call.phone_number=Phone_book.phone_number) 
  WHERE Phone_book.phone_number IS NULL

Supprime la sous-requête, permettant ainsi à l'optimiseur de requêtes d'opérer sa magie.

Évitez également "SELECT *" car cela peut casser votre code si quelqu'un modifie les tables ou les vues sous-jacentes (et c'est inefficace).

12 votes

C'est généralement la méthode la plus efficace car elle n'effectue pas de passages multiples sur la deuxième table ... J'espère que certaines personnes lisent les condamnations.

4 votes

J'espère plutôt que les gens se profilent : à moins d'être un gourou des performances SQL, il est assez difficile de dire à l'avance ce qui sera le plus rapide (et cela dépend du moteur du SGBD que vous utilisez).

2 votes

La notation Big O vous indiquera facilement ce que vous pouvez espérer être le plus rapide dans ce cas. Ce sont des ordres de grandeur différents.

28voto

Alterlife Points 2914

Le code ci-dessous serait un peu plus efficace que les réponses présentées ci-dessus lorsqu'il s'agit de plus grands ensembles de données.

SELECT *
FROM Call
WHERE NOT EXISTS (
    SELECT 'x'
    FROM Phone_book
    WHERE Phone_book.phone_number = Call.phone_number
);

2 votes

Comme toujours, il est utile de profiler les performances des requêtes par rapport à votre ensemble de données cible pour choisir celle qui offre les meilleures performances. Les optimiseurs SQL sont suffisamment performants de nos jours pour que les résultats en termes de performances soient souvent surprenants.

1 votes

Un avantage de cette approche (par rapport au LEFT OUTER JOIN par WOPR) est qu'elle évite de renvoyer plusieurs lignes par ligne de Call s'il y a plusieurs lignes correspondantes dans Phone_book . C'est-à-dire que s'il existe un 1:N relation entre vos deux tables.

1 votes

Je commencerais par celui-ci - il représente directement l'intention. Si les performances ne sont pas suffisantes, assurez-vous que les index appropriés existent. Ensuite seulement, essayez la moins évidente LEFT OUTER JOIN pour voir si ses performances sont meilleures.

7voto

Vlado Points 136
SELECT DISTINCT Call.id 
FROM Call 
LEFT OUTER JOIN Phone_book USING (id) 
WHERE Phone_book.id IS NULL

Vous obtiendrez ainsi les identifiants supplémentaires qui manquent dans votre table Phone_book.

5voto

Nat Geo Points 86

Je pense

SELECT CALL.* FROM CALL LEFT JOIN Phone_book ON 
CALL.id = Phone_book.id WHERE Phone_book.name IS NULL

0 votes

El id dans la colonne call n'est pas la même valeur que le tableau id dans la colonne Phone_book donc vous ne pouvez pas faire de jointure sur ces valeurs. Voir la réponse de WOPR pour une approche similaire.

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