Oracle CBO几种基本的查询转换详解
在执行计划的开发过程中,转换和选择有这个不同的任务;实际上,在一个查询进行完语法和权限检查后,首先发生通称为“查询转换”的步骤,这里会进行一系列查询块的转换,然后才是“优选”(优化器为了决定最终的执行计划而为不同的计划计算成本从而选择最终的执行计划)。
我们知道查询块是以SELECT关键字区分的,查询的书写方式决定了查询块之间的关系,各个查询块通常都是嵌在另一个查询块中或者以某种方式与其相联结;例如:
select*fromemployeeswheredepartment_idin(selectdepartment_idfromdepartments)
就是嵌套的查询块,不过它们的目的都是去探索如果改变查询写法会不会提供更好的查询计划。
这种查询转换的步骤对于执行用户可以说是完全透明的,要知道转换器可能会在不改变查询结果集的情况下完全改写你的SQL语句结构,因此我们有必要重新评估自己的查询语句的心理预期,尽管这种转换通常来说都是好事,为了获得更好更高效的执行计划。
我们现在来讨论一下几种基本的转换:
1.视图合并
2.子查询解嵌套
3.谓语前推
4.物化视图查询重写
一、视图合并
这种方式比较容易理解,它会将内嵌的视图展开成一个独立处理的查询块,或者将其与查询剩余部分合并成一个总的执行计划,转换后的语句基本上不包含视图了。
视图合并通常发生在当外部查询块的谓语包括:
1,能够在另一个查询块的索引中使用的列
2,能够在另一个查询块的分区截断中所使用的列
3,在一个联结视图能够限制返回行数的条件
在这种查询器的转换下,视图并不总会有自己的子查询计划,它会被预先分析并通常情况下与查询的其他部分合并以获得性能的提升,如下例。
SQL>setautotracetraceonlyexplain --进行视图合并 SQL>select*fromEMPLOYEESa, 2 (selectDEPARTMENT_IDfromEMPLOYEES)b_view 3 wherea.DEPARTMENT_ID=b_view.DEPARTMENT_ID(+) 4 anda.SALARY>3000;
ExecutionPlan ---------------------------------------------------------- Planhashvalue:1634680537
---------------------------------------------------------------------------------------- |Id |Operation |Name |Rows |Bytes|Cost(%CPU)|Time | ---------------------------------------------------------------------------------------- | 0|SELECTSTATEMENT | | 3161| 222K| 3 (0)|00:00:01| | 1| NESTEDLOOPSOUTER| | 3161| 222K| 3 (0)|00:00:01| |* 2| TABLEACCESSFULL|EMPLOYEES | 103| 7107| 3 (0)|00:00:01| |* 3| INDEXRANGESCAN|EMP_DEPARTMENT_IX| 31| 93| 0 (0)|00:00:01| ----------------------------------------------------------------------------------------
PredicateInformation(identifiedbyoperationid): ---------------------------------------------------
2-filter("A"."SALARY">3000) 3-access("A"."DEPARTMENT_ID"="DEPARTMENT_ID"(+))
--使用NO_MERGE防止视图被重写 SQL>select*fromEMPLOYEESa, 2 (select/*+NO_MERGE*/DEPARTMENT_IDfromEMPLOYEES)b_view 3 wherea.DEPARTMENT_ID=b_view.DEPARTMENT_ID(+) 4 anda.SALARY>3000;
ExecutionPlan ---------------------------------------------------------- Planhashvalue:1526679670
----------------------------------------------------------------------------------- |Id |Operation |Name |Rows |Bytes|Cost(%CPU)|Time | ----------------------------------------------------------------------------------- | 0|SELECTSTATEMENT | | 3161| 253K| 7 (15)|00:00:01| |* 1| HASHJOINRIGHTOUTER| | 3161| 253K| 7 (15)|00:00:01| | 2| VIEW | | 107| 1391| 3 (0)|00:00:01| | 3| TABLEACCESSFULL |EMPLOYEES| 107| 321| 3 (0)|00:00:01| |* 4| TABLEACCESSFULL |EMPLOYEES| 103| 7107| 3 (0)|00:00:01| -----------------------------------------------------------------------------------
PredicateInformation(identifiedbyoperationid): ---------------------------------------------------
1-access("A"."DEPARTMENT_ID"="B_VIEW"."DEPARTMENT_ID"(+)) 4-filter("A"."SALARY">3000)