2008-09-29 4 views
23

Ich habe zwei Tabellen, die zusammengefügt sind.Wie finde ich Datensätze, die nicht verknüpft sind?

A hat viele B

Normalerweise würden Sie tun:

select * from a,b where b.a_id = a.id 

Um alle Datensätze aus einer zu erhalten, die einen Datensatz in b hat.

Wie bekomme ich nur die Datensätze in einem, das nichts in b hat?

Antwort

42
select * from a where id not in (select a_id from b) 

Oder wie einige andere Leute auf diesen Thread sagt:

select a.* from a 
left outer join b on a.id = b.a_id 
where b.a_id is null 
2

select * from a where id nicht in (wählen a_id aus b)

+1

Dies wird extrem teuer sein, da die Engine die Unterabfrage in ihrer Gesamtheit generieren muss, bevor sie beginnen kann, die Tupel von einem zu entfernen. Im Allgemeinen keine gute Idee. – dland

10
select * from a 
left outer join b on a.id = b.a_id 
where b.a_id is null 
+0

Ich denke, dass dieser linke äußere Join wird viel besser als die 'in' -Klausel durchführen, es sei denn, der Abfrageoptimierer behandelt sie gleich ... – Codewerks

+0

Es tut ziemlich viel. Hör zu. –

+0

Ja, interessanterweise hat der Abfrageplan einen zusätzlichen Strep (Filter) für den linken Join und das 'Where In' wird zu einem 'richtigen Anti Semi Join' aufgelöst ... was auch immer das ist ... – Codewerks

5

Ein anderer Ansatz:

select * from a where not exists (select * from b where b.a_id = a.id) 

Der Ansatz "existiert" ist nützlich, wenn th Es gibt eine andere "where" -Klausel, die Sie an die innere Abfrage anhängen müssen.

1

Sie werden wahrscheinlich viel bessere Leistung (als mit 'nicht in'), wenn man eine äußere verwenden beitreten:

select * from a left outer join b on a.id = b.a_id where b.a_id is null; 
0

Ein anderer Weg, es

select a.* from a left outer join b on a.id = b.id where b.id is null

Ouch des Schreibens, geschlagen von Nathan :)

0

Dies schützt Sie vor Nullen in der IN-Klausel, die unerwartetes Verhalten verursachen können.

select * from a where id nicht in (wählen Sie [eine id] aus b, wo [a id] nicht null ist)

+0

Sie sind besser dran mit ein linker äußerer Join, anstatt ein IN-Prädikat an erster Stelle zu verwenden. – dland

+0

Gründe für diese Meinung? –

3
SELECT id FROM a 
EXCEPT 
SELECT a_id FROM b; 
+0

Das EXCEPT-Schlüsselwort ist in Oracle "MINUS". – onedaywhen

0

Bei man es kommen ziemlich schnell, aber wenn Wir entfernen Datensätze aus der Datenbank, die etwa 50 Millionen Datensätze und 4 und mehr Joins aufgrund von Fremdschlüsseln enthält, es dauert ein paar Minuten, es zu tun. Viel schneller zu verwenden, wenn nicht in einem Zustand wie folgt aus:

select a.* from a 
where a.id NOT IN(SELECT DISTINCT a_id FROM b where a_id IS NOT NULL) 
//And for more joins 
AND a.id NOT IN(SELECT DISTINCT a_id FROM c where a_id IS NOT NULL) 

ich diesen Ansatz auch zum Löschen empfohlen, falls wir nicht konfiguriert Kaskade löschen. Diese Abfrage dauert nur wenige Sekunden.

0

Der erste Ansatz ist

select a.* from a where a.id not in (select b.ida from b) 

der zweite Ansatz ist

select a.* 
    from a left outer join b on a.id = b.ida 
    where b.ida is null 

Der erste Ansatz ist sehr teuer. Der zweite Ansatz ist besser.

Mit PostgreSQL 9.4, habe ich die "EXPLAIN Query" -Funktion und die erste Abfrage als Kosten von Kosten = 0,00 ..1982043603.32. Stattdessen kommen die Abfrage als Kosten für Kosten = 45946.77..45946.78

Zum Beispiel habe ich die Suche für alle Produkte, die nicht kompatibel ohne Fahrzeuge. Ich habe 100k Produkte und mehr als 1m Kompatibilität.

Die Join-Abfrage verbrachte etwa 5 Sekunden, stattdessen wurde die Unterabfrage-Version nach 3 Minuten nie beendet.