2009-08-17 11 views
11

Wie erstellen Sie eine SQL-Anweisung, die Ergebnisse zurückgibt, die von einer Unterabfrage oder einem Join - oder etwas anderem - geändert wurden, das sich mit Informationen befasst, die Sie zurückgeben möchten?SQL: "NICHT IN" Alternative zum Auswählen von Zeilen basierend auf Werten von * verschiedenen * Zeilen?

Zum Beispiel:

CREATE TABLE bowlers (
bowling_id int4 not null primary key auto_increment, 
name text, 
team text 
); 

Jemand falsch auf mehr als ein Team sein könnte:

INSERT INTO `bowlers` (`name`, `team`) VALUES 
('homer', 'pin pals'), 
('moe', 'pin pals'), 
('carl', 'pin pals'), 
('lenny', 'pin pals'), 
('homer', 'The homer team'), 
('bart', 'The homer team'), 
('maggie', 'The homer team'), 
('lisa', 'The homer team'), 
('marge', 'The homer team'), 
('that weird french guy', 'The homer team'); 

So homer kann nicht auf seinem Team entscheiden, so ist er auf beides. Do'h!

Ich möchte alle wissen, wer ist, the homer team Wer ist nicht auch auf dem pin pals Team. Das Beste, was ich tun kann, ist dies:

SELECT a.name, a.team 
    FROM bowlers a where a.team = 'The homer team' 
    AND a.name 
    NOT IN (SELECT b.name FROM bowlers b WHERE b.team = 'pin pals'); 

Resultat:

+-----------------------+----------------+ 
| name     | team   | 
+-----------------------+----------------+ 
| bart     | The homer team | 
| maggie    | The homer team | 
| lisa     | The homer team | 
| marge     | The homer team | 
| that weird french guy | The homer team | 
+-----------------------+----------------+ 
5 rows in set (0.00 sec) 

Welche, wissen Sie, brilliant!

Die Leistung wird darunter leiden, da die Unterabfrage für jedes Ergebnis der Abfrage ausgeführt werden wird, was ziemlich schlecht B zu A zu D. Groß für ein paar Zeilen, für die Hunderttausende von Reihen.

Was ist ein besserer Weg? Ich denke meistens, dass ein Self Join den Trick machen würde, aber ich kann mir nicht vorstellen, wie ich das machen soll.

Gibt es noch andere Möglichkeiten, dies zu tun, ohne Verwendung von NOT IN(SELECT ...)

Auch, was ist der Name für diese Art von Problem?

+1

linken äußeren Join zurück zu sich selbst ist, was Sie wollen. –

Antwort

15

So:

SELECT a.name, a.team 
FROM bowlers a 
LEFT OUTER JOIN bowlers b ON a.name = b.name AND b.team = 'pin pals' 
WHERE a.team = 'The homer team' 
AND b.name IS NULL; 

Sie können es auch tun, wie folgt aus:

SELECT a.name, a.team 
FROM bowlers a 
WHERE a.team = 'The homer team' 
AND NOT EXISTS (SELECT * FROM bowlers b 
    WHERE b.team = 'pin pals' 
    AND a.name = b.name 
    ); 

By the way, das ein "Left Anti-Semi Join" genannt wird.

+0

Brilliant! Ihr erstes Beispiel ist sicherlich eine Verbesserung meines Problems. Immer noch ein wenig langsam (MySQL Version 5.0.37), aber ein paar ... nun, zumindest jetzt ist es wieder da! Danke für den Begriff, um dies zu erklären (Linke Anti-Semi Join) –

2

Sie können LEFT JOIN und stellen Sie sicher, dass die verbundene Tabelle keine Daten enthält (alles ist Null).

SELECT a.name, a.team 
    FROM bowlers a 
    LEFT JOIN bowlers b 
     ON b.name = a.name AND b.team = 'pin pals' 
    WHERE a.team = 'The homer team' 
    AND a.name 
    -- the join has to fail for this to be null 
    AND b.bowling_id IS NULL 
+0

Das funktioniert definitiv, aber ist es mehr oder weniger effizient als seine Unterabfrage? Es scheint, als würde es mehr Aufwand für den Beitritt geben, aber ich weiß es nicht. – chrissr

+0

Es hängt stark von der Tabellenstruktur ab, welche Indizes verfügbar sind, wie viele Personen in Team A oder Team B sind, etc. Anzahl der Zeilen in der Tabelle usw. –

Verwandte Themen