2014-11-21 81 views
5

左連接查詢不返回記錄,儘管左表的where子句應該找到單個記錄。在這種情況下,它應該返回一個記錄,其中包含值的左表中的字段和右表中的字段爲null,因爲它們之間沒有匹配。左連接表達式和Oracle返回的行數量

顯然,使用引用連接表達式上的右表的案例存在問題。

在SQL Server中,相同的查詢按預期工作。

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 

結果:沒有行返回。

然後,我將表達式t2.Id = t2.Id(這裏僅僅是爲了演示問題,並且應該總是返回true,顯然)移出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 

結果:返回一行。

以上查詢僅用於演示問題,在實際情況下無用而且未優化。

我想知道是否有人知道Oracle與本案有關的任何限制。到目前爲止,我們認爲這是一個錯誤。

使用的數據:

  • 答:ID = 1,描述=項目A1;
  • B:Id = 1,Description = Item B1;
  • C:Id = 1,Id_B = 2,Flag = S。
+0

我不會說它是一個錯誤,我想叫它不同的實現。 SQL Server和ORACLE不是克隆。坦率地說,如果您覺得有必要在聯接中添加任何類型的case語句,我個人會更關心我自己的數據庫設計。 – HLGEM 2014-11-21 18:33:37

+0

爲了仔細檢查我對問題的理解,您預期第一個查詢將返回一行? – 2014-11-21 21:14:53

+0

這裏是測試表和查詢這個問題的任何人的問題查詢。運行查詢,然後查看執行計劃。 http://sqlfiddle.com/#!4/449ba/4 – 2014-11-21 21:25:01

回答

1

你的假設t2.id = t2.id總是對的是錯的。如果值爲NULL將被視爲錯誤。我不認爲這與這個特定的例子有關,只是爲了澄清。

問題是如何處理left join。這個想法很簡單。處理on子句。如果沒有匹配,則保留第一個表中的行。這與on條款中的內容無關。 (這是一個功能描述;有很多可能的實現。)

根據您的示例數據,Oracle不正確。應該返回一行。 SQL Server示例還應該返回一行。我懷疑數據可能略有不同;我個人從未遇到SQL Server(或Oracle)中的left join問題。

+0

@philipxy。 。 。你是對的 (;)。這似乎是一個錯字/拼寫錯誤,但它完全改變了意思。 – 2014-11-23 13:11:37

+0

@philipxy。 。 。對於「左連接」,「on」子句是作爲整體計算的。要麼有比賽,要麼沒有比賽。當沒有匹配時,內部連接不產生輸出。 「left join」從第一個表中生成行,第二個列中的列爲NULL值。在「on」子句中發生的事情對這種行爲並不重要。如果你想用'left join'獲得樂趣,但是在* first *表中有一個過濾條件,並且思考爲什麼它沒有做任何事情。 – 2014-11-23 21:47:18

+0

我只是說你的答案的第一段中的單詞選擇不清楚,在表達和概念處理中,什麼東西可以具有什麼輸入值。類似地,「on子句中發生的事情對此行爲無關緊要」似乎很奇怪,因爲on子句決定了匹配。 – philipxy 2014-11-23 22:30:08

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; 

輸出:no rows selected

結果看起來很奇怪,我想這可能是一個錯誤。

Oracle文檔僅指出
https://docs.oracle.com/cd/B28359_01/server.111/b28286/queries006.htm#SQLRF52337

你不能用任何 外的WHERE子句中的子查詢比較的列加入,無論何種形式指定。

通過查看上述查詢我可以看到這種情況的計劃:

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 

被解釋爲:

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

,是聯接後計算。

我想,直到加入的(因爲T2.ID(+)=T2.ID(+)

+0

我想有一種感覺,其中*給出*它使用t2.id的左後連接值,在「不能」的情況下在ON中這樣做;但它*不應該*在案件中使用該值。 – philipxy 2014-11-23 11:06:17

1

使用SQLFiddle的Oracle 11g R2(感謝香遣散)第一個查詢給記錄計數進行Oracle無法calcuate消協:0,但通過簡單地移除情況下,我們得到記錄數:1(注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 

這表明它是與CASE被打錯了算盤。

注意,在ON t2.Id是至少有時(正確地)取爲從來自交叉產品,而不是NULL值,它是接通後:

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) 

我發現這個鏈接:Outer Join Bug in Oracle 12c?