2014-11-21 11 views
5

Zapytanie z lewym złączeniem nie zwraca rekordów, chociaż klauzula where z lewej tabeli powinna znaleźć pojedynczy rekord. W takim przypadku powinien zwrócić rekord z polami z lewej tabeli zawierającymi wartości oraz z prawej tabeli null, ponieważ nie ma między nimi żadnego dopasowania.Lewe wyrażenie złączenia i liczba wierszy zwróconych przez Oracle

Najwyraźniej występuje problem z użyciem przypadku, który odwołuje się do prawej tabeli w wyrażeniu sprzężenia.

W SQL Server to samo zapytanie działało zgodnie z oczekiwaniami.

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 

Wynik: nie zwrócono wierszy.

Następnie przeniosłem wyrażenie t2.Id = t2.Id (które jest tutaj tylko po to, aby zademonstrować problem i powinno zawsze zwracać wartość true, najwyraźniej) z wyrażenia case.

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 

Wynik: zwrócił jeden wiersz.

Powyższe zapytania służą jedynie do wykazania problemu, nie są przydatne w rzeczywistej sytuacji i nie są zoptymalizowane.

Chcę wiedzieć, czy ktokolwiek wie jakiekolwiek ograniczenia Oracle związane z tą sprawą. Do tej pory uważamy, że jest to błąd.

Dane wykorzystywane:

  • A: Dichlorowodorek 1 Opis Przedmiot = A1;
  • B: Id = 1, Opis = Pozycja B1;
  • C: Id = 1, Id_B = 2, Flaga = S.
+0

Nie nazwałbym tego błędem, ja nazywam to inną implementacją. SQL Server i ORacle nie są klonami. Szczerze mówiąc, osobiście martwiłbym się bardziej o mój własny projekt bazy danych, jeśli czujesz potrzebę umieszczenia każdego rodzaju oświadczenia w akcie. – HLGEM

+0

Aby jeszcze raz sprawdzić, jak rozumiem problem, spodziewano się, że pierwsze zapytanie zwróci jeden wiersz? –

+0

Oto tabela testowa i zapytanie dotyczące problemu dla każdego, kto na to patrzy. Uruchom zapytanie, a następnie wyświetl plan wykonania. http://sqlfiddle.com/#!4/449ba/4 –

Odpowiedz

1

Twoje założenie, że t2.id = t2.id jest zawsze prawdziwe, jest błędne. Jeśli wartość wynosi NULL, będzie traktowana jako fałszywa. Nie uważam, że ma to znaczenie dla tego konkretnego przykładu, ale po to, by wyjaśnić.

Pytanie brzmi, jak przetwarzane jest left join. Pomysł jest prosty. Klauzula on jest przetwarzana. Jeśli nie ma dopasowań, wiersz z pierwszej tabeli jest zachowywany. Jest to niezależne od klauzuli on. (Jest to opis funkcjonalny, istnieje wiele możliwych implementacji).

Na podstawie przykładowych danych, Oracle jest nieprawidłowe. Jeden wiersz powinien zostać zwrócony. Przykład SQL Server powinien również zwrócić jeden wiersz. Podejrzewam, że dane mogą być subtelnie różne; Osobiście nigdy nie miałem problemów z left join s w SQL Server (lub Oracle).

+0

@philipxy. . . Masz rację (;). To wydawało się literówką/błędną pisownią, ale całkowicie zmieniło to znaczenie. –

+0

@philipxy. . . Dla 'left join', klauzula' on' jest obliczana jako całość. Albo jest mecz, albo nie ma dopasowania. Gdy nie ma zgodności, wewnętrzne sprzężenie nie generuje wyjścia. "Left join" tworzy wiersz z pierwszej tabeli z wartościami "NULL" dla kolumn w drugim. To, co dzieje się w klauzuli "on", jest nieistotne dla tego zachowania. Jeśli chcesz się dobrze bawić z 'left join', ale warunek filtru w * pierwszej * tabeli i zastanów się, dlaczego nic nie robi. –

+0

Mam na myśli tylko to, że twój wybór słów w pierwszym akapicie twojej odpowiedzi jest niejasny, gdzie w wyrażeniu i przetwarzaniu pojęciowym, jakie rzeczy mogą mieć wartości, jakie dane wejściowe. Podobnie "To, co dzieje się w klauzuli on, jest nieistotne dla tego zachowania" wydaje się dziwne, ponieważ klauzula on decyduje o tym, co się dzieje. – 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; 

Wyjście: no rows selected

Wynik wygląda dziwnie, przypuszczam, to może być błąd.

dokumentacji Oracle tylko stwierdza
https://docs.oracle.com/cd/B28359_01/server.111/b28286/queries006.htm#SQLRF52337

Nie można porównać kolumnę z podzapytania w klauzuli WHERE jakiejkolwiek łączenia zewnętrznego, niezależnie które tworzą można określić.

Patrząc na planie powyższego zapytania widzę, że ten warunek:

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 

jest interpretowane jako:

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

i obliczane po przyłączyć.

Przypuszczam, że Oracle nie może calcuate przypadku aż dołączyć odbywa się (z powodu T2.ID(+)=T2.ID(+))

+0

Sądzę, że istnieje pewien sens, w którym * dany * używa wartości post-left-join t2.id w przypadku, gdy "nie może" zrobić tego w ON; ale * nie powinno * używać tej wartości w przypadku. – philipxy

1

Korzystanie SQLFiddle Oracle 11g R2 (dzięki Shannon Severance) swój pierwszy kwerendy daje Record Count: 0, ale po prostu przez usunięcie CASE otrzymujemy Liczba rekordów: 1. (Uwaga zmianę nazwy 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 

to sugeruje, że to ma coś wspólnego z CASE jest przeliczył.

Zauważ, że w NA t2.Id jest przynajmniej czasami (prawidłowo) przyjmuje się wartość z dośrodkowań produktu, który nie jest NULL po ON:

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) 

znalazłem ten link : Outer Join Bug in Oracle 12c?

Powiązane problemy