2014-02-26 35 views
2
SELECT * 
FROM Customers c 
INNER JOIN CustomerAccounts ca 
ON ca.CustomerID = c.CustomerID 
AND c.State = 'NY' 
INNER JOIN Accounts a 
ON ca.AccountID = a.AccountID 
AND a.Status = 1 

等效:連接條件 「ON」 與在 「WHERE」

SELECT * 
FROM Customers c 
INNER JOIN CustomerAccounts ca 
ON ca.CustomerID = c.CustomerID 
INNER JOIN Accounts a 
ON ca.AccountID = a.AccountID 
WHERE c.State = 'NY' 
AND a.Status = 1 

左連接:

SELECT * 
FROM Customers c 
LEFT JOIN CustomerAccounts ca 
ON ca.CustomerID = c.CustomerID 
AND c.State = 'NY' 
LEFT JOIN Accounts a 
ON ca.AccountID = a.AccountID 
AND a.Status = 1 

等效:

SELECT * 
FROM Customers c 
LEFT JOIN CustomerAccounts ca 
ON ca.CustomerID = c.CustomerID 
LEFT JOIN Accounts a 
ON ca.AccountID = a.AccountID 
WHERE c.State = 'NY' 
AND a.Status = 1 

右連接

SELECT * 
FROM Customers c 
RIGHT JOIN CustomerAccounts ca 
ON ca.CustomerID = c.CustomerID 
AND c.State = 'NY' 
RIGHT JOIN Accounts a 
ON ca.AccountID = a.AccountID 
AND a.Status = 1 

等效:

SELECT * 
FROM Customers c 
RIGHT JOIN CustomerAccounts ca 
ON ca.CustomerID = c.CustomerID 
RIGHT JOIN Accounts a 
ON ca.AccountID = a.AccountID 
WHERE c.State = 'NY' 
AND a.Status = 1 

有什麼區別時,我們指定 「WHERE」 條款與 「ON連接條件」 的連接條件它使?

通過指定「ON」子句中的連接條件和「WHERE」子句中的連接條件,我們是否可以在內部,左外部,右外部連接中獲得相同的結果。請指教。

回答

6

那麼,你所說的「等價」不是外連接的等價物。以左連接爲例。

條件在JOIN:

SELECT * FROM Customers c 
LEFT JOIN CustomerAccounts ca ON ca.CustomerID = c.CustomerID AND c.State = 'NY' 
LEFT JOIN Accounts a ON ca.AccountID = a.AccountID AND a.Status = 1 

VS WHERE:

SELECT * FROM Customers c 
LEFT JOIN CustomerAccounts ca ON ca.CustomerID = c.CustomerID 
LEFT JOIN Accounts a ON ca.AccountID = a.AccountID 
WHERE c.State = 'NY' 
AND a.Status = 1 

把條件到WHERE子句中有效地使加入INNER加入,因爲WHERE子句是行過濾器即在之後應用已加入。

2

右側表中的任何條件(第一個在左連接中)可以放在where子句中的連接上。內連接的所有條件也是如此。

任何條件在左邊的表(首先在右連接,第二在左連接)必須被放在on條款。如果將條件放在where子句中,則可以有效地將外部聯接轉換爲內部聯接。


您的左連接示例並不等同。在第二個條件中,條件爲where條款(a.Status = 1)中的左側表的條件,以便作爲內部聯接工作。

您的右連接示例並不等同。在第二個條件中,where子句(c.State = 'NY')中的左側表格有一個條件,所以它將作爲內部連接工作。

3

對於內部聯接,Oracle將根據基於成本的優化器的分析選擇要使用的條件以及要過濾的條件。您可能會從前兩個查詢中看到相同的執行計劃。它不一定使用on子句加入,然後使用where子句進行篩選。(無論如何它會將其重寫爲其內部格式,即ANSI之前的版本 - 如果您追蹤查詢,則可以看到它 - 並且在該格式中沒有區別)。

您可以通過查看解釋計劃來證明。一個有趣的演示是,如果您在兩列上有外鍵關係,並將父項與on中的相關列中的一個以及where中的其中一列相關聯,則將其與父項相加。

create table parent (pid1 number, pid2 number, 
    constraint parent_pk primary key (pid1, pid2)); 
create table child (cid number, pid1 number not null, pid2 number not null, 
    constraint child_pk primary key (cid), 
    constraint child_fk_parent foreign key (pid1, pid2) 
    references parent (pid1, pid2)); 
