2014-11-21 11 views
5

Eine Abfrage mit linkem Join gibt keine Datensätze zurück, obwohl die where-Klausel aus der linken Tabelle einen einzelnen Datensatz finden sollte. In diesem Fall sollte ein Datensatz mit den Feldern aus der linken Tabelle mit Werten und aus der rechten Tabelle mit null zurückgegeben werden, da keine Übereinstimmung zwischen ihnen besteht.Linker Join-Ausdruck und Anzahl der von Oracle zurückgegebenen Zeilen

Anscheinend gibt es ein Problem mit der Verwendung von case, die auf die richtige Tabelle auf dem Join-Ausdruck verweist.

In SQL Server funktionierte die gleiche Abfrage wie erwartet.

select 
    t1.Description, t2.Description 
from 
    A t1 
left join 
    B t2 
on 
    t1.Id = t2.Id and 
    1 = case when (
    t2.Id = t2.Id and 
    (select t3.Flag from C t3 where t3.ID_B = t2.Id) = 'S' 
) then 1 else 0 
    end 
where t1.Id = 1 

Ergebnis: keine Zeilen zurückgegeben.

Dann habe ich den Ausdruck t2.Id = t2.Id (das ist hier nur, um das Problem zu demonstrieren und sollte immer wahr, scheinbar) aus dem Fall Ausdruck verschoben.

select 
    t1.Description, t2.Description 
from 
    A t1 
left join 
    B t2 
on 
    t1.Id = t2.Id and 
    t2.Id = t2.Id and 
    1 = case when ( 
    (select t3.Flag from C t3 where t3.ID_B = t2.Id) = 'S') then 1 else 0 
    end 
where t1.Id = 1 

Ergebnis: eine Zeile zurückgegeben.

Die obigen Abfragen dienen nur zur Demonstration des Problems, sind in einer realen Situation nicht sinnvoll und nicht optimiert.

Ich möchte wissen, ob irgendjemand eine Einschränkung von Oracle in Bezug auf diesen Fall kennt. Bis jetzt glauben wir, dass es ein Fehler ist.

verwendet Daten:

  • A: Id = 1, Description = Artikel A1;
  • B: Id = 1, Beschreibung = Element B1;
  • C: Id = 1, Id_B = 2, Flag = S.
+0

Ich würde es nicht einen Fehler nennen, ich würde es eine andere Implementierung nennen. SQL Server und Oracle sind keine Klone. Und ehrlich gesagt würde ich persönlich mir mehr Sorgen um mein eigenes Datenbankdesign machen, wenn Sie das Bedürfnis verspüren, eine Fallaussage jeglicher Art in einen Join einzufügen. – HLGEM

+0

Um mein Verständnis des Problems zu überprüfen, haben Sie erwartet, dass die erste Abfrage eine Zeile zurückgeben würde? –

+0

Hier sind Testtabelle und die Problemabfrage für jeden, der sich das anschaut. Führen Sie die Abfrage aus, und zeigen Sie den Ausführungsplan an. http://sqlfiddle.com/#!4/449ba/4 –

Antwort

1

Ihre Annahme, dass t2.id = t2.id immer wahr ist, ist falsch. Wenn der Wert NULL wäre, würde das als falsch behandelt werden. Ich glaube nicht, dass das für dieses spezielle Beispiel relevant ist, sondern nur zur Klarstellung.

Die Frage ist, wie wird ein left join verarbeitet. Die Idee ist einfach. Die on Klausel wird verarbeitet. Wenn keine Übereinstimmungen vorhanden sind, wird die Zeile aus der ersten Tabelle beibehalten. Dies ist unabhängig davon, was in der on Klausel ist. (Dies ist eine Funktionsbeschreibung; es gibt viele mögliche Implementierungen.)

Basierend auf Ihren Beispieldaten ist Oracle nicht korrekt. Eine Zeile sollte zurückgegeben werden. Das SQL Server-Beispiel sollte außerdem eine Zeile zurückgeben. Ich vermute, dass die Daten sich geringfügig unterscheiden könnten; Ich persönlich hatte nie Probleme mit left join s in SQL Server (oder Oracle).

+0

@philipxy. . . Du hast Recht (;). Das schien ein Tippfehler zu sein, aber es änderte die Bedeutung völlig. –

+0

