Oracle12外连接发明了列值

时间:2017-01-17 16:09:26

标签: left-join outer-join oracle12c

如果我在Oracle12 db上执行以下语句,我得到一个结果,我根本无法解释:

CREATE TABLE table_a (
    a_id NUMBER NOT NULL ,
    PRIMARY KEY ( a_id )
);

CREATE TABLE table_b (
    b_id NUMBER NOT NULL ,
    col_1 NUMBER ,
    PRIMARY KEY ( b_id )
);

ALTER TABLE table_b
    ADD FOREIGN KEY (b_id) REFERENCES table_a (a_id);

insert into table_a (a_id) values (1);
insert into table_a (a_id) values (2);
insert into table_a (a_id) values (3);
insert into table_a (a_id) values (4);
insert into table_b (b_id, col_1) values (1, 100);
insert into table_b (b_id, col_1) values (2, 101);

select a_id, b_id, col_1
    from table_a left outer join table_b on a_id=b_id
    where a_id in (1 , 3 , 4);

这导致以下输出:

      A_ID       B_ID      COL_1
---------- ---------- ----------
         1          1        100
         3          3
         4          4

根据我的理解,B_ID列中的值3和4不应该存在,因为该表仅包含值1和2:

select * from table_b;

      B_ID      COL_1
---------- ----------
         1        100
         2        101

为了列出完整的数据,这里是table_a

select * from table_a;

      A_ID
----------
         1
         2
         3
         4

以下是有关执行路径的更多信息:

select * from table( dbms_xplan.display_cursor( null, null, '+OUTLINE' ) );

PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  6wg8b65y25utv, child number 2
-------------------------------------
select a_id, b_id, col_1     from table_a left outer join table_b on
a_id=b_id     where a_id in (1 , 3 , 4)

Plan hash value: 2951123891

---------------------------------------------------------------------------------------------
| Id  | Operation                    | Name         | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |              |       |       |     2 (100)|          |
|   1 |  NESTED LOOPS OUTER          |              |     1 |    39 |     2   (0)| 00:00:01 |
|   2 |   INLIST ITERATOR            |              |       |       |            |          |
|*  3 |    INDEX UNIQUE SCAN         | SYS_C0013651 |     3 |    39 |     1   (0)| 00:00:01 |
|   4 |   TABLE ACCESS BY INDEX ROWID| TABLE_B      |     1 |    26 |     1   (0)| 00:00:01 |
|*  5 |    INDEX UNIQUE SCAN         | SYS_C0013653 |     1 |       |     1   (0)| 00:00:01 |
---------------------------------------------------------------------------------------------

Outline Data
-------------

  /*+
      BEGIN_OUTLINE_DATA
      IGNORE_OPTIM_EMBEDDED_HINTS
      OPTIMIZER_FEATURES_ENABLE('12.1.0.2')
      DB_VERSION('12.1.0.2')
      OPT_PARAM('optimizer_dynamic_sampling' 11)
      OPT_PARAM('optimizer_index_cost_adj' 20)
      OPT_PARAM('optimizer_index_caching' 90)
      ALL_ROWS
      OUTLINE_LEAF(@"SEL$2BFA4EE4")
      MERGE(@"SEL$8812AA4E")
      OUTLINE(@"SEL$948754D7")
      ANSI_REARCH(@"SEL$2")
      OUTLINE(@"SEL$8812AA4E")
      ANSI_REARCH(@"SEL$1")
      OUTLINE(@"SEL$2")
      OUTLINE(@"SEL$1")
      INDEX(@"SEL$2BFA4EE4" "TABLE_A"@"SEL$1" ("TABLE_A"."A_ID"))
      INDEX_RS_ASC(@"SEL$2BFA4EE4" "TABLE_B"@"SEL$1" ("TABLE_B"."B_ID"))
      LEADING(@"SEL$2BFA4EE4" "TABLE_A"@"SEL$1" "TABLE_B"@"SEL$1")
      USE_NL(@"SEL$2BFA4EE4" "TABLE_B"@"SEL$1")
      END_OUTLINE_DATA
  */

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

   3 - access(("TABLE_A"."A_ID"=1 OR "TABLE_A"."A_ID"=3 OR "TABLE_A"."A_ID"=4))
   5 - access("A_ID"="B_ID")
       filter(("B_ID"=1 OR "B_ID"=3 OR "B_ID"=4))

Note
-----
   - dynamic statistics used: dynamic sampling (level=AUTO)
   - statistics feedback used for this statement
   - this is an adaptive plan


58 rows selected.

到目前为止我发现的结果是正确的,如果

  • 上述陈述在较低的Oracle版本
  • 上执行
  • 我不查询col_1
  • 省略where子句
  • and (b_id is null or b_id=1)被添加到where子句
  • 我从table_b
  • 中删除了主键

如果没有col_1,则会有以下执行计划信息(产生正确的结果):

SQL> select a_id, b_id
    from table_a left outer join table_b on a_id=b_id
    where a_id in (1 , 3 , 4);

      A_ID       B_ID
---------- ----------
         1          1
         3
         4

SQL> select * from table( dbms_xplan.display_cursor( null, null, '+OUTLINE' ) );

PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  cnycu7vr2k975, child number 0
-------------------------------------
select a_id, b_id     from table_a left outer join table_b on a_id=b_id
    where a_id in (1 , 3 , 4)

Plan hash value: 2928418244

------------------------------------------------------------------------------------
| Id  | Operation           | Name         | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |              |       |       |     2 (100)|          |
|   1 |  NESTED LOOPS OUTER |              |     3 |    78 |     2   (0)| 00:00:01 |
|   2 |   INLIST ITERATOR   |              |       |       |            |          |
|*  3 |    INDEX UNIQUE SCAN| SYS_C0013651 |     3 |    39 |     1   (0)| 00:00:01 |
|*  4 |   INDEX UNIQUE SCAN | SYS_C0013653 |     1 |    13 |     1   (0)| 00:00:01 |
------------------------------------------------------------------------------------

Outline Data
-------------

  /*+
      BEGIN_OUTLINE_DATA
      IGNORE_OPTIM_EMBEDDED_HINTS
      OPTIMIZER_FEATURES_ENABLE('12.1.0.2')
      DB_VERSION('12.1.0.2')
      OPT_PARAM('optimizer_dynamic_sampling' 11)
      OPT_PARAM('optimizer_index_cost_adj' 20)
      OPT_PARAM('optimizer_index_caching' 90)
      ALL_ROWS
      OUTLINE_LEAF(@"SEL$2BFA4EE4")
      MERGE(@"SEL$8812AA4E")
      OUTLINE(@"SEL$948754D7")
      ANSI_REARCH(@"SEL$2")
      OUTLINE(@"SEL$8812AA4E")
      ANSI_REARCH(@"SEL$1")
      OUTLINE(@"SEL$2")
      OUTLINE(@"SEL$1")
      INDEX(@"SEL$2BFA4EE4" "TABLE_A"@"SEL$1" ("TABLE_A"."A_ID"))
      INDEX(@"SEL$2BFA4EE4" "TABLE_B"@"SEL$1" ("TABLE_B"."B_ID"))
      LEADING(@"SEL$2BFA4EE4" "TABLE_A"@"SEL$1" "TABLE_B"@"SEL$1")
      USE_NL(@"SEL$2BFA4EE4" "TABLE_B"@"SEL$1")
      END_OUTLINE_DATA
  */

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

   3 - access(("TABLE_A"."A_ID"=1 OR "TABLE_A"."A_ID"=3 OR
              "TABLE_A"."A_ID"=4))
   4 - access("A_ID"="B_ID")
       filter(("B_ID"=1 OR "B_ID"=3 OR "B_ID"=4))

Note
-----
   - dynamic statistics used: dynamic sampling (level=AUTO)

如果没有主键约束但包括col_1,则会有以下执行计划信息(也会产生正确的结果):

SQL> alter table table_b drop primary key;

Table altered.

SQL> select a_id, b_id, col_1
    from table_a left outer join table_b on a_id=b_id
    where a_id in (1 , 3 , 4);

      A_ID       B_ID      COL_1
---------- ---------- ----------
         1          1        100
         3
         4

SQL> select * from table( dbms_xplan.display_cursor( null, null, '+OUTLINE' ) );

PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  6wg8b65y25utv, child number 2
-------------------------------------
select a_id, b_id, col_1     from table_a left outer join table_b on
a_id=b_id     where a_id in (1 , 3 , 4)

Plan hash value: 3493943395

------------------------------------------------------------------------------------
| Id  | Operation           | Name         | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |              |       |       |     4 (100)|          |
|*  1 |  HASH JOIN OUTER    |              |     3 |   117 |     4   (0)| 00:00:01 |
|   2 |   INLIST ITERATOR   |              |       |       |            |          |
|*  3 |    INDEX UNIQUE SCAN| SYS_C0013651 |     3 |    39 |     1   (0)| 00:00:01 |
|*  4 |   TABLE ACCESS FULL | TABLE_B      |     1 |    26 |     3   (0)| 00:00:01 |
------------------------------------------------------------------------------------

Outline Data
-------------

  /*+
      BEGIN_OUTLINE_DATA
      IGNORE_OPTIM_EMBEDDED_HINTS
      OPTIMIZER_FEATURES_ENABLE('12.1.0.2')
      DB_VERSION('12.1.0.2')
      OPT_PARAM('optimizer_dynamic_sampling' 11)
      OPT_PARAM('optimizer_index_cost_adj' 20)
      OPT_PARAM('optimizer_index_caching' 90)
      ALL_ROWS
      OUTLINE_LEAF(@"SEL$2BFA4EE4")
      MERGE(@"SEL$8812AA4E")
      OUTLINE(@"SEL$948754D7")
      ANSI_REARCH(@"SEL$2")
      OUTLINE(@"SEL$8812AA4E")
      ANSI_REARCH(@"SEL$1")
      OUTLINE(@"SEL$2")
      OUTLINE(@"SEL$1")
      INDEX(@"SEL$2BFA4EE4" "TABLE_A"@"SEL$1" ("TABLE_A"."A_ID"))
      FULL(@"SEL$2BFA4EE4" "TABLE_B"@"SEL$1")
      LEADING(@"SEL$2BFA4EE4" "TABLE_A"@"SEL$1" "TABLE_B"@"SEL$1")
      USE_HASH(@"SEL$2BFA4EE4" "TABLE_B"@"SEL$1")
      END_OUTLINE_DATA
  */

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

   1 - access("A_ID"="B_ID")
   3 - access(("TABLE_A"."A_ID"=1 OR "TABLE_A"."A_ID"=3 OR
              "TABLE_A"."A_ID"=4))
   4 - filter(("B_ID"=1 OR "B_ID"=3 OR "B_ID"=4))

Note
-----
   - dynamic statistics used: dynamic sampling (level=AUTO)


56 rows selected.

我有强烈的感觉,Oracles优化器在某种程度上错误地配置错误,以至于查询结果是错误的。但不幸的是,我无法直接访问它。

问题:上面的选择查询结果是否正确?如果没有,我需要更改哪些优化器设置才能获得正确的结果?

2 个答案:

答案 0 :(得分:0)

我得到以下结果:

      A_ID       B_ID      COL_1
---------- ---------- ----------
         1          1        100
         3
         4

......我认为这是正确答案。

您可以运行查询,然后立即运行以下内容并发布输出:

select * from table( dbms_xplan.display_cursor( null, null, '+OUTLINE' ) );

答案 1 :(得分:0)

总而言之,我将这个答案发布到我自己的问题上,这可以解决它。

如果我按照我的问题所示创建表并插入数据,则以下内容会产生错误的结果(我添加了optimizer参数以使我当前的全局设置更加清晰。如果我省略提示,则会返回相同的输出):

SQL> select /*+ OPT_PARAM('optimizer_index_cost_adj' 20) */ a_id, b_id, col_1
    from table_a left outer join table_b on a_id=b_id
    where a_id in (1 , 3 , 4);

      A_ID       B_ID      COL_1
---------- ---------- ----------
         1          1        100
         3          3
         4          4

如果我将优化器参数optimizer_index_cost_adj的值从20(当前在我的Oracle上全局配置)更改为100(默认值),则会显示正确的结果:

SQL> select /*+ OPT_PARAM('optimizer_index_cost_adj' 100) */ a_id, b_id, col_1
    from table_a left outer join table_b on a_id=b_id
    where a_id in (1 , 3 , 4);

      A_ID       B_ID      COL_1
---------- ---------- ----------
         1          1        100
         3
         4

唯一令我困惑的是,没有人能够重现这个问题。所以似乎还有更多东西......