create index child_fk_index on child (pid1, pid2); 

set autotrace on explain 
select * 
from parent p 
join child c on c.pid2 = p.pid2 
where c.pid1 = p.pid1; 

----------------------------------------------------------------------------------------------- 
| Id | Operation     | Name   | Rows | Bytes | Cost (%CPU)| Time  | 
----------------------------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT    |    |  1 | 65 |  2 (0)| 00:00:01 | 
| 1 | NESTED LOOPS    |    |  |  |   |   | 
| 2 | NESTED LOOPS    |    |  1 | 65 |  2 (0)| 00:00:01 | 
| 3 | TABLE ACCESS FULL   | PARENT   |  1 | 26 |  2 (0)| 00:00:01 | 
|* 4 | INDEX RANGE SCAN   | CHILD_FK_INDEX |  1 |  |  0 (0)| 00:00:01 | 
| 5 | TABLE ACCESS BY INDEX ROWID| CHILD   |  1 | 39 |  0 (0)| 00:00:01 | 
----------------------------------------------------------------------------------------------- 

Predicate Information (identified by operation id): 
--------------------------------------------------- 

    4 - access("C"."PID1"="P"."PID1" AND "C"."PID2"="P"."PID2") 

該計劃顯示兩個列用於訪問,以及正在使用的索引。

甲骨文並不一定是你所期望的順序加入 - 表中from的順序並不限制甲骨文的上上策決定:

select * 
from parent p 
join child c on c.pid2 = p.pid2 
where c.pid1 = p.pid1 
and c.cid = 1; 

------------------------------------------------------------------------------------------ 
| Id | Operation     | Name  | Rows | Bytes | Cost (%CPU)| Time  | 
------------------------------------------------------------------------------------------ 
| 0 | SELECT STATEMENT    |   |  1 | 65 |  1 (0)| 00:00:01 | 
| 1 | NESTED LOOPS    |   |  1 | 65 |  1 (0)| 00:00:01 | 
| 2 | TABLE ACCESS BY INDEX ROWID| CHILD  |  1 | 39 |  1 (0)| 00:00:01 | 
|* 3 | INDEX UNIQUE SCAN   | CHILD_PK |  1 |  |  1 (0)| 00:00:01 | 
|* 4 | INDEX UNIQUE SCAN   | PARENT_PK | 82 | 2132 |  0 (0)| 00:00:01 | 
------------------------------------------------------------------------------------------ 

Predicate Information (identified by operation id): 
--------------------------------------------------- 

    3 - access("C"."CID"=1) 
    4 - access("C"."PID1"="P"."PID1" AND "C"."PID2"="P"."PID2") 

因此,對於內部連接它們是等價的,但是分開定義on子句中的關係的列可能是有用的,例如期望使用的鍵/索引中的列;以及任何僅僅在where中過濾的內容。 Oracle可能仍然沒有達到你期望的水平,但它顯示了你的意圖,並且有點自我記錄。

select * 
from child c 
join parent p on p.pid1 = c.pid1 and p.pid2 = c.pid2 
where c.cid = 1; 

...,它得到了相同的執行計劃爲前一個出現的,儘管相當不同:

------------------------------------------------------------------------------------------ 
| Id | Operation     | Name  | Rows | Bytes | Cost (%CPU)| Time  | 
------------------------------------------------------------------------------------------ 
| 0 | SELECT STATEMENT    |   |  1 | 65 |  1 (0)| 00:00:01 | 
| 1 | NESTED LOOPS    |   |  1 | 65 |  1 (0)| 00:00:01 | 
| 2 | TABLE ACCESS BY INDEX ROWID| CHILD  |  1 | 39 |  1 (0)| 00:00:01 | 
|* 3 | INDEX UNIQUE SCAN   | CHILD_PK |  1 |  |  1 (0)| 00:00:01 | 
|* 4 | INDEX UNIQUE SCAN   | PARENT_PK | 82 | 2132 |  0 (0)| 00:00:01 | 
------------------------------------------------------------------------------------------ 

Predicate Information (identified by operation id): 
--------------------------------------------------- 

    3 - access("C"."CID"=1) 
    4 - access("P"."PID1"="C"."PID1" AND "P"."PID2"="C"."PID2") 

從跟蹤這一點,在尋找跟蹤文件,你可以看到它轉化爲:

