【发布时间】:2017-11-06 16:11:16
【问题描述】:
连接两个表时,连接消除工作正常:
SQL> set lines 200;
SQL>
SQL> select * from v$version;
BANNER CON_ID
-------------------------------------------------------------------------------- ----------
Oracle Database 12c Release 12.1.0.1.0 - 64bit Production 0
PL/SQL Release 12.1.0.1.0 - Production 0
CORE 12.1.0.1.0 Production 0
TNS for Linux: Version 12.1.0.1.0 - Production 0
NLSRTL Version 12.1.0.1.0 - Production 0
SQL>
SQL> create table t01 (
2 id integer,
3 apk varchar2(255 char),
4 constraint pk_01 primary key (id)
5 );
Tabelle wurde erstellt.
SQL> create table t02 (
2 id integer,
3 apk varchar2(255 char),
4 id_t01 integer,
5 constraint pk_02 primary key (id),
6 constraint fk_02 foreign key (id_t01) references t01(id)
7 );
Tabelle wurde erstellt.
SQL> create table t03 (
2 id integer,
3 apk varchar2(255 char),
4 id_t02 integer,
5 constraint pk_03 primary key (id),
6 constraint fk_03 foreign key (id_t02) references t02(id)
7 );
Tabelle wurde erstellt.
SQL> create index ix_t03 on t03(id_t02);
Index wurde erstellt.
SQL> create index ix_t02 on t02(id_t01);
Index wurde erstellt.
SQL> insert into t01 (id, apk)
2 select level, to_char(level)
3 from dual
4 connect by level <= 1000;
1000 Zeilen erstellt.
SQL> insert into t02(id, apk, id_t01)
2 select id, apk, id from t01;
1000 Zeilen erstellt.
SQL> insert into t03(id, apk, id_t02)
2 select id, apk, id from t01;
1000 Zeilen erstellt.
SQL> commit;
Transaktion mit COMMIT abgeschlossen.
SQL>
SQL> exec dbms_stats.gather_table_stats(null, 'T01', method_opt=>'for all columns size skewonly', cascade=>true);
PL/SQL-Prozedur erfolgreich abgeschlossen.
SQL> exec dbms_stats.gather_table_stats(null, 'T02', method_opt=>'for all columns size skewonly', cascade=>true);
PL/SQL-Prozedur erfolgreich abgeschlossen.
SQL> exec dbms_stats.gather_table_stats(null, 'T03', method_opt=>'for all columns size skewonly', cascade=>true);
PL/SQL-Prozedur erfolgreich abgeschlossen.
SQL> commit;
Transaktion mit COMMIT abgeschlossen.
SQL>
SQL> set autotrace traceonly explain;
SQL>
SQL> select t02.id
2 from t02
3 left join t01 on t01.id = t02.id_t01;
Ausführungsplan
----------------------------------------------------------
--------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost |
--------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1000 | 8000 | 2 |
| 1 | INDEX FAST FULL SCAN| PK_02 | 1000 | 8000 | 2 |
--------------------------------------------------------------
--> 对 t02 的主键索引进行快速全扫描,不读取 t01。这是我所期待的。
加入 t02 和 t03 也可以正常工作:
SQL>
SQL> select t03.id
2 from t03
3 left join t02 on t02.id = t03.id_t02;
Ausführungsplan
----------------------------------------------------------
--------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost |
--------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1000 | 8000 | 2 |
| 1 | INDEX FAST FULL SCAN| PK_03 | 1000 | 8000 | 2 |
--------------------------------------------------------------
--> 对 t03 的主键索引进行快速全扫描,不读取 t02。这是我所期待的。
当我尝试加入 t01、t02 和 t03 时出现问题:
SQL> select t03.id
2 from t03
3 left join t02 on t02.id = t03.id_t02
4 left join t01 on t01.id = t02.id_t01;
Ausführungsplan
----------------------------------------------------------
------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost |
------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1000 | 16000 | 18 |
| 1 | NESTED LOOPS OUTER| | 1000 | 16000 | 18 |
| 2 | TABLE ACCESS FULL| T03 | 1000 | 8000 | 18 |
| 3 | INDEX UNIQUE SCAN| PK_02 | 1 | 8 | 0 |
------------------------------------------------------------
我希望(仅)在此处对 pk_03 进行全索引扫描,但执行计划在 T03 和 PK02 之间执行嵌套循环。
我做错了什么?我有错误的期望吗?我在 Oracle 文档/stackoverflow/google 中找不到任何解释此行为的内容。
我正在使用的实际数据库确实有更多的列/表等,这只是一个最小的例子。当加入 20 个表并且没有发生预期的连接消除时,问题会变得更糟。这对我们的查询执行时间有相当负面的影响。
非常感谢。
【问题讨论】:
-
优化器必须认为查询计划将比替代方案更有效地执行。你确定这是一个问题吗?如果是这样,您是否尝试过使用查询提示,例如没有嵌套循环等?
-
对 pk_03 进行快速全扫描的代价为 2(参见 t02 和 t03 之间的连接),t01、t02 和 t03 之间的连接的执行计划的代价为 18。这正是我不明白,为什么不选择成本较低的计划= 2?我的生产数据库中的实际查询表示为一个视图,该视图被许多其他查询使用;虽然我可以尝试调整这个特定示例,但我无法对所有可能的查询组合使用查询提示。
-
您的代码在版本 12.1.0.2 中按预期工作。我建议检查 support.oracle.com。
-
@Matthew McPeak 看起来这个问题实际上与 oracle 版本有关:它在 Oracle 12.2.0.1.0 和 11.2.0.4.0 中按预期工作 - 尚未检查 12.1.0.2。谢谢!
标签: oracle sqlperformance