Oracle约束Constraint对于CBO优化器的作用(3)

5、外键“想当然”

外键Foreign Key是维持参照完整性的重要手段。对CBO而言,Foreign Key意味着取值规则上的差异,也会影响落实到执行计划中。

首先创建实验环境。

SQL> create table t_master (id number, mas_name varchar2(100));

Table created

SQL> create table t_child (cid number, id number, chi_name varchar2(100));

Table created

SQL> alter table t_master add constraint pk_t_master primary key (id);

Table altered

SQL> alter table t_child add constraint pk_t_child primary key (cid);

Table altered

SQL> alter table t_child add constraint fk_t_child_master foreign key (id) references t_master(id);
 
Table altered

--收集统计量

SQL> exec dbms_stats.gather_table_stats(user, 'T_MASTER', cascade => true);

PL/SQL procedure successfully completed

SQL> exec dbms_stats.gather_table_stats(user, 'T_CHILD', cascade => true);

PL/SQL procedure successfully completed

我们研究一个SQL语句,讨论查找不存在主表中的子表记录。

SQL> explain plan for select * from t_child where id not in (select id from t_master);
 
 

Explained

SQL> select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT

--------------------------------------------------------------------------------
 
Plan hash value: 1412281931

--------------------------------------------------------------------------------
 
| Id  | Operation          | Name        | Rows  | Bytes | Cost (%CPU)| Time

--------------------------------------------------------------------------------
 
|  0 | SELECT STATEMENT  |            |    1 |    91 |    3  (34)| 00:00:01
 
|*  1 |  HASH JOIN ANTI SNA|            |    1 |    91 |    3  (34)| 00:00:01
 
|  2 |  TABLE ACCESS FULL| T_CHILD    |    1 |    78 |    2  (0)| 00:00:01
 
|  3 |  INDEX FULL SCAN  | PK_T_MASTER |    1 |    13 |    0  (0)| 00:00:01
 
--------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):

---------------------------------------------------

1 - access("ID"="ID")

15 rows selected

Not in的标准过程是使用反连接动作,上面的执行计划也证明了这点。但是,这个执行计划没有考虑外键因素。在外键约束中,外键列上是不可能出现不是空值的非主键值。Not in最大的警惕点在于是否有空。
 
我们调节外键列的属性约束,取得新的执行计划。

SQL> alter table t_child modify id not null;

Table altered

SQL> explain plan for select * from t_child where id not in (select id from t_master);
 
 

Explained

SQL> select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT

--------------------------------------------------------------------------------
 
Plan hash value: 2639974602

------------------------------------------------------------------------------

| Id  | Operation          | Name    | Rows  | Bytes | Cost (%CPU)| Time    |

------------------------------------------------------------------------------

|  0 | SELECT STATEMENT  |        |    1 |    78 |    0  (0)|          |

|*  1 |  FILTER            |        |      |      |            |          |

|  2 |  TABLE ACCESS FULL| T_CHILD |    1 |    78 |    2  (0)| 00:00:01 |

------------------------------------------------------------------------------

Predicate Information (identified by operation id):

---------------------------------------------------

1 - filter(NULL IS NOT NULL)

14 rows selected

成本值是0!执行计划中的确有对子表的FTS动作,但是上面的那个filter,引用了一个条件:null is not null——永假式。

SQL> select 1 from dual where NULL IS NOT NULL;

1

----------

Oracle在承认子表id非空之后,直接判定这个SQL是不会有任何结果的。索性执行给一个永假式,返回0行记录。

这种取巧路径在RBO时代,也是不存在的。

SQL> explain plan for select /*+rule*/* from t_child where id not in (select id from t_master);
 
 

Explained

SQL> select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT

--------------------------------------------------------------------------------
 
Plan hash value: 2659151261

------------------------------------------

| Id  | Operation          | Name        |

------------------------------------------

|  0 | SELECT STATEMENT  |            |

|*  1 |  FILTER            |            |

|  2 |  TABLE ACCESS FULL| T_CHILD    |

|*  3 |  INDEX UNIQUE SCAN| PK_T_MASTER |

------------------------------------------

Predicate Information (identified by operation id):

---------------------------------------------------

1 - filter( NOT EXISTS (SELECT 0 FROM "T_MASTER" "T_MASTER" WHERE

"ID"=:B1))

3 - access("ID"=:B1)

Note

-----

PLAN_TABLE_OUTPUT

--------------------------------------------------------------------------------
 
  - rule based optimizer used (consider using cbo)

21 rows selected

6、结论

执行计划是SQL语句执行效率的命脉。如何生成更快速的执行计划,不仅是Oracle,更是所有优化器追求的方向目标。在CBO时代,Oracle放弃了RBO时候简单15个等级的策略,应用成本Cost度量执行计划策略,统计量+内核公式构成了优化器比较基础。
 
除此之外,我们说数据库对象的描述,特别是约束起到更加重要的作用。本篇中,我们介绍了几个关键场景下,约束对于执行计划的作用。从直观上看,约束描述可以帮助CBO找到各种“取巧”路径。这实际上就反映了约束对于数据库的描述作用,生成高效执行计划。
 
对于开发设计人员,这个结论很重要。实际工作中,不少开发人员忽视约束的作用。不愿意进行字段非空讨论,不进行适度外键设计,这些都是需要我们借鉴的方面。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/2ebbccce9e8c3937dfb85469d793ca85.html