[英]Is this a bug in Oracle when joining a table to a view that depends on the table
我發現了我認為Oracle中存在的一個錯誤,但我想知道是否記錄了我錯過的內容。
小提琴:Oracle: http ://sqlfiddle.com/#!4/43c19/2 SQL Server: http ://sqlfiddle.com/#!3 / ddc49 / 1 MySql: http ://sqlfiddle.com/#!2 / 43c195 / 1
基本上我有一個主表,我把它連接到一個輔助表。 然后我離開了加入視圖。 如果我在視圖的連接中指定我只想在輔助表中的列不為空時加入,則會得到意外的結果。 通過顯示查詢可以最好地解釋這一點:
SELECT
1,
MainTable.*
FROM
MainTable
LEFT JOIN SecondaryTable ON MainTable.KeyColumn = SecondaryTable.KeyColumn
LEFT JOIN ViewWithoutSecondary ON ((SecondaryTable.KeyColumn IS NOT NULL) AND SecondaryTable.KeyColumn = ViewWithoutSecondary.KeyColumn)
UNION ALL
SELECT
2,
MainTable.*
FROM
MainTable
LEFT JOIN SecondaryTable ON MainTable.KeyColumn = SecondaryTable.KeyColumn
LEFT JOIN ViewWithSecondary ON ((SecondaryTable.KeyColumn IS NOT NULL) AND SecondaryTable.KeyColumn = ViewWithSecondary.KeyColumn)
請參閱下面的創建腳本以自行測試。 在SQL Server和MySql中,我得到相同的結果,但Oracle是不同的。 架構中有三個表和兩個視圖。 視圖定義如下:
CREATE VIEW ViewWithoutSecondary
AS
SELECT
TertiaryTable.KeyColumn,
TertiaryValue + 1 ViewValue
FROM
TertiaryTable
CREATE VIEW ViewWithSecondary
AS
SELECT
SecondaryTable.KeyColumn,
TertiaryValue + 1 ViewValue
FROM
SecondaryTable
LEFT JOIN TertiaryTable ON SecondaryTable.KeyColumn = TertiaryTable.KeyColumn;
在Oracle中,我發現如果視圖包含對SecondaryTable的引用,那么我只從MainTable中獲取在輔助表中具有匹配項的行。 在我看來,Oracle正在以某種方式內聯視圖代碼,以便省略其中一行。
我認為如果MainTable有三行,那么它的兩個左連接應該總是至少返回三行,加上連接的任何結果。 但是在給出的例子中並非如此。
我知道SecondaryTable.KeyValue IS NOT NULL
是多余的,因為如果值為null,則子句的后半部分不會為true,但我一直在嘗試重寫查詢以幫助優化器提出更好的計划。
運行該示例的完整創建腳本是:
CREATE TABLE MainTable
(
KeyColumn varchar(32),
ValueColumn varchar(32)
);
INSERT INTO MainTable VALUES ('123', 'abc');
INSERT INTO MainTable VALUES ('456', 'def');
INSERT INTO MainTable VALUES ('789', 'ghi');
CREATE TABLE SecondaryTable
(
KeyColumn varchar(32),
SecondaryValue integer
);
INSERT INTO SecondaryTable VALUES ('123', 1);
INSERT INTO SecondaryTable VALUES ('456', 2);
CREATE TABLE TertiaryTable
(
KeyColumn varchar(32),
TertiaryValue integer
);
INSERT INTO TertiaryTable VALUES ('123', 1);
CREATE VIEW ViewWithoutSecondary
AS
SELECT
TertiaryTable.KeyColumn,
TertiaryValue + 1 ViewValue
FROM
TertiaryTable;
CREATE VIEW ViewWithSecondary
AS
SELECT
SecondaryTable.KeyColumn,
TertiaryValue + 1 ViewValue
FROM
SecondaryTable
LEFT JOIN TertiaryTable ON SecondaryTable.KeyColumn = TertiaryTable.KeyColumn;
如果對查詢運行解釋計划,則可以看到Oracle通過內聯視圖來轉換查詢,並且由於某種原因,它在第2行執行內部聯接,而不是在左外部執行。
explain plan
SET statement_id = 'no-hint' FOR
SELECT
MainTable.*
FROM
MainTable
LEFT JOIN SecondaryTable ON MainTable.KeyColumn = SecondaryTable.KeyColumn
LEFT JOIN ViewWithSecondary ON ((SecondaryTable.KeyColumn IS NOT NULL) AND SecondaryTable.KeyColumn = ViewWithSecondary.KeyColumn);
SELECT PLAN_TABLE_OUTPUT
FROM TABLE(DBMS_XPLAN.DISPLAY(NULL, 'no-hint','TYPICAL'));
----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2 | 108 | 20 (10)| 00:00:01 |
| 1 | NESTED LOOPS OUTER | | 2 | 108 | 20 (10)| 00:00:01 |
|* 2 | HASH JOIN | | 2 | 108 | 7 (15)| 00:00:01 |
| 3 | TABLE ACCESS FULL | SECONDARYTABLE | 2 | 36 | 3 (0)| 00:00:01 |
| 4 | TABLE ACCESS FULL | MAINTABLE | 3 | 108 | 3 (0)| 00:00:01 |
| 5 | VIEW | | 1 | | 7 (15)| 00:00:01 |
|* 6 | FILTER | | | | | |
|* 7 | HASH JOIN OUTER | | 1 | 36 | 7 (15)| 00:00:01 |
|* 8 | TABLE ACCESS FULL| SECONDARYTABLE | 1 | 18 | 3 (0)| 00:00:01 |
| 9 | TABLE ACCESS FULL| TERTIARYTABLE | 1 | 18 | 3 (0)| 00:00:01 |
----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("MAINTABLE"."KEYCOLUMN"="SECONDARYTABLE"."KEYCOLUMN")
6 - filter("SECONDARYTABLE"."KEYCOLUMN" IS NOT NULL)
7 - access("SECONDARYTABLE"."KEYCOLUMN"="TERTIARYTABLE"."KEYCOLUMN"(+))
8 - filter("SECONDARYTABLE"."KEYCOLUMN"="SECONDARYTABLE"."KEYCOLUMN")
解決此問題的方法是使用NO_MERGE提示。
SELECT /*+ NO_MERGE(ViewWithSecondary) */
MainTable.*
FROM
MainTable
LEFT JOIN SecondaryTable ON MainTable.KeyColumn = SecondaryTable.KeyColumn
LEFT JOIN ViewWithSecondary ON ((SecondaryTable.KeyColumn IS NOT NULL) AND SecondaryTable.KeyColumn = ViewWithSecondary.KeyColumn);
這會產生預期的結果:
KEYCOLUMN VALUECOLUMN
-------------------------------- --------------------------------
123 abc
456 def
789 ghi
比較提示查詢的查詢計划。 這里我們在第2行看到左外連接。
explain plan
SET statement_id = 'with-hint' FOR
SELECT /*+ NO_MERGE(ViewWithSecondary) */
MainTable.*
FROM
MainTable
LEFT JOIN SecondaryTable ON MainTable.KeyColumn = SecondaryTable.KeyColumn
LEFT JOIN ViewWithSecondary ON ((SecondaryTable.KeyColumn IS NOT NULL) AND SecondaryTable.KeyColumn = ViewWithSecondary.KeyColumn);
SELECT PLAN_TABLE_OUTPUT
FROM TABLE(DBMS_XPLAN.DISPLAY(NULL, 'with-hint','TYPICAL'));
--------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 6 | 324 | 26 (8)| 00:00:01 |
| 1 | NESTED LOOPS OUTER | | 6 | 324 | 26 (8)| 00:00:01 |
|* 2 | HASH JOIN OUTER | | 3 | 162 | 7 (15)| 00:00:01 |
| 3 | TABLE ACCESS FULL | MAINTABLE | 3 | 108 | 3 (0)| 00:00:01 |
| 4 | TABLE ACCESS FULL | SECONDARYTABLE | 2 | 36 | 3 (0)| 00:00:01 |
| 5 | VIEW | | 2 | | 7 (15)| 00:00:01 |
|* 6 | FILTER | | | | | |
|* 7 | VIEW | VIEWWITHSECONDARY | 2 | 36 | 7 (15)| 00:00:01 |
|* 8 | HASH JOIN OUTER | | 2 | 72 | 7 (15)| 00:00:01 |
| 9 | TABLE ACCESS FULL| SECONDARYTABLE | 2 | 36 | 3 (0)| 00:00:01 |
| 10 | TABLE ACCESS FULL| TERTIARYTABLE | 1 | 18 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("MAINTABLE"."KEYCOLUMN"="SECONDARYTABLE"."KEYCOLUMN"(+))
6 - filter("SECONDARYTABLE"."KEYCOLUMN" IS NOT NULL)
7 - filter("SECONDARYTABLE"."KEYCOLUMN"="VIEWWITHSECONDARY"."KEYCOLUMN")
8 - access("SECONDARYTABLE"."KEYCOLUMN"="TERTIARYTABLE"."KEYCOLUMN"(+))
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.