标量子查询 外表有多少重复值决定filter次数 转化 left join
1. 标量子查询的执行计划
SORT 和 Table 是齐平的,上面没有类似HASH JOIN之类的连接方式。
2. 外表驱动内表的时候 starts 代表执行次数,由内表的distinct值决定的,相当于内表做了一个view
3. 内表做的view,可以直接和外表做外链接,这样可以改为join,12C 后可以自动转化了。
---------标量子查询 case when 用 left join 的改写-----
--Oracle标量子查询执行计划,性能为王:SQL标量子查询的优化案例分析-CSDN博客
SELECT
CASE WHEN OFFER_SPEC_ID IN ----A的distinct OFFER_SPEC_ID 值决定循环次数
(SELECT LOCAL_ITEM_CODE FROM PU_meta_DIM.CODE_ITEM
WHERe PROV_TYPE_ID = 49AND PROV_ITEM_ID = 64 改写成view )
THEN 1 ELSE 0 END
FROM PU_base_IND.DM_SP_SUB_OFFER_SERV_D_201407
PARTITION(P20140727) A
WHERe LOCAL_CODE = '028'
SELECT
CASE WHEN a.OFFER_SPEC_ID = b.LOCAL_ITEM_CODE THEN 1 ELSE 0 END
FROM PU_base_IND.DM_SP_SUB_OFFER_SERV_D_201407 PARTITION (P20140727) A,
(SELECt DISTINCT LOCAL_ITEM_CODE
FROM PU_meta_DIM.CODE_ITEM
WHERe PROV_TYPE_ID = 49 AND PROV_ITEM_ID = 64) b
AND a.OFFER_SPEC_ID = b.LOCAL_ITEM_CODE(+ )
AND TO_CHAr (a.OFFER_SPEC_ID) = c.OFFER_SPEC_ID(+ )
AND TO_CHAr (a.OFFER_SPEC_ID) = d.OFFER_SPEC_ID(+ )
AND TO_CHAr (a.OFFER_SPEC_ID) = e.LOCAL_ITEM_CODE(+ )
标量子查询特点及特殊执行计划
1.1 标量子查询执行计划
1)标量子查询的计划和普通计划的执行顺序不同,执行计划中标量子查询虽然在上面,但是它由下面的节点驱动,每行驱动查询一次标量子查询,而普通执行计划是下面的节点受上面节点驱动。(具体见下面示例分析)
2)标量子查询和FILTER类似,如果是它们引起的性能问题,要重点关注是否是执行子查询的次数过多导致查询的效率不高。3)标量子查询和FILTER类似,它会根据输入和输出构建HASH表缓存键值对,已经查询过的键值对直接从缓存中查找,不用再次执行子查询,从而减少子查询的次数达到优化的目的,10g和11g缓存的是255个HASH BUCKETS,12C是1024个HASH BUCKETS。如下所示:
从上面可以看出,标量子查询执行计划和普通执行计划不一样,它上面节点的是被下面节点驱动的,类似FILTER,谓词部分有系统绑定变量:B1,本例中ID 1-9属于标量子查询部分,它由ID=10的节点驱动,ID=10的结果集影响ID 1-9标量子查询的执行次数,标量子查询执行次数还和缓存有关,也就是ID=10的关联条件CUST_ID的DISTINCT数目。
下面探讨下标量子查询特点以及优化方法,对标量子查询的优化首选方式就是改写。
1.2 标量子查询特点
子查询执行次数依赖于关键条件DISTINCT数目,重复值越多,效率越好,反之,效率差。
如下所示:
从上面执行计划可以看出,虽然驱动的A-ROWS有108K行,但是因为distinct数目是11,实际上标量子查询只执行11次,由此可以看出,ORACLE内部构建了缓存存储键值对,从而减少子查询执行次数,达到优化目的。
2
标量子查询优化方法
标量子查询的改写:减少标量子查询次数、改为外连接使用JOIN。
2.1 减少子查询执行次数
2.2 将标量子查询改为外连接
其中改为外连接也是12C对标量子查询优化的新特性。
3
12C标量子查询优化新特性
如前面所示,在12c之前,对于scalary subquery是无法unnest的,这样就导致标量子查询的执行次数受驱动表行数的影响,类似FILTER操作,而且scalary subquery的执行计划和普通执行计划不一样。
select dname, (select max(b.sal) from scott.emp b where b.deptno = a.deptno) from scott.dept a;
12C对标量子查询的优化如下:
12C优化改进其实将标量子查询内部改成OUTER JOIN,从而充分利用JOIN算法进行优化提高效率,注意12C针对标量子查询的优化只对于max,min,avg单个有效(类似max||min等组合的无效),对count、查询列等无效,并且受参数_optimizer_unnest_scalar_sq控制。
----------------------
oracle标量子查询和自己定义函数有时用起来比較方便,并且开发者也常常使用。数据量小还无所谓。数据量大,往往存在性能问题。
下面測试帮助大家彻底搞懂标量子查询。 SQL> create table a (id int,name varchar2(10)); Table created. SQL> create table b (id int,name varchar2(10)); Table created. SQL> insert into a values (1,'a1'); 1 row created. SQL> insert into a values (2,'a2'); 1 row created. SQL> insert into b values (1,'b1'); 1 row created. SQL> insert into b values (2,'b2'); 1 row created. SQL> commit; Commit complete. SQL> @getlvall Session altered. SQL> select a.*,(select name from b where b.id=a.id) from a; ID NAME (SELECTNAMEFROMBWHER ---------- -------------------- -------------------- 1 a1 b1 2 a2 b2 SQL> @getplanspe PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- SQL_ID 8rv825dykpx1m, child number 0 ------------------------------------- select a.*,(select name from b where b.id=a.id) from a Plan hash value: 2657529235 ------------------------------------------------------------------------------------ | Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | ------------------------------------------------------------------------------------ |* 1 | TABLE ACCESS FULL| B | 2 | 1 | 2 |00:00:00.01 | 14 | | 2 | TABLE ACCESS FULL| A | 1 | 2 | 2 |00:00:00.01 | 8 | ------------------------------------------------------------------------------------ Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("B"."ID"=:B1) Note ----- - dynamic sampling used for this statement 22 rows selected.
复制
<strong>--由上面的运行计划能够知道,b表运行2次。返回2行</strong>
SQL> insert into a values (3,'a3'); 1 row created. SQL> commit; Commit complete. SQL> select a.*,(select name from b where b.id=a.id) from a;ID NAME (SELECTNAMEFROMBWHER ---------- -------------------- --------------------1 a1 b12 a2 b23 a3 SQL> @getplanspe PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- SQL_ID 8rv825dykpx1m, child number 0 ------------------------------------- select a.*,(select name from b where b.id=a.id) from a Plan hash value: 2657529235 ------------------------------------------------------------------------------------ | Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | ------------------------------------------------------------------------------------ |* 1 | TABLE ACCESS FULL| B | 3 | 1 | 2 |00:00:00.01 | 21 | | 2 | TABLE ACCESS FULL| A | 1 | 2 | 3 |00:00:00.01 | 8 | ------------------------------------------------------------------------------------ Predicate Information (identified by operation id): ---------------------------------------------------1 - filter("B"."ID"=:B1) Note ------ dynamic sampling used for this statement 22 rows selected.
<strong>--由上面的运行计划能够知道,b表运行3次。返回2行</strong>
SQL> insert into a values (4,'a4'); 1 row created. SQL> insert into a values (5,'a5'); 1 row created. SQL> insert into a values (6,'a6'); 1 row created. SQL> insert into a values (7,'a7'); 1 row created. SQL> insert into a values (8,'a8'); 1 row created. SQL> insert into a values (9,'a9'); 1 row created. SQL> commit; Commit complete. SQL> select a.*,(select name from b where b.id=a.id) from a;ID NAME (SELECTNAMEFROMBWHER ---------- -------------------- --------------------1 a1 b12 a2 b23 a34 a45 a56 a67 a78 a89 a9 9 rows selected. SQL> @getplanspe PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- SQL_ID 8rv825dykpx1m, child number 0 ------------------------------------- select a.*,(select name from b where b.id=a.id) from a Plan hash value: 2657529235 ------------------------------------------------------------------------------------ | Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | ------------------------------------------------------------------------------------ |* 1 | TABLE ACCESS FULL| B | 9 | 1 | 2 |00:00:00.01 | 63 | | 2 | TABLE ACCESS FULL| A | 1 | 2 | 9 |00:00:00.01 | 8 | ------------------------------------------------------------------------------------ Predicate Information (identified by operation id): ---------------------------------------------------1 - filter("B"."ID"=:B1) Note ------ dynamic sampling used for this statement 22 rows selected.
<strong> --由上面的运行计划能够知道,b表运行9次,返回2行</strong>
SQL> update b set name='b1'; 2 rows updated. SQL> commit; Commit complete. SQL> select a.*,(select name from b where b.id=a.id) from a;ID NAME (SELECTNAMEFROMBWHER ---------- -------------------- --------------------1 a1 b12 a2 b13 a34 a45 a56 a67 a78 a89 a9 9 rows selected. SQL> @getplanspe PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- SQL_ID 8rv825dykpx1m, child number 0 ------------------------------------- select a.*,(select name from b where b.id=a.id) from a Plan hash value: 2657529235 ------------------------------------------------------------------------------------ | Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | ------------------------------------------------------------------------------------ |* 1 | TABLE ACCESS FULL| B | 9 | 1 | 2 |00:00:00.01 | 63 | | 2 | TABLE ACCESS FULL| A | 1 | 2 | 9 |00:00:00.01 | 8 | ------------------------------------------------------------------------------------ Predicate Information (identified by operation id): ---------------------------------------------------1 - filter("B"."ID"=:B1) Note ------ dynamic sampling used for this statement 22 rows selected.
<strong>--由上面的运行计划能够知道,b表运行2次,返回2行</strong> 原作者这里应该错了
SQL> insert into b values (3,'b1'); 1 row created. SQL> insert into b values (4,'b1'); 1 row created. SQL> insert into b values (5,'b1'); 1 row created. insert into b values (6,'b1');b1'); 1 row created. SQL> insert into b values (7,'b1'); 1 row created. SQL> insert into b values (8,'b1'); 1 row created. SQL> insert into b values (9,'b1'); 1 row created. SQL> commit; Commit complete. SQL> select a.*,(select name from b where b.id=a.id) from a;ID NAME (SELECTNAMEFROMBWHER ---------- -------------------- --------------------1 a1 b12 a2 b13 a3 b14 a4 b15 a5 b16 a6 b17 a7 b18 a8 b19 a9 b1 9 rows selected. SQL> @getplanspe PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- SQL_ID 8rv825dykpx1m, child number 0 ------------------------------------- select a.*,(select name from b where b.id=a.id) from a Plan hash value: 2657529235 ------------------------------------------------------------------------------------ | Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | ------------------------------------------------------------------------------------ |* 1 | TABLE ACCESS FULL| B | 9 | 1 | 9 |00:00:00.01 | 63 | | 2 | TABLE ACCESS FULL| A | 1 | 2 | 9 |00:00:00.01 | 8 | ------------------------------------------------------------------------------------ Predicate Information (identified by operation id): ---------------------------------------------------1 - filter("B"."ID"=:B1) Note ------ dynamic sampling used for this statement 22 rows selected.
<strong>--b.name字段所有为‘b1’。由上面的运行计划能够知道。b表运行9次。返回9行</strong>
SQL> update a set id=1; 9 rows updated. SQL> commit; Commit complete. SQL> select * from a;ID NAME ---------- --------------------1 a11 a21 a31 a41 a51 a61 a71 a81 a9 9 rows selected. SQL> select * from b;ID NAME ---------- --------------------1 b12 b13 b14 b15 b16 b17 b18 b19 b1 9 rows selected. SQL> select a.*,(select name from b where b.id=a.id) from a;ID NAME (SELECTNAMEFROMBWHER ---------- -------------------- --------------------1 a1 b11 a2 b11 a3 b11 a4 b11 a5 b11 a6 b11 a7 b11 a8 b11 a9 b1 9 rows selected. SQL> @getplanspe PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- SQL_ID 8rv825dykpx1m, child number 0 ------------------------------------- select a.*,(select name from b where b.id=a.id) from a Plan hash value: 2657529235 ------------------------------------------------------------------------------------ | Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | ------------------------------------------------------------------------------------ |* 1 | TABLE ACCESS FULL| B | 1 | 1 | 1 |00:00:00.01 | 7 | | 2 | TABLE ACCESS FULL| A | 1 | 2 | 9 |00:00:00.01 | 8 | ------------------------------------------------------------------------------------ Predicate Information (identified by operation id): ---------------------------------------------------1 - filter("B"."ID"=:B1) Note ------ dynamic sampling used for this statement 22 rows selected. SQL>
关联字段a.id所有为1。a表有9行,标量子查询相当于运行9次select name from b where b.id=1 ,oracle也不傻,starts=1。说明仅仅运行了1次。
所以是看外表有多少重复值决定filter次数的,和内表重复值无关。