2009-05-09 2 views
0

Dies ist eine vereinfachte Version einer Abfrage, die wir ausführen, wo wir alle Zeilen in der übergeordneten Haupttabelle finden müssen, in der die untergeordneten Zeilen übereinstimmen. Die folgende Abfrage gibt keine Ergebnisse zurück, wenn eine der untergeordneten Tabellen leer ist.Warum gibt diese Abfrage nur Ergebnisse mit nicht leeren untergeordneten Tabellen zurück?

Die Haupttabelle hat zwei untergeordneten Tabellen:

CREATE TABLE main (id INT PRIMARY KEY, name VARCHAR(8)); 

CREATE TABLE child1(id INT PRIMARY KEY, main_id int, name VARCHAR(8)); 
ALTER TABLE child1 add constraint fk_child1_main foreign key (main_id) references main (id); 

CREATE TABLE child2(id INT PRIMARY KEY, main_id int, name VARCHAR(8)); 
ALTER TABLE child2 add constraint fk_child2_main foreign key (main_id) references main (id); 

INSERT INTO main (id, name) VALUES (1, 'main'); 
INSERT INTO child1 (id, main_id, name) VALUES (2, 1, 'child1'); 

Es gibt keine Zeilen in child2 und die folgende Abfrage gibt keine Zeilen, wenn es leer ist:

SELECT 
    main.* 
FROM 
    main 
INNER JOIN 
    child1 
ON 
    main.id = child1.main_id 
INNER JOIN 
    child2 
ON 
    main.id = child2.main_id 
WHERE 
    child1.name = 'child1' OR 
    child2.name = 'DOES NOT EXIST'; 

Wenn eine Zeile hinzugefügt wird child2, selbst wenn es nicht mit der WHERE-Klausel übereinstimmt, gibt SELECT die Zeile in der Haupttabelle zurück.

INSERT INTO child2 (id, main_id, name) VALUES (4, 1, 'child2'); 

Ich habe dies auf Derby und SQLite getestet, so dass dies etwas allgemein mit Datenbanken aussieht.

Warum verhält sich das so?

Was kann ich tun, um es zu beheben?

Ich könnte zu UNION separate SELECTs ändern, aber das ist viel ausführlicher, und plus, generieren wir die SQL dynamisch und ich würde lieber nicht unseren Code ändern müssen.

Ein weiterer Fix ist nur eine dumme Zeile zur Datenbank hinzufügen, aber das ist chaotisch.

PS Die Haupttabelle ist eine Sitzungstabelle in einem Asset Management-System, die die Assets aufzeichnet, die Clients suchen. Es gibt verschiedene Arten von Suchvorgängen, und jeder Typ erhält eine separate Kindtabelle, außerdem gibt es eine Attributtabelle für Schlüssel/Wert-Paare für die Sitzung, nach der gesucht werden kann.

Antwort

4

Wenn child2 keine Zeilen enthält, gibt die Abfrage keine Zeilen zurück, da der innere Join zur Tabelle child2 besteht. Wenn Sie innerlich einer Tabelle beitreten, die keine Zeilen enthält, werden Sie nie irgendwelche Ergebnisse erhalten - Sie müssten sich stattdessen mit child2 verbinden, wenn Sie Ergebnisse erhalten möchten, wenn child2 leer ist.

Wenn child2 hat eine Zeile, den Grund Ihrer Abfrage gibt die Ergebnisse wegen der Where-Klausel ist:

 
WHERE 
    child1.name = 'child1' OR 
    child2.name = 'DOES NOT EXIST'; 

Die innere Verknüpfung sagt etwas in child2 mit einer entsprechenden ID sein, aber die, wo Die Klausel hat ein OR, also erhalten Sie nur Ergebnisse, weil child1.name = 'child1'. Danach muss sich die Datenbank nicht mehr die child2-Tabellen ansehen.

es beheben:

ich Ahnung haben, dass Sie nur die untergeordneten Zeilen zurückgegeben werden soll, wenn eine bestimmte Bedingung erfüllt ist. Sie sollten beide Außen verbinden, und vielleicht auch Ihre persönlichen Bedingungen der Where-Klausel in die Join-Klausel verschieben, wie folgt aus:

 
SELECT 
    main.* 
FROM 
    main 
LEFT OUTER JOIN 
    child1 
ON 
    main.id = child1.main_id 
    AND child1.name = 'child1' 
LEFT OUTER JOIN 
    child2 
ON 
    main.id = child2.main_id 
    AND child2.name = 'whatever' 
  • Die Outer-Joins meinen Sie die Chance auf Ergebnisse haben auch wenn ein Tisch leer ist.

  • Das Verschieben der zusätzlichen Bedingungen (child1.name = ...) von der WHERE-Klausel in den äußeren Join bedeutet, dass Sie nur die Tabelleninformationen erhalten, wenn die Bedingung wahr ist.(Ich denke, das könnte sein, was Sie zu tun versuchen, aber vielleicht auch nicht, wobei in diesem Fall lassen Sie die Bedingungen in der WHERE-Klausel, wo man sie ursprünglich hatte.)

2

Es ist nichts zurückkehrt, weil Sie verwenden innere Joins .

Ändern Sie Ihre innere Joins

+0

Warum ändert das Hinzufügen einer nicht übereinstimmenden Zeile zu child2 das Ergebnis der Abfrage? Gemäß dieser Anweisung sollte die Abfrage selbst nach dem Hinzufügen einer nicht übereinstimmenden Zeile zu child2 immer noch keine Zeilen zurückgeben. –

+1

Nevermind, ich sehe, dass es die main.id = child2.main_id ist, die verhindert hat, dass die Abfrage Ergebnisse zurückgibt, selbst wenn child2.name nicht übereinstimmt. Ich habe diesen Teil der Abfrage ignoriert. –

2

schließt sich nach links Wenn Sie sagen, INNER JOIN Sie die Abfrage fragen Zeilen zurückgeben, die Ergebnisse auf beiden Seiten der Verbindung haben. Dies bedeutet, dass alle Zeilen entfernt werden, die keine übereinstimmenden untergeordneten Zeilen haben.

Es klingt nach dem, was Sie suchen, ist LEFT JOIN, die alle Zeilen auf der linken Seite des Joins (main) enthält, auch wenn sie keinen passenden Eintrag auf der rechten Seite haben (child1, child2) .

Dies ist Standardverhalten und ein sehr häufiges Problem für Leute, die nicht mit SQL vertraut sind. Wikipedia hat alle Details, sonst bringt eine schnelle Google search viele Ergebnisse.

+0

Warum ändert das Hinzufügen einer nicht übereinstimmenden Zeile zu child2 das Ergebnis der Abfrage? Gemäß dieser Anweisung sollte die Abfrage selbst nach dem Hinzufügen einer nicht übereinstimmenden Zeile zu child2 immer noch keine Zeilen zurückgeben. –

+0

Nevermind, ich sehe, dass es die main.id = child2.main_id ist, die verhindert hat, dass die Abfrage Ergebnisse zurückgibt, selbst wenn child2.name nicht übereinstimmt. Ich habe diesen Teil der Abfrage ignoriert. –

Verwandte Themen