Wie wähle ich Zeilen ohne passenden Eintrag in einer anderen Tabelle aus?


323

Ich mache einige Wartungsarbeiten an einer Datenbankanwendung und habe festgestellt, dass aus Freude, obwohl Werte aus einer Tabelle im Stil von Fremdschlüsseln verwendet werden, keine Fremdschlüsseleinschränkungen für die Tabellen gelten.

Ich versuche, FK-Einschränkungen für diese Spalten hinzuzufügen, aber ich stelle fest, dass ich die Zeilen finden muss, die dies nicht tun, da die Tabellen bereits eine ganze Menge fehlerhafter Daten aus früheren Fehlern enthalten, die naiv korrigiert wurden stimmen Sie mit der anderen Tabelle überein und löschen Sie sie dann.

Ich habe einige Beispiele für diese Art von Abfrage im Web gefunden, aber alle scheinen eher Beispiele als Erklärungen zu liefern, und ich verstehe nicht, warum sie funktionieren.

Kann mir jemand erklären , wie eine Abfrage zu erstellen , die alle Zeilen ohne Treffer in einer anderen Tabelle zurückgibt, und was es tut, so dass ich mich diese Fragen machen können, anstatt für jede Tabelle zu SO in diesem laufenden kommenden Chaos , das hat Keine FK-Einschränkungen?

Antworten:


611

Hier ist eine einfache Abfrage:

SELECT t1.ID
FROM Table1 t1
    LEFT JOIN Table2 t2 ON t1.ID = t2.ID
WHERE t2.ID IS NULL

Die wichtigsten Punkte sind:

  1. LEFT JOINwird genutzt; Dadurch werden ALLE Zeilen zurückgegeben Table1, unabhängig davon, ob eine übereinstimmende Zeile vorhanden ist oder nicht Table2.

  2. Die WHERE t2.ID IS NULLKlausel; Dadurch werden die zurückgegebenen Ergebnisse nur auf die Zeilen beschränkt, in denen die zurückgegebene ID Table2null ist. Mit anderen Worten, es gibt KEINEN Datensatz Table2für diese bestimmte ID von Table1. Table2.IDwird für alle Datensätze, bei Table1denen die ID nicht übereinstimmt, als NULL zurückgegeben Table2.


4
Schlägt fehl, wenn eine ID NULL ist
Michael

168
@Michael - Wenn eine NULLID in Ihrem Schema gültig ist, haben Sie möglicherweise größere Probleme, würden Sie nicht zustimmen? :)
Rinogo

1
Funktioniert dies auch dann, wenn Tabelle1 mehr Datensätze als Tabelle2 enthält? Wenn Tabelle1 100 Datensätze und Tabelle2 200 Datensätze enthält (100, die übereinstimmen / verknüpfen, und 100, die nicht übereinstimmen / verknüpfen), werden dann alle 200 Datensätze zurückgegeben?
Juan Velez

1
Ich mag es oft, den linken Join als Unterabfrage / Inline-Ansicht zu verpacken, um sicherzustellen, dass es kein Zusammenspiel zwischen der WHERE-Klausel und dem LEFT JOIN gibt.
Andrew Wolfe

1
@Jas Schlüsselpunkt 1 der Antwort, ALLE Zeilen aus der ersten Tabelle, auch diejenigen, die nicht mit t1.ID = t2.ID übereinstimmen. Bedingung der linken Verknüpfung. Wenn Sie die erste Zeile in SELECT t1.ID, t2.IDdie Zeile WHERE ändern und diese entfernen, erhalten Sie eine bessere Vorstellung davon, wie dies funktioniert.
Peter Laboš

97

Ich würde EXISTSAusdruck verwenden, da er leistungsfähiger ist. Sie können also genauer Zeilen auswählen, denen Sie beitreten möchten, falls LEFT JOINSie alles nehmen müssen, was in der verknüpften Tabelle enthalten ist. Die Effizienz ist wahrscheinlich die gleiche wie bei einem LEFT JOINNulltest.

SELECT t1.ID
FROM Table1 t1
WHERE NOT EXISTS (SELECT t2.ID FROM Table2 t2 WHERE t1.ID = t2.ID)

Etwas so Einfaches kann vom Abfrageoptimierer für eine optimale Ausführung leicht gehandhabt werden.
Andrew Wolfe

2
Ja, der Hauptvorteil von EXISTSist seine Variabilität.
Ondrej Bozek

1
Einfach, elegant und es hat mein Problem gelöst! Schön!
MikeMighty

2
Reduzierte tatsächlich die Geschwindigkeit einer Abfrage, die ich hatte, von 7 Sekunden auf 200 ms ... (im Vergleich zu WHERE t2.id IS NULL) Danke.
Moti Korets

4
@MotiKorets du meinst die Geschwindigkeit erhöht :)
Ondrej Bozek

14
SELECT id FROM table1 WHERE foreign_key_id_column NOT IN (SELECT id FROM table2)

