在实施计划的发展过程,并选择不同任务的转换;事实上,通过语法和查询中的权限检查,第一个被称为查询转换步骤,将有一系列的查询块的转换,那么优化器(最好是为了确定不同的计划选择的最终计算成本计划的最终方案)。

我们知道查询块是通过选择关键字来区分的。查询写的方式决定了查询块之间的关系。每个查询块通常嵌入在另一个查询块中或以某种方式连接。

复制代码代码如下所示:

选择*从员工那里department_id在(选择department_id部门)



它们是嵌套的查询块,但是它们的目的是探究如果查询改变了,是否提供了更好的查询计划。

查询转换步骤是完全透明的用户,可以说知道转换器可能不会改变对你的SQL语句的结构完全重写的情况下查询的结果,所以我们重新评估查询他们的心理预期是必要的,但这种转换总体上是好的,为了得到更有效的执行计划。

现在我们讨论几个基本的转换:

1。视图合并

2。子查询解决嵌套

三.谓词推进

4。物化视图查询重写

一、视图合并

这种方式很容易理解。它将把嵌入式视图扩展为独立的查询块,或者将查询的其余部分与一般的执行计划相结合。转换后的语句基本不包含视图。

视图合并通常发生在外部查询块的谓词包括:

1,可以在另一个查询块的索引中使用的列

2,可以在另一个查询块的分区截断中使用的列

3,可以限制连接视图中返回行数的条件

在这样的查询转换中,视图并不总是有自己的子查询计划,这是预先分析的,通常与查询的其他部分合并以获得性能改进。

复制代码代码如下所示:

SQL > AutoTrace traceonly解释

视图合并

从雇员A,

2(选择department_id员工)b_view

3在a.department_id = b_view.department_id(+)

4、a.salary > 3000;

执行计划

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

计划哈希值:1634680537

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

我的名字|操作| | |行| |字节成本(CPU)时间| |

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

| 0 | SELECT语句| | | 3161 222k | 3(0)| 00:00:01 |

1嵌套循环外| | | | | 3161 222k | 3(0)| 00:00:01 |

2表访问全|员工| * | | | | 103 7107 3(0)| 00:00:01 |

3索引范围扫描| * | | emp_department_ix | | | 31 93 0(0)| 00:00:01 |

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

谓词信息(由操作ID标识):

离开

2过滤器()。工资> > 3000

3 -访问(。department_id=department_id (+))

使用no_merge防止重写的意见

从雇员A,

2(选择 / * * / + no_merge department_id员工b_view)

3在a.department_id = b_view.department_id(+)

4、a.salary > 3000;

执行计划

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

计划哈希值:1526679670

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

我的名字|操作| | |行| |字节成本(CPU)时间| |

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

| 0 | SELECT语句| | | 3161 253k | 7(15)| 00:00:01 |

1哈希连接右外| | * | | | 3161 253k | 7(15)| 00:00:01 |

| 2 |观| | | | 107 1391 3(0)| 00:00:01 |

3表访问全| | |员工| | | 107 321 3(0)| 00:00:01 |

4表访问全| * | |员工| | | 103 7107 3(0)| 00:00:01 |

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

谓词信息(由操作ID标识):

离开

1 -访问(。department_id=b_view 。department_id (+))

4过滤器()。工资> > 3000



在某些情况下,视图合并将被禁止或限制,如果一个查询块中使用的功能,分析聚合函数,并设置操作(如工会、相交、减),在通过的条款,任何一个,这是命令和行号;尽管如此,我们仍然可以使用 / * +合并(V)* /提示强制视图组合使用,但前提必须是确保返回的结果集是一致的!!!以下示例如下:

复制代码代码如下所示:

SQL >集AutoTrace

-使用聚合函数AVG导致视图合并失败

SQL >选择e1.last_name,e1.salary,v.avg_salary

2从hr.employees E1,

3(选择department_id,AVG(工资)avg_salary

4从hr.employees E2

5组department_id)V

