繁体   English   中英

在将表连接到依赖于表的视图时,这是Oracle中的错误吗?

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM