Oracle12 外连接发明列值

Oracle12 outer join invents column values

如果我在 Oracle12 数据库上执行以下语句,我会得到一个我无法解释的结果:

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 不应存在,因为 table 仅包含值 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(@"SELBFA4EE4")
      MERGE(@"SEL12AA4E")
      OUTLINE(@"SEL8754D7")
      ANSI_REARCH(@"SEL")
      OUTLINE(@"SEL12AA4E")
      ANSI_REARCH(@"SEL")
      OUTLINE(@"SEL")
      OUTLINE(@"SEL")
      INDEX(@"SELBFA4EE4" "TABLE_A"@"SEL" ("TABLE_A"."A_ID"))
      INDEX_RS_ASC(@"SELBFA4EE4" "TABLE_B"@"SEL" ("TABLE_B"."B_ID"))
      LEADING(@"SELBFA4EE4" "TABLE_A"@"SEL" "TABLE_B"@"SEL")
      USE_NL(@"SELBFA4EE4" "TABLE_B"@"SEL")
      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.

目前我查到的结果是正确的,如果

没有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(@"SELBFA4EE4")
      MERGE(@"SEL12AA4E")
      OUTLINE(@"SEL8754D7")
      ANSI_REARCH(@"SEL")
      OUTLINE(@"SEL12AA4E")
      ANSI_REARCH(@"SEL")
      OUTLINE(@"SEL")
      OUTLINE(@"SEL")
      INDEX(@"SELBFA4EE4" "TABLE_A"@"SEL" ("TABLE_A"."A_ID"))
      INDEX(@"SELBFA4EE4" "TABLE_B"@"SEL" ("TABLE_B"."B_ID"))
      LEADING(@"SELBFA4EE4" "TABLE_A"@"SEL" "TABLE_B"@"SEL")
      USE_NL(@"SELBFA4EE4" "TABLE_B"@"SEL")
      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(@"SELBFA4EE4")
      MERGE(@"SEL12AA4E")
      OUTLINE(@"SEL8754D7")
      ANSI_REARCH(@"SEL")
      OUTLINE(@"SEL12AA4E")
      ANSI_REARCH(@"SEL")
      OUTLINE(@"SEL")
      OUTLINE(@"SEL")
      INDEX(@"SELBFA4EE4" "TABLE_A"@"SEL" ("TABLE_A"."A_ID"))
      FULL(@"SELBFA4EE4" "TABLE_B"@"SEL")
      LEADING(@"SELBFA4EE4" "TABLE_A"@"SEL" "TABLE_B"@"SEL")
      USE_HASH(@"SELBFA4EE4" "TABLE_B"@"SEL")
      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.

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

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

我得到以下结果:

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

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

你能运行查询吗,然后紧接着,运行下面,post输出:

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

总而言之,我 post 这个对我自己的问题的回答,可以帮我解决。

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

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

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