6在e1.department_id = v.department_id和e1.salary > v.avg_salary;

执行计划

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

计划哈希值:2695105989

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

我的名字|操作| | |行| |字节成本(CPU)时间| |

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

| 0 | SELECT语句| | | | 17 697 8(25)| 00:00:01 |

|×1 |哈希连接| | | | 17 697 8(25)| 00:00:01 |

| 2 |观| | | | 11 286 4(25)| 00:00:01 |

3哈希组| | | | | | 11 77 4(25)| 00:00:01 |

4表访问全|员工| | | | | 107 749 3(0)| 00:00:01 |

5表访问全| | |员工| | | 107 1605 3(0)| 00:00:01 |

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

谓词信息(由操作ID标识):

离开

1 -访问()。department_id=V。department_id )

筛选器(。工资>V。avg_salary )

合并使用(合并)

SQL >选择合并(V) / / * + e1.last_name,e1.salary,v.avg_salary

2从hr.employees E1,

3(选择department_id,AVG(工资)avg_salary

4从hr.employees E2

5组department_id)V

6在e1.department_id = v.department_id和e1.salary > v.avg_salary;

执行计划

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

计划哈希值:3553954154

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

我的名字|操作| | |行| |字节成本(CPU)时间| |

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

| 0 | SELECT语句| | | | 165 5610 8(25)| 00:00:01 |

|×1 |滤波器| | | | | |

2哈希组| | | | | | 165 5610 8(25)| 00:00:01 |

|×3 |哈希连接| | | 3296 109k | 7(15)| 00:00:01 |

4表访问全|员工| | | | | 107 2889 3(0)| 00:00:01 |

5表访问全|员工| | | | | 107 749 3(0)| 00:00:01 |

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



二、子查询解决嵌套

最典型的方法是对表连接进行子查询。它与视图合并的主要区别在于它的子查询位于WHERE子句中,嵌套检测由转换器完成。

下面是一个子查询表连接示例:

复制代码代码如下所示:

SQL >选择employee_id,last_name,工资,department_id

2从hr.employees

3 department_id在哪里

4(选择department_id)

5从hr.departments哪里location_id > 1700);

执行计划

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

计划哈希值:432925905

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

我的名字|操作| | |行| |字节成本(CPU)时间| |

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

| 0 | SELECT语句| | | | 34 884 4(0)| 00:00:01 |

| 1 |嵌套循环| | | | | |

| 2 |嵌套循环| | | | 34 884 4(0)| 00:00:01 |

| 3表访问的索引rowid |部门| | | | 4 28 2(0)| 00:00:01 |

4索引范围扫描| * | | dept_location_ix | 4 | | 1(0)| 00:00:01 |

5索引范围扫描| * | | emp_department_ix | 10 | | 0(0)| 00:00:01 |

| 6表访问的索引rowid | |员工| | | 10 190 1(0)| 00:00:01 |

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

谓词信息(由操作ID标识):

离开

4访问(location_id> 1700)

5访问(department_id=department_id )

使用 / * * / + no_unnest被迫分开的子查询执行计划生成

SQL >选择employee_id,last_name,工资,department_id

2从hr.employees

3 department_id在哪里

4(选择no_unnest * / / * + department_id

5从hr.departments哪里location_id > 1700);

执行计划

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

计划哈希值:4233807898

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

我的名字|操作| | |行| |字节成本(CPU)时间| |

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

| 0 | SELECT语句| | | | 10 190 14(0)| 00:00:01 |

|×1 |滤波器| | | | | |

2表访问全| | |员工| | | 107 2033 3(0)| 00:00:01 |

| * 3表访问的索引rowid |部门| | | | 1 7 1(0)| 00:00:01 |

4索引唯一扫描| * | | dept_id_pk | 1 | | 0(0)| 00:00:01 |

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

谓词信息(由操作ID标识):

离开

1过滤器(存在(选择 / * + no_unnest×0从人力资源部门。

部门,department_id=:B1和location_id> 1700))

3过滤器(location_id> 1700)

4访问(department_id=:B1)



你可以看到任何嵌套查询子查询的解决方案只使用滤波器两表,第一步的查询谓词的信息并没有变化,这意味着107排的雇员表中返回的每一行必须执行子查询。尽管在Oracle的子查询缓存优化我们不能判断两者的计划的缺点,但相比于嵌套循环,过滤操作的缺点是很明显的。

如果有相关的子查询,嵌套过程通常将相关子查询转换成非嵌套视图,然后与主查询中的表x连接,例如:

复制代码代码如下所示:

SQL >选择outer.employee_id,outer.last_name,outer.salary,outer.department_id

2从hr.employees外

3在outer.salary >

4(选择AVG(内部工资))

5从hr.employees内

6在inner.department_id =外department_id);

执行计划

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

计划哈希值:2167610409

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

我的名字|操作| | |行| |字节成本(CPU)时间| |

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

| 0 | SELECT语句| | | | 17 765 8(25)| 00:00:01 |

|×1 |哈希连接| | | | 17 765 8(25)| 00:00:01 |

| 2 |观| vw_sq_1 | | | 11 286 4(25)| 00:00:01 |

3哈希组| | | | | | 11 77 4(25)| 00:00:01 |

4表访问全|员工| | | | | 107 749 3(0)| 00:00:01 |

5表访问全| | |员工| | | 107 2033 3(0)| 00:00:01 |

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

谓词信息(由操作ID标识):

离开

1访问(item_1=外。department_id )

过滤器(外部)。工资>AVG(内部工资)



上面的查询是将查询到一个哈希视图与主查询,以及查询转换后实际上是这样的:

复制代码代码如下所示:

SQL >选择outer.employee_id,outer.last_name,outer.salary,outer.department_id

2从hr.employees外,

3(选择department_id,AVG(工资)的department_id hr.employees组从avg_sal)内

4在inner.department_id = outer.department_id和outer.salary > inner.avg_sal;



事实上,这两份声明的执行计划也是一致的。

三,谓词向前推

将谓词从内部查询块推入非联合查询块,使谓词条件更早被选择,提前过滤不必要的数据行,提高效率。它还允许一些索引以这种方式使用。

复制代码代码如下所示:

谓词向前推的一个例子

SQL > AutoTrace traceonly解释

SQL >选择e1.last_name,e1.salary,v.avg_salary

2从hr.employees E1,

3(选择department_id,AVG(工资)avg_salary

4从hr.employees E2

5组department_id)V

6在e1.department_id = v.department_id

7、e1.salary > v.avg_salary

8、e1.department_id = 60;

执行计划

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

计划哈希值:3521487559

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

我的名字|操作| | |行| |字节成本(CPU)时间| |

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

| 0 | SELECT语句| | | | 1 41 3(0)| 00:00:01 |

| 1 |嵌套循环| | | | | |

| 2 |嵌套循环| | | | 1 41 3(0)| 00:00:01 |

| 3 |观| | | | 1 26 2(0)| 00:00:01 |

4哈希组| | | | | | 1 7 2(0)| 00:00:01 |

| 5表访问的索引rowid |员工| | | | 5 35 2(0)| 00:00:01 |

6索引范围扫描| * | | emp_department_ix | 5 | | 1(0)| 00:00:01 |

7索引范围扫描| * | | emp_department_ix | 5 | | 0(0)| 00:00:01 |

| * 8表访问的索引rowid | |员工| | | 1 15 1(0)| 00:00:01 |

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

谓词信息(由操作ID标识):

离开

6访问(department_id= 60)

7 -访问()。department_id= 60)

8 -过滤器()。工资>V。avg_salary )

非谓语前向推进

SQL >选择e1.last_name,e1.salary,v.avg_salary

2从hr.employees E1,