Tabelle 1 enthält eine Spalte, zu der Sie die Fremdschlüsseleinschränkung hinzufügen möchten. Die Werte in Tabelle 1 foreign_key_id_columnstimmen jedoch nicht mit denen idin Tabelle 2 überein .

  1. Die anfängliche Auswahl listet die ids aus Tabelle1 auf. Dies sind die Zeilen, die wir löschen möchten.
  2. Die NOT INKlausel in der where-Anweisung beschränkt die Abfrage auf nur Zeilen, in denen der Wert in der foreign_key_id_columnnicht in der Liste der Tabelle 2 ids enthalten ist.
  3. Die SELECTAnweisung in Klammern enthält eine Liste aller ids in Tabelle 2.

@ zb226: Ihr Link zu hat mit Einschränkungen der INKlausel mit einer Liste von Literalwerten zu tun . INDies gilt nicht für die Verwendung einer Klausel mit dem Ergebnis einer Unterabfrage. Diese akzeptierte Antwort auf diese Frage löst das Problem tatsächlich mithilfe einer Unterabfrage. (Eine große Liste von Literalwerten ist problematisch, weil sie einen großen SQL-Ausdruck erzeugt. Eine
Unterabfrage funktioniert einwandfrei

@KannanGoundan Du hast absolut Recht. Rückzug des fehlerhaften Kommentars.
zb226

8

Wo T2ist die Tabelle, zu der Sie die Einschränkung hinzufügen:

SELECT *
FROM T2
WHERE constrained_field NOT
IN (
    SELECT DISTINCT t.constrained_field
    FROM T2 
    INNER JOIN T1 t
    USING ( constrained_field )
)

Und löschen Sie die Ergebnisse.


4

Lassen Sie uns die folgenden 2 Tabellen haben (Gehalt und Angestellter) Geben Sie hier die Bildbeschreibung ein

Jetzt möchte ich die Datensätze aus der Mitarbeitertabelle, die nicht im Gehalt sind. Wir können dies auf drei Arten tun:

  1. Verwenden der inneren Verknüpfung
select * from employee
where id not in(select e.id from employee e inner join salary s on e.id=s.id)

Geben Sie hier die Bildbeschreibung ein

  1. Verwenden der linken äußeren Verknüpfung
select * from employee e 
left outer join salary s on e.id=s.id  where s.id is null

Geben Sie hier die Bildbeschreibung ein

  1. Verwenden von Full Join
select * from employee e
full outer join salary s on e.id=s.id where e.id not in(select id from salary)

Geben Sie hier die Bildbeschreibung ein


2

Aus einer ähnlichen Frage hier MySQL Inner Join-Abfrage, um Datensätze zu erhalten, die in einer anderen Tabelle nicht vorhanden sind Ich habe dies zum Laufen gebracht

SELECT * FROM bigtable 
LEFT JOIN smalltable ON bigtable.id = smalltable.id 
WHERE smalltable.id IS NULL

smalltableHier haben Sie fehlende Datensätze. bigtableDort haben Sie alle Datensätze. Die Abfrage listet alle Datensätze auf, die nicht vorhanden sind, smalltableaber auf dem vorhanden sind bigtable. Sie können iddurch andere Übereinstimmungskriterien ersetzen .


0

Sie können sich wie unten gezeigt für Ansichten entscheiden :

CREATE VIEW AuthorizedUserProjectView AS select t1.username as username, t1.email as useremail, p.id as projectid, 
(select m.role from userproject m where m.projectid = p.id and m.userid = t1.id) as role 
FROM authorizeduser as t1, project as p

und arbeiten Sie dann an der Ansicht zum Auswählen oder Aktualisieren:

select * from AuthorizedUserProjectView where projectid = 49

Dies ergibt das Ergebnis wie in der Abbildung unten gezeigt, dh für eine nicht übereinstimmende Spalte wurde Null ausgefüllt.

[Result of select on the view][1]

0

Ich wusste nicht, welches optimiert ist (im Vergleich zu @AdaTheDev), aber dieses scheint schneller zu sein, wenn ich es benutze (zumindest für mich)

SELECT id  FROM  table_1 EXCEPT SELECT DISTINCT (table1_id) table1_id FROM table_2

Wenn Sie ein anderes spezifisches Attribut erhalten möchten, können Sie Folgendes verwenden:

SELECT COUNT(*) FROM table_1 where id in (SELECT id  FROM  table_1 EXCEPT SELECT DISTINCT (table1_id) table1_id FROM table_2);


-2

Sie können so etwas tun

   SELECT IFNULL(`price`.`fPrice`,100) as fPrice,product.ProductId,ProductName 
          FROM `products` left join `price` ON 
          price.ProductId=product.ProductId AND (GeoFancingId=1 OR GeoFancingId 
          IS NULL) WHERE Status="Active" AND Delete="No"

-6

Wie wähle ich Zeilen ohne übereinstimmenden Eintrag in der Tabelle Beide aus?

    Wählen Sie * aus [dbo]. [EmppDetails] e
     rechter Beitritt [Mitarbeiter]. [Geschlecht] d auf e.Gid = d.Gid
    Dabei ist e.Gid Null

    Union 
    Wählen Sie * aus [dbo]. [EmppDetails] e
     linker Beitritt [Mitarbeiter]. [Geschlecht] d auf e.Gid = d.Gid
    wobei d.Gid Null ist

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.