@philipxy. . . Für einen 'linken Join' wird die On-Klausel als Ganzes berechnet. Entweder gibt es eine Übereinstimmung oder es gibt keine Übereinstimmung. Wenn keine Übereinstimmung vorhanden ist, erzeugt eine innere Verknüpfung keine Ausgabe. Ein 'left join' erzeugt die Zeile aus der ersten Tabelle mit' NULL' Werten für die Spalten in der zweiten Tabelle. Was in der On-Klausel passiert, spielt für dieses Verhalten keine Rolle. Wenn Sie mit 'left join' Spaß haben wollen, aber eine Filterbedingung in der * first * -Tabelle haben und darüber nachdenken, warum es nichts bringt. –

+0

Ich meine nur, dass Ihre Wortwahl im ersten Absatz Ihrer Antwort unklar ist, wo im Ausdruck & konzeptionelle Verarbeitung welche Dinge welche Werte für welche Eingabe haben können. Ähnlich verhält es sich mit "Was mit der on-Klausel passiert, spielt für dieses Verhalten keine Rolle". Es erscheint seltsam, da die on-Klausel die Übereinstimmungen bestimmt. – philipxy

1
CREATE TABLE t1 AS (SELECT 1 ID FROM dual); 
CREATE TABLE t2 AS (SELECT 2 ID FROM dual); 
CREATE TABLE t3 AS (SELECT 2 id_b, 's' flag FROM dual); 

SELECT t1.* 
FROM t1 LEFT JOIN t2 
     ON t1.ID = t2.ID 
     AND 1 = CASE WHEN t2.id = t2.id and (SELECT flag FROM t3 WHERE t3.id_b = t2.ID) = 's' THEN 1 ELSE 0 END 
where t1.id = 1; 

Der Ausgang: no rows selected

Das Ergebnis sieht seltsam aus, ich nehme an es kann ein Fehler sein.

Oracle-Dokumentation wird nur
https://docs.oracle.com/cd/B28359_01/server.111/b28286/queries006.htm#SQLRF52337

Sie können eine Spalte mit einer Unterabfrage in der WHERE-Klausel jeder äußeren vergleichen verbinden, unabhängig davon, welche Sie angeben bilden.

auf dem Plan der obigen Abfrage Mit Blick ich, dass dieser Zustand sehen:

AND 1 = CASE WHEN t2.id = t2.id and (SELECT flag FROM t3 WHERE t3.id_b = t2.ID) = 's' THEN 1 ELSE 0 END 

Ist interpretiert als:

CASE WHEN (T2.ID(+)=T2.ID(+) AND (SELECT FLAG FROM T3 T3 WHERE T3.ID_B=:B1)='s') THEN 1 ELSE 0 END =1 

und ist berechnet nach dem Join.

Ich nehme an, dass Oracle nicht der Fall calcuate kann, bis die Verbindung durchgeführt wird (wegen T2.ID(+)=T2.ID(+))

+0

Ich denke, es gibt einen Sinn, in dem * gegeben *, dass es den Post-links-Join-Wert von t2.id in dem Fall verwendet, in dem es dies nicht im ON tun kann; aber * sollte diesen Wert nicht in diesem Fall verwenden. – philipxy

1

Mit SQLFiddle Oracle 11g R2 (dank Shannon Severance) Ihre erste Abfrage gibt Rekord Anzahl: 0, aber einfach durch das Entfernen CASE wir bekommen Anzahl der Datensätze: 1. (Beachten sie die Umbenennung von t2Description.)

create table A (ID number(38), Description varchar(10)); 
create table B (ID number(38), Description varchar(10)); 
create table C (ID number(38), ID_B number(38), Flag varchar(10)); 

insert into A values(1, 'Item A1'); 
insert into B values(2, 'Item B1'); 
insert into C values(1, 2, 'S'); 

select 
    t1.Description, t2.Description as t2d 
from 
    A t1 
left join 
    B t2 
on 
    t1.Id = t2.Id and 
    t2.Id = t2.Id and 
    (select t3.Flag from C t3 where t3.ID_B = t2.Id) = 'S' 
where t1.Id = 1 

Dies legt nahe, dass es etwas mit CASE falsch berechnet wird, zu tun hat.

anzumerken, dass in der EIN t2.Id zumindest manchmal ist (richtig) genommen wird der Wert aus dem von dem Langprodukt zu sein, nicht NULL, die es nach dem EIN ist:

select 
    t1.Description, t2.Description as t2d 
from 
    A t1 
left join 
    B t2 
on 
    -- for above data t2.id should be 1 here 
    t2.id is null 
where t1.Id = 1 
-- for above data t2.id should be null here 

DESCRIPTION  T2D 
Item A1  (null) 

fand ich diesen Link : Outer Join Bug in Oracle 12c?

Verwandte Themen