3(选择department_id,AVG(工资)avg_salary

4从hr.employees E2

5在rownum > 1行号等于no_merge和no_push_pred提示同时使用,禁用视图合并和谓词推进

6组department_id)V

7在e1.department_id = v.department_id

8、e1.salary > v.avg_salary

9、e1.department_id = 60;

执行计划

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

计划哈希值:3834222907

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

我的名字|操作| | |行| |字节成本(CPU)时间| |

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

| 0 | SELECT语句| | | | 3 123 7(29)| 00:00:01 |

|×1 |哈希连接| | | | 3 123 7(29)| 00:00:01 |

| 2表访问的索引rowid |员工| | | | 5 75 2(0)| 00:00:01 |

3索引范围扫描| * | | emp_department_ix | 5 | | 1(0)| 00:00:01 |

|×4 |观| | | | 11 286 4(25)| 00:00:01 |

5哈希组| | | | | | 11 77 4(25)| 00:00:01 |

| 6 |计数| | | | | |

|×7 |滤波器| | | | | |

8表访问全| | |员工| | | 107 749 3(0)| 00:00:01 |

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

谓词信息(由操作ID标识):

离开

1 -访问()。department_id=V。department_id )

筛选器(。工资>V。avg_salary )

3 -访问()。department_id= 60)

4过滤器(。department_id= 60)

7过滤器(rownum > 1)



两个以上查询可以在第一个查询看到,的department_id = 60谓词推入在V的观点,这使得内部视图查询只需要获得了60的平均工资系数;而平均工资计算每个部门第二查询,然后使用department_id = 60与过滤器查询连接的外部条件,相对来说在这里等待查询谓词中的应用,并做更多的工作。

四。使用物化视图进行查询重写

作为一个物化视图中打开查询重写,CBO优化器将评估相应的查询的基表和视图的访问成本,如果优化器从视图的查询结果可以得到更有效的,那么它将对物化视图进行自动选择,或对基表的查询计划生成。

或板栗:

复制代码代码如下所示:

SQL > AutoTrace traceonly解释

SQL >选择department_id,计数(employee_id)从员工的department_id组;

执行计划

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

计划哈希值:1192169904

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

我的名字|操作| | |行| |字节成本(CPU)时间| |

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

| 0 | SELECT语句| | | | 11 33 4(25)| 00:00:01 |

1哈希组| | | | | | 11 33 4(25)| 00:00:01 |

2表访问全|员工| | | | | 107 321 3(0)| 00:00:01 |

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

-创建物化视图日志

在序列中创建实化视图日志,

2(employee_id rowid,department_id)包括新的价值观;

创建实体化视图日志。

-创建物化视图并指定查询重写功能

SQL >创建物化视图mv_t

2在提交时立即刷新

3启用查询重写

4选择department_id,计数(employee_id)从员工的department_id组;

创建实体化视图。

SQL >选择department_id,计数(employee_id)从员工的department_id组;

执行计划

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

计划哈希值:1712400360

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

我的名字|操作| | |行| |字节成本(CPU)时间| |

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

| 0 | SELECT语句| | | | 12 312 3(0)| 00:00:01 |

| 1 mat_view重写访问全| mv_t | | | | 12 312 3(0)| 00:00:01 |

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





用于此语句的动态采样(级别= 2)



可以在第二个查询中看到,虽然查询指定了员工表,但是优化器自动选择了物化视图的执行路径,因为它判断物化视图已经记录了当前查询结果需要设置的数据,直接访问物化视图的效率更高。

值得注意的是,物化视图的查询重写这是自动的,也可以使用 / * +重写(mv_t)*提示方式强制查询重写。

总结:

虽然查询优化器重写我们的用户结构在透明的情况下,但通常都是基于CBO优化模式的判断更加有效率的选择,这是我们所期望的,但也为我们提供了一个学习方法,是时候考虑在编写SQL语句的过程优化的作用。