SQL - Suchen Sie Datensätze aus einer Tabelle, die in einer anderen nicht vorhanden sind

310

Ich habe die folgenden zwei SQL-Tabellen (in 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 |
+----+------+--------------+

Wie finde ich heraus, welche Anrufe von Personen getätigt wurden, die phone_numbernicht in der sind Phone_book? Die gewünschte Ausgabe wäre:

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

Jede Hilfe wäre sehr dankbar.

Philip Morton
quelle

Antworten:

439

Es gibt verschiedene Möglichkeiten, dies mit unterschiedlicher Effizienz zu tun, je nachdem, wie gut Ihr Abfrageoptimierer ist und wie groß Ihre beiden Tabellen sind:

Dies ist die kürzeste Aussage und kann am schnellsten sein, wenn Ihr Telefonbuch sehr kurz ist:

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

alternativ (danke an Alterlife )

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

oder (danke an WOPR)

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

(Wenn Sie dies ignorieren, ist es normalerweise am besten, nur die gewünschten Spalten auszuwählen, nicht ' *')

Alnitak
quelle
1
Vermeiden Sie IN, verwenden Sie EXISTS - der Hinweis befindet sich im
Fragentitel
28
Die linke äußere Verknüpfung ist im allgemeinen Fall wahrscheinlich am schnellsten, da sie die wiederholte Ausführung der Unterabfrage verhindert.
WOPR
Nicht wählerisch zu sein, aber die Unterabfrage auf meinem Vorschlag gibt <code> select 'x' </ code> und nicht <code> select * </ code> zurück
Alterlife
ja - MySQL-Handbuch schlägt vor, dass dies für eine 'EXISTS'-Abfrage normal ist
Alnitak
2
@Alnitak: In der zweiten Abfrage brauchen Sie nicht SELECT *in der Unterabfrage. Stattdessen sollte zum Beispiel SELECT 1hübsch genug sein.
Alexander Abakumov
90
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

Sollte die Unterabfrage entfernen, damit der Abfrageoptimierer seine Magie entfalten kann.

Vermeiden Sie außerdem "SELECT *", da dies Ihren Code beschädigen kann, wenn jemand die zugrunde liegenden Tabellen oder Ansichten ändert (und dies ineffizient ist).

WOPR
quelle
10
Dies ist im Allgemeinen die effizienteste Methode, da nicht mehrere Durchgänge für den zweiten Tisch ausgeführt werden. Ich hoffe, einige Leute lesen die Kommentare.
Nerdfest
3
Ich würde eher hoffen, dass sich die Leute profilieren: Wenn Sie kein Top-SQL-Performance-Guru sind, ist es ziemlich schwierig, im Voraus zu sagen, was am schnellsten sein wird (und hängt von der von Ihnen verwendeten DBMS-Engine ab).
Bortzmeyer
2
Die Big O-Notation sagt Ihnen leicht, was Sie in diesem Fall als am schnellsten erwarten können. Die Größenordnungen sind unterschiedlich.
Jonesopolis
Siehe die Antwort von Afterlife und meinen Kommentar dort, wenn es eine 1:NBeziehung zwischen Ihren beiden Tabellen gibt. ODER fügen Sie hinzu, DISTINCTwie in Vlados Antwort zu sehen
ToolmakerSteve
25

Der folgende Code wäre etwas effizienter als die oben dargestellten Antworten, wenn es sich um größere Datensätze handelt.

SELECT * FROM Call WHERE 
NOT EXISTS (SELECT 'x' FROM Phone_book where 
Phone_book.phone_number = Call.phone_number)
Alterlife
quelle
1
Wie immer lohnt es sich, die Leistung der Abfragen anhand Ihres Zieldatensatzes zu profilieren, um den mit der besten Leistung auszuwählen. SQL-Optimierer sind heutzutage gut genug, dass die Leistungsergebnisse oft überraschend sind.
Greg Hewgill
1
Ein Vorteil dieses Ansatzes (gegenüber LEFT OUTER JOIN von WOPR) besteht darin, dass nicht mehrere Zeilen pro Zeile von zurückgegeben werden Call, wenn mehrere übereinstimmende Zeilen vorhanden sind Phone_book. Das heißt, wenn es eine 1:NBeziehung zwischen Ihren beiden Tabellen gibt.
ToolmakerSteve
Ich würde mit diesem anfangen - es repräsentiert direkt die Absicht. Wenn die Leistung nicht gut genug ist, stellen Sie sicher, dass geeignete Indizes vorhanden sind. Versuchen Sie erst dann das weniger Offensichtliche LEFT OUTER JOIN, um festzustellen , ob die Leistung besser ist.
ToolmakerSteve
6
SELECT DISTINCT Call.id 
FROM Call 
LEFT OUTER JOIN Phone_book USING (id) 
WHERE Phone_book.id IS NULL

Dadurch werden die zusätzlichen IDs zurückgegeben, die in Ihrer Phone_book-Tabelle fehlen.

Vlado
quelle
4

Ich glaube

SELECT CALL.* FROM CALL LEFT JOIN Phone_book ON 
CALL.id = Phone_book.id WHERE Phone_book.name IS NULL
Nat Geo
quelle
Die idSpalte in der callTabelle hat nicht den gleichen Wert wie die idSpalte in der Phone_bookTabelle, daher können Sie diesen Werten nicht beitreten. Siehe die Antwort von WOPR für einen ähnlichen Ansatz.
Michael Fredrickson
3
SELECT t1.ColumnID,
CASE 
    WHEN NOT EXISTS( SELECT t2.FieldText  
                     FROM Table t2 
                     WHERE t2.ColumnID = t1.ColumnID) 
    THEN t1.FieldText
    ELSE t2.FieldText
END FieldText       
FROM Table1 t1, Table2 t2
Harvinder Sidhu
quelle
Dies gibt Ihnen Daten aus einer Tabelle zurück, wenn keine Daten in einer anderen Tabelle für dieselbe Spalte vorhanden sind
Harvinder Sidhu
1
SELECT name, phone_number FROM Call a
WHERE a.phone_number NOT IN (SELECT b.phone_number FROM Phone_book b)
JoshYates1980
quelle
Dies gibt keine Antwort auf die Frage. Um einen Autor zu kritisieren oder um Klarstellung zu bitten, hinterlassen Sie einen Kommentar unter seinem Beitrag. - Aus dem Rückblick
Dennis Kriechel
@DennisKriechel hat die Abfrage aktualisiert, damit sie spezifischer für die Frage ist.
JoshYates1980
1

Alternative,

select id from call
minus
select id from phone_number
elfekz
quelle
1
Ich bin mir nicht sicher, ob dies die Frage beantwortet, da der Operator (obwohl der MINUS) neu hinzugefügt wurde. Dies endete in der Warteschlange mit geringer Qualität - vielleicht möchten Sie diese Antwort verbessern.
Ste-Fu