Final query after transformations:******* UNPARSED QUERY IS ******* 
SELECT "C"."CID" "CID","C"."PID1" "PID1","C"."PID2" "PID2","P"."PID1" "PID1", 
"P"."PID2" "PID2" FROM "STACKOVERFLOW"."CHILD" "C","STACKOVERFLOW"."PARENT" "P" 
WHERE "C"."CID"=1 AND "P"."PID1"="C"."PID1" AND "P"."PID2"="C"."PID2" 

...所以內部沒有區別 - 所有的條件都在where條款。

其他人已經涵蓋爲什麼這並不適用於外部連接,但因爲我提到的舊格式,移動的外連接條件的where大致相同,在舊的語法從條件省略(+)

比較這些查詢的轉換;外連接,其中兩個條件是on子句中:

select * 
from parent p 
left outer join child c on c.pid1 = p.pid1 and c.pid2 = p.pid2; 

Final query after transformations:******* UNPARSED QUERY IS ******* 
SELECT "P"."PID1" "PID1","P"."PID2" "PID2","C"."CID" "CID","C"."PID1" "PID1", 
"C"."PID2" "PID2" FROM "STACKOVERFLOW"."PARENT" "P","STACKOVERFLOW"."CHILD" "C" 
WHERE "C"."PID2"(+)="P"."PID2" AND "C"."PID1"(+)="P"."PID1" 

...和「相同」的查詢,其中條件之一已經被移動到where條款:

select * 
from parent p 
left outer join child c on c.pid1 = p.pid1 
where c.pid2 = p.pid2; 

Final query after transformations:******* UNPARSED QUERY IS ******* 
SELECT "P"."PID1" "PID1","P"."PID2" "PID2","C"."CID" "CID","C"."PID1" "PID1", 
"C"."PID2" "PID2" FROM "STACKOVERFLOW"."PARENT" "P","STACKOVERFLOW"."CHILD" "C" 
WHERE "C"."PID2"="P"."PID2" AND "C"."PID1"="P"."PID1" 

注意第一個查詢的條件都標記爲(+),而第二個查詢都沒有。在跟蹤的細節表明其對(外)連接消除決定:

OJE: Begin: find best directive for query block SEL$58A6D7F6 (#0) 
OJE: Considering outer-join elimination on query block SEL$58A6D7F6 (#0) 
OJE: considering predicate"C"."PID1"(+)="P"."PID1" 

rejected 
OJE: outer-join not eliminated 
OJE: End: finding best directive for query block SEL$58A6D7F6 (#0) 
... 
OJE: Begin: find best directive for query block SEL$9E43CB6E (#0) 
OJE: Considering outer-join elimination on query block SEL$9E43CB6E (#0) 
OJE: considering predicate"C"."PID2"="P"."PID2" 

OJE:  Converting outer join of CHILD and PARENT to inner-join. 
considered 
OJE: considering predicate"C"."PID1"="P"."PID1" 

rejected 
Registered qb: SEL$AE545566 0x2d07c338 (OUTER-JOIN REMOVED FROM QUERY BLOCK 
SEL$9E43CB6E; SEL$9E43CB6E; "C"@"SEL$1") 

外連接查詢已經成爲與此相同內加入:

select * 
from parent p 
inner join child c on c.pid1 = p.pid1 
where c.pid2 = p.pid2; 

Final query after transformations:******* UNPARSED QUERY IS ******* 
SELECT "P"."PID1" "PID1","P"."PID2" "PID2","C"."CID" "CID","C"."PID1" "PID1", 
"C"."PID2" "PID2" FROM "STACKOVERFLOW"."PARENT" "P","STACKOVERFLOW"."CHILD" "C" 
WHERE "C"."PID2"="P"."PID2" AND "C"."PID1"="P"."PID1" 
+0

我明白你說的話。 ...你說內部連接在ON或WHERE子句中沒有區別指定條件。所以它不會在外部連接上有所作爲吧? – user1118468

+0

@ user1118468 - 它在外連接方面有很大的不同 - 正如Bohemian和Guffa所說的那樣,將外連接條件移動到'where子句將其轉換爲內連接。用舊的語法很容易發生意外;與明確的加入你沒有太多的藉口。 –

+0

@ user1118468 - 我添加了一些跡象,顯示當條件移到'where'時,外連接是如何被消除的。幫助跟蹤文件明確地說「將CHILD和PARENT的外部連接轉換爲內部連接」。 –