2010-12-09 131 views
0

我已經撕裂了我的頭髮在此:問題將謂詞外連接

-- misses nulls 
SELECT * 
FROM BASE_TABLE TAB1 
    FULL JOIN BASE_TABLE TAB2 
     USING (ANOTHER_ID) 
WHERE (TAB1.ID = 6 OR TAB1.ID IS NULL) 
    AND (TAB2.ID = 8 OR TAB2.ID IS NULL); 

-- catches nulls 
SELECT * 
FROM (SELECT * FROM BASE_TABLE WHERE ID = 6) TAB1 
    FULL JOIN (SELECT * FROM BASE_TABLE WHERE ID = 8) TAB2 
     USING (ANOTHER_ID); 

的第一個查詢失去其中一個行或其他表不存在行。爲什麼第一個查詢無法執行外連接?

我一直在想我已經得到它 - WHERE子句首先被評估,所以'OR IS NULL'稍後不會被應用 - 但這對我沒有意義,因爲我已經成功地應用了'IS NULL '過去在連接後選擇行的謂詞。

我想做第一個查詢工作,出於性能原因 - 有誰知道問題是什麼?

+0

我感到困惑`USING` - 什麼語法是什麼?我無法在任何文檔中找到它,普通SQL使用`ON TAB1.ANOTHER_ID = TAB2.ANOTHER_ID` ..? – thomaspaulb 2010-12-09 23:25:21

+0

@littlegreen 9i以來的Oracle特性。它基本上是你寫的內容的簡寫。 – 2010-12-09 23:27:11

+2

@littlegreen,djacobson:當你使用`USING(ANOTHER_ID)`時,它只會產生一個名爲`ANOTHER_ID`的列(不能被別名引用),而`ON`語法產生兩列。 – 2010-12-10 02:34:26

回答

3

第一個查詢做了一個連接,然後是一個過濾器,第二個查詢過濾器然後是一個連接。 對於外連接而言,區分很重要。

你會理解它的一些示例數據。

create table tab1 (id number, another_id number); 
create table tab2 (id number, another_id number); 

insert into tab1 values (6,5); 
insert into tab2 values (8,5); 
insert into tab1 values (1,6); 
insert into tab2 values (2,6); 

SELECT * 
FROM TAB1 
    FULL JOIN TAB2 USING (ANOTHER_ID); 

    ANOTHER_ID    ID    ID 
--------------- --------------- --------------- 
      5.00   6.00   8.00 
      6.00   1.00   2.00 

結果集(不含WHERE)顯示another_id 6的連接條件已成功。不需要外連接。

當您添加WHERE過濾器時,它會過濾掉6的匹配項,因爲ids既不是6,8也不爲null。也就是說,你已經將它用作過濾器謂詞而不是連接謂詞。

我相信你的意圖是TAB1加入到TAB2應該有一個匹配ANOTHER_ID加上ID應爲6 TAB1和8 TAB2。這就是第二個SQL中的內容。

連接謂詞也可以表示爲

SELECT * 
FROM TAB1 
    FULL JOIN TAB2 ON 
      (TAB1.ANOTHER_ID = TAB2.ANOTHER_ID AND TAB1.ID=6 AND TAB2.ID=8) 
0

嗯。這很好,但我想我可能會擁有它。

在您的第一個查詢中,您完全將該表連接到ANOTHER_ID鍵上。

當兩個連接表相同時,無論是完全連接,內連接,左連接還是右連接都無關緊要:結果相同。因爲您的密鑰ANOTHER_ID總是存在或不存在於兩個表中。沒有任何情況下,一個表的ANOTHER_ID值在另一個表中找不到,因此不會出現TAB1.ANOTHER_ID或TAB2.ANOTHER_ID最終爲NULL的情況。所以你實際上只是在ANOTHER_ID上進行自我內部連接。

現在我不知道你的ID列的內容,但我想它總是充滿了一些價值。所以在你的自我內連接之後,生成的ID列總會被填充一些東西。也許不是6或8,但也不是NULL。在沒有NULL值的情況下,WHERE查詢轉換爲WHERE TAB1.ID = 6 AND TAB2.ID = 8,只留下正確的組合,而沒有其他值。

相比之下,在您的第二個查詢中,您正在定義ID = 6和ID = 8的子集,並將這些子集完全連接到另一個子集。子集1包含某些在子集2中不存在的ANOTHER_ID,反之亦然。所以現在有一個FULL JOIN的基礎,因爲某些行將不會被加入到其他行中,在您可以檢測到的TAB1.ID或TAB2.ID中留下NULL值。

我認爲你的第一個查詢可以通過改變你的WHERE子句來調整:WHERE TAB1.ID IN (6,8) AND TAB2.ID IN (6,8)。然而,這會給你一個不同的結果作爲你的第二個查詢,我想也是一定數量的重複行。我也不認爲這會更快。