對於內部聯接,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"
我明白你說的話。 ...你說內部連接在ON或WHERE子句中沒有區別指定條件。所以它不會在外部連接上有所作爲吧? – user1118468
@ user1118468 - 它在外連接方面有很大的不同 - 正如Bohemian和Guffa所說的那樣,將外連接條件移動到'where子句將其轉換爲內連接。用舊的語法很容易發生意外;與明確的加入你沒有太多的藉口。 –
@ user1118468 - 我添加了一些跡象,顯示當條件移到'where'時,外連接是如何被消除的。幫助跟蹤文件明確地說「將CHILD和PARENT的外部連接轉換爲內部連接」。 –