在测试USE_CONCAT提示的时候遇到了一个比较奇怪的现象。
SQL> create table t as select rownum id, a.* from dba_objects a;
表已创建。
SQL> exec dbms_stats.gather_table_stats(user, 'T')
PL/SQL 过程已成功完成。
SQL> set autot trace exp
SQL> select * from t where owner = 'SYS' or OBJECT_TYPE = 'PACKAGE';
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=10 Card=846 Bytes=72756)
1 0 TABLE ACCESS (FULL) OF 'T' (Cost=10 Card=846 Bytes=72756)
SQL> select /*+ use_concat */ * from t
2 where owner = 'SYS' or OBJECT_TYPE = 'PACKAGE';
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=10 Card=846 Bytes=72756)
1 0 TABLE ACCESS (FULL) OF 'T' (Cost=10 Card=846 Bytes=72756)
在上面的测试中使用了USE_CONCAT提示,但Oracle并没有根据提示生成相应的执行计划。
下面为T建立两个索引。
SQL> create index ind_t_owner on t(owner);
索引已创建。
SQL> create index ind_t_object_type on t(object_type);
索引已创建。
SQL> select * from t where owner = 'SYS' or OBJECT_TYPE = 'PACKAGE';
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=10 Card=846 Bytes=72756)
1 0 TABLE ACCESS (FULL) OF 'T' (Cost=10 Card=846 Bytes=72756)
SQL> select /*+ use_concat */ * from t
2 where owner = 'SYS' or OBJECT_TYPE = 'PACKAGE';
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=20 Card=273 Bytes=23478)
1 0 CONCATENATION
2 1 TABLE ACCESS (FULL) OF 'T' (Cost=10 Card=31 Bytes=2666)
3 1 TABLE ACCESS (FULL) OF 'T' (Cost=10 Card=31 Bytes=2666)
建立索引后,查询的执行计划没有变。根据OWNER和OBJECT_TYPE的分布,这个结果是正常的。但是加上USE_CONCAT提示后,执行计划由一个全表扫描变为了两个全表扫描的CONCATENATION。
刚看到这个执行计划的时候,我有些迷惑,为什么开始USE_CONCAT不起作用而现在开始起作用了呢?
在建立索引以前,Oracle对T的访问路径只存在一种,即全表扫描。由于Oracle只存在一种执行计划,因此Oracle忽略了USE_CONCAT提示。当建立索引之后,Oracle存在着多种访问路径,包括对T全表扫描和对T的IND_T_OWNER索引扫描以及对T的IND_T_OBJECT_TYPE索引扫描。由于存在多种访问途径,因此优化器按照提示生成相应的计划,优化器根据提示生成执行计划的同时,对于提示中没有给出的部分,优化器会根据代价的大小来确定。而在这个查询中,全表扫描肯定比通过索引扫描代价要小,因此,优化器生成了对T表执行两次全表扫描,然后进行CONCATENATION的执行计划。