Oracle查询转换和SQL连接方式
一、表连接方法:
在Oracle数据库中,几个表甚至十几个表相互关联的语句随处可见,这就需要Oracle提供一整套的表连接方案来支持这种类型的SQL,实际上不管多少个表的连接,Oracle都只能先处理两个表的连接,Oracle会通过优化器依据统计信息选出驱动表和被驱动表,然后完成第一次两表关联,依此类推直至完成整条SQL的表连接。关于Oracle提供的表连接方式有排序合并连接(Sort Merge Join)、嵌套轮换连接(Nested Loops Join)、哈希连接(Hash Join)和笛卡儿连接(Cross Join)四种,其中排序合并和哈希连接是Oracle在10g之后才提供的。
1.排序合并连接(Sort Merge Join)
顾名思义排序合并连接就是将两个表(T1表和T2表)先对结果集进行排序,然后在进行结果集合并。
它的执行过程大致应该是这样的:
①首先对T1表根据谓词条件查询出目标结果集并进行排序,记录为结果集1。
②然后对T2表根据谓词条件查询出目标结果集并进行排序,记录为结果集2。
③最后在把结果集1和结果集2进行合并,为最终结果集。其中这部分操作应该是这样的,首先取出结果集1的第一条记录数在结果集2中进行遍历,由于已经做了排序操作,所以不需要扫描全部结果集,只需要遍历目标数的结果集即可,删除遍历到的结果集2中的记录数,然后从结果集2选取第二条记录数依此遍历。
sort mrege join原理:
BEGIN
FOR OUTER IN (SELECT * FROM PGA里排序好的emp)
LOOP -- outer loop
FOR INNER IN (SELECT * FROM PGA里排序好的dept
WHERE dept.deptno = emp.deptno)
LOOP -- inner loop
dbms_output.put_line( ... );
END LOOP;
END LOOP;
END;
执行计划:
SQL> select /*+ use_merge(a)*/ a.* ,b.loc
2 from scott.emp a ,scott.dept b
3 where a.deptno=b.deptno
4 and empno>'1500';
14 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 817787759
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 14 | 686 | 5 (20)| 00:00:01 |
| 1 | MERGE JOIN | | 14 | 686 | 5 (20)| 00:00:01 |
| 2 | TABLE ACCESS BY INDEX ROWID | DEPT | 4 | 44 | 2 (0)| 00:00:01 |
| 3 | INDEX FULL SCAN | PK_DEPT | 4 | | 1 (0)| 00:00:01 |
|* 4 | SORT JOIN | | 14 | 532 | 3 (34)| 00:00:01 |
| 5 | TABLE ACCESS BY INDEX ROWID| EMP | 14 | 532 | 2 (0)| 00:00:01 |
|* 6 | INDEX RANGE SCAN | PK_EMP | 14 | | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - access("A"."DEPTNO"="B"."DEPTNO")
filter("A"."DEPTNO"="B"."DEPTNO")
6 - access("EMPNO">1500)
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
6 consistent gets
0 physical reads
0 redo size
1689 bytes sent via SQL*Net to client
524 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
14 rows processed
总结:
①对于处理海量数据有较好的支撑,处理过程中outer 和inter只扫描一次到buffer cache中,不可避免存在latch争用,其中的sort操作需要大量消耗pga。
②使用范围比hash join更为广泛,可以在不等式连接中使用。
③连接过程不太依赖索引,但是对于索引排序效果会更快。
2.嵌套轮换连接(Nested Loops Join)
嵌套连接实际上就是if else循环,其中存在驱动表和被驱动表的概念,Oracle会根据一定的规则选择驱动表(在没有SQL指定的情况下Oracle默认选择结果集数较少的表为驱动表,若有SQL指定例如hint,则根据SQL指定来选择具体驱动表)
它的执行过程大致应该是这样的:
①首先Oracle会根据统计信息和实际的SQL语句判断驱动表与被驱动表选择。
②对驱动表T1根据谓词条件查询出目标结果集,记录为结果集1。
③对被驱动表T2根据谓词条件查询出目标结果集,记录为结果集2。
④遍历结果集1,并将遍历的记录数依次在结果集2中遍历,得出最终结果集。实际过程应该是这样的,从结果集1中遍历第一条数,然后在结果集2中全部遍历得到结果,然后选择结果集1中的第二条数依此遍历,这个过程需要对结果集1(驱动表)多次遍历。
netsted loop join 连接原理:
< C , JAVA >
FOR (i = 0; i<100; i++){ -- outer loop
FOR (j = 0; j<100; j++) { -- inner loop
// Do Anything ...
}
}
< PL/SQL >
FOR OUTER IN 1..100 LOOP
FOR INNER IN 1..100 LOOP
dbms_output.put_line(OUTER || ' : ' || INNER);
END LOOP;
END LOOP
PL/SQL代码示例:
BEGIN
FOR OUTER IN (SELECT emp.* FROM emp
where emp.empno>'1500')
LOOP -- outer loop 外循环
FOR INNER IN (SELECT dept.loc FROM dept
WHERE dept.deptno = emp.deptno)
LOOP -- inner loop 内循环
dbms_output.put_line(
outer.职员名 || ' : ' || INNER.客户名 || ' : ' || INNER.电话号);
END LOOP;
END LOOP;
END;
执行计划:
SQL> select /*+ leading(a) use_nl(b)*/ a.* ,b.loc
2 from scott.emp a ,scott.dept b
3 where a.deptno=b.deptno
4 and a.empno>'1500';
14 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 1465018025
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 14 | 686 | 16 (0)| 00:00:01 |
| 1 | NESTED LOOPS | | 14 | 686 | 16 (0)| 00:00:01 |
| 2 | NESTED LOOPS | | 14 | 686 | 16 (0)| 00:00:01 |
| 3 | TABLE ACCESS BY INDEX ROWID| EMP | 14 | 532 | 2 (0)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | PK_EMP | 14 | | 1 (0)| 00:00:01 |
|* 5 | INDEX UNIQUE SCAN | PK_DEPT | 1 | | 0 (0)| 00:00:01 |
| 6 | TABLE ACCESS BY INDEX ROWID | DEPT | 1 | 11 | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - access("EMPNO">1500)
5 - access("A"."DEPTNO"="B"."DEPTNO")
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
22 consistent gets
0 physical reads
0 redo size
1748 bytes sent via SQL*Net to client
524 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
14 rows processed
总结:
①若驱动表小(结果集小)同时被驱动表的连接列上存在唯一性索引或者选择性较好的索引,那么嵌套循环是非常高效的。反之,若驱动表比较大,即使被驱动表存在选择性好的索引,那么嵌套循环的效率也不好。
②嵌套循环可以实现快速响应,即第一时间先返回已经连接过且满足连接条件的记录,这一点在排序合并连接和哈希连接都是做不到的。
③依赖于索引,需要对驱动表多次扫描,需要读取数据表块到buffer cache中,且读取被驱动表都需要以单块读的方式读取到buffer cache中(在11g中引入了向量IO,把一批需要单块读的IO组合起来,使用一个向量IO代替),不可避免存在latch争用。
④对于海量数据处理效果不佳
3.哈希连接(Hash Join)
哈希连接主要依靠哈希运算实现的,是在Oracle10g以后才引入的,由参数_HASH_JOIN_ENABLED参数控制,默认为TRUE。
它的工作方式比较复杂,大致为:
①Oracle根据参数HASH_AREA_SIZE,DB_BLOCK_SIZE和_HASH_MULTIBLOCK_IO_COUNT参数值来确定Hash partition的数量(一组hash bucket组成一个hash partition,一组hash partition组成一个hash table),oracle 根据以上原理生成hash表(build 阶段)。
②Oracle分别对表T1和T2生成的hash表根据谓词条件查询出目标结果集,选择小结果集(A)为驱动结果集,并对此结果集(A)进行遍历,此过程发生在pga的work area中,若pga不够会使用temp表空间,如果结果集A的数据量很大,那么就会出现pga工作区填满的情况(Probe 阶段)。
③同样的方法遍历结果集B,并根据已经遍历出来的结果集A查找与结果集B相同的结果集,记录为最终结果集。
hash join原理(Probe 阶段):
BEGIN
FOR OUTER IN (SELECT emp.* FROM emp
where emp.empno>'1500')
LOOP -- outer loop
FOR INNER IN (SELECT dept.loc
FROM dept的PGA里的生成的Hash表
WHERE dept.deptno = emp.deptno)
LOOP -- inner loop
dbms_output.put_line( ... );
END LOOP;
END LOOP;
END;
执行计划:
SQL> select /*+ use_hash(a)*/ a.* ,b.loc
2 from scott.emp a ,scott.dept b
3 where a.deptno=b.deptno
4 and empno>'1500';
14 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 1505574247
---------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 14 | 686 | 5 (0)| 00:00:01 |
|* 1 | HASH JOIN | | 14 | 686 | 5 (0)| 00:00:01 |
| 2 | TABLE ACCESS FULL | DEPT | 4 | 44 | 3 (0)| 00:00:01 |
| 3 | TABLE ACCESS BY INDEX ROWID| EMP | 14 | 532 | 2 (0)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | PK_EMP | 14 | | 1 (0)| 00:00:01 |
---------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("A"."DEPTNO"="B"."DEPTNO")
4 - access("EMPNO">1500)
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
10 consistent gets
0 physical reads
0 redo size
1748 bytes sent via SQL*Net to client
524 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
14 rows processed
总结:
①hash join不一定会进行排序,大部分情况下是不需要排序的。
②hash join 仅适用于CBO,且在选择的表为小表和大表之间的连接性能尤为突出。
③连接过程不依赖索引,从pga的hash表中读取数据在buffer cache中构建查询表,不可避免存在latch争用。对于小表直接在pga中进行处理。
4.笛卡儿连接(Cross Join)
笛卡儿连接又称为笛卡儿乘积,实在两个表没有执行表连接时产生的。
它的执行顺序是这样的:
①首先对表T1根据谓词条件查询出目标结果集,记录为结果集1记录数为m。
②然后对表T2根据谓词条件查询出目标结果集,记录为结果集2记录数为n。
③最后把结果集1和结果集2进行合并(m*n)
执行计划:
SQL> select count(*) from scott.emp;
COUNT(*)
----------
14
SQL> select count(*) from scott.dept;
COUNT(*)
----------
4 --结果集为14*4=56
SQL> select a.* ,b.loc
2 from scott.emp a ,scott.dept b ;
56 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 2034389985
-----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 56 | 2576 | 10 (0)| 00:00:01 |
| 1 | MERGE JOIN CARTESIAN| | 56 | 2576 | 10 (0)| 00:00:01 |
| 2 | TABLE ACCESS FULL | DEPT | 4 | 32 | 3 (0)| 00:00:01 |
| 3 | BUFFER SORT | | 14 | 532 | 7 (0)| 00:00:01 |
| 4 | TABLE ACCESS FULL | EMP | 14 | 532 | 2 (0)| 00:00:01 |
-----------------------------------------------------------------------------
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
15 consistent gets
0 physical reads
0 redo size
3914 bytes sent via SQL*Net to client
557 bytes received via SQL*Net from client
5 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
56 rows processed
一般出现笛卡儿连接的SQL都是有问题的SQL。
二、表连接类型:
1.内连接(Inner Join)
连接结果集只包含完全满足条件的记录
对于SQL的传统写法如下:
select t1.col1,t2.col2,t2.col3
from t1,t2
where t1.col2=t2.col2;
使用内连接改写为:
select t1.col1,t2.col2,t2.col3
from t1
join t2 on (t1.col2=t2.col2);
或
select t1.col1, col2,t2.col3
from t1
join t2 using (col2); ---对于此方式写法则要求连接列不能使用别名,否则会报ORA - 25154错误
其中内连接还有一种特殊的连接类型 NATURAL JOIN ,此连接类型会自动连接两张表的同名列,不需要指定。
优点:写法方面简单 缺点:对于生产业务表列同名并不代表含义完全一样,此写法增加了SQL错误的风险。所以并不推荐使用此方法
select t1.col1,t2.col2,t2.col3
from t1 natural join t2 ;
2.外连接(Outer Join)
外连接是内连接的一种扩展,这种连接结果集除了包含完全满足连接条件的记录之外还会包含驱动表不满足该条件的记录。
左外连接: left outer join 或 left join
其中t1为驱动表,显示结果集包含t1和t2表结果集相交部分,还包含t1表连接记录之外的结果集,t2.col3列没有的行用null代替。在两个表连接时首先先扫描t1,在使用t1的选择结果集与t2表上逐一扫描。所以Oracle建议选择选择结果集小的表为驱动表。
select t1.col1,t2.col2,t2.col3
from t1 left outer join t2
on (t1.col2=t2.tol2);
或
select t1.col1,t2.col2,t2.col3
from t1 , t2
where t1.col2=t2.tol2(+);
select t1.col1,t2.col2,t2.col3
from t1 left outer join t2
on (t1.col2=t2.tol2 and t1.col1=1);
或
select t1.col1,t2.col2,t2.col3
from t1 , t2
where t1.col2=t2.tol2(+)
and t1.col1 =1(+);
右外连接: right outer join 或 right join
其中t2为驱动表,显示结果集包含t1和t2表结果集相交部分,还包含t2表连接记录之外的结果集,t1.col1列没有的行用null代替。
select t1.col1,t2.col2,t2.col3
from t1 right outer join t2
on (t1.col2=t2.tol2);
或
select t1.col1,t2.col2,t2.col3
from t1 , t2
where t1.col2(+)=t2.tol2;
全部外连接:full outer join 或 full join
显示结果集包含t1表和t2表关联之后所有的结果集,对应不上的结果行用null代替。对于full outer join 可以理解成 先 left outer join 在right outer join,然后结果集union(可以这么理解,但是实际执行不是这样的)
select t1.col1,t2.col2,t2.col3
from t1 full outer join t2
on (t1.col2=t2.tol2);
3.反连接(Anti Join )
反连接是一种特殊的连接类型,在SQL文本中没有特殊的关键字来表示反连接,这里为了描述方便讲反连接描述为:t1.x anti=t2.y(t1为驱动表),含义:t2表中有满足t1.x=t2.y条件的结果存在就会返回t1中不满足条件的结果行。当做子查询展开时,Oracle经常会吧外部where条件为 not exists、not IN或<> all 的子查询转换成对应的反连接。
select * from t1
where col2 not in (select col2 from t2);
执行计划:
SQL> select *
2 from scott.emp
3 where deptno not in (select deptno from scott.dept );
no rows selected
Execution Plan
----------------------------------------------------------
Plan hash value: 1948401889
------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 5 | 205 | 4 (0)| 00:00:01 |
|* 1 | HASH JOIN ANTI SNA| | 5 | 205 | 4 (0)| 00:00:01 |
| 2 | TABLE ACCESS FULL| EMP | 14 | 532 | 3 (0)| 00:00:01 |
| 3 | INDEX FULL SCAN | PK_DEPT | 4 | 12 | 1 (0)| 00:00:01 |
------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("DEPTNO"="DEPTNO")
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
7 consistent gets
0 physical reads
0 redo size
799 bytes sent via SQL*Net to client
513 bytes received via SQL*Net from client
1 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
0 rows processed
总结:
在表中一旦有了null值那么not exists、not IN或<> all久不是等价的了,not exists、not IN对null比较敏感,若子查询中有了null,则not exists、not IN连接就没有结果。若外部查询存在null,那么not exists、not IN也是不显示null行。
4.半连接(Semi Join)
半连接是一种特殊的连接类型,在SQL文本中没有特殊的关键字来表示半连接,这里为了描述方便讲半连接描述为:t1.x semi=t2.y(t1为驱动表),含义:只要t2表中找到一条满足t1.x=t2.y条件的结果存在,就会停止搜索t2表返回t1表中满足条件的记录行(只返回一行)。实际上半连接会去重。当做子查询展开时,Oracle经常会吧外部where条件为exists、IN或=any的子查询转换成对应的半连接。
select * from t1
where col2 in (select col2 from t2);
执行计划:
SQL> select *
2 from scott.dept
3 where deptno in (select deptno from scott.emp );
Execution Plan
----------------------------------------------------------
Plan hash value: 1090737117
----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 3 | 69 | 6 (17)| 00:00:01 |
| 1 | MERGE JOIN SEMI | | 3 | 69 | 6 (17)| 00:00:01 |
| 2 | TABLE ACCESS BY INDEX ROWID| DEPT | 4 | 80 | 2 (0)| 00:00:01 |
| 3 | INDEX FULL SCAN | PK_DEPT | 4 | | 1 (0)| 00:00:01 |
|* 4 | SORT UNIQUE | | 14 | 42 | 4 (25)| 00:00:01 |
| 5 | TABLE ACCESS FULL | EMP | 14 | 42 | 3 (0)| 00:00:01 |
----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - access("DEPTNO"="DEPTNO")
filter("DEPTNO"="DEPTNO")
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
10 consistent gets
0 physical reads
0 redo size
768 bytes sent via SQL*Net to client
524 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
3 rows processed
总结:
exists、IN或=any的结果是一样的。
5.星型连接(Star Join)
星型连接一般出现在数据仓库中,是单个事实表(fact table)和多个维度表(dimension table)之间的连接。其中多个维度表基于事实表从各个维度通过外键列和对应维度表的主键列之间建立连接,通常需要在事实表中创建位图索引(bitmap index)<关于索引会在后边详细讨论>,而对于此类创建了位图索引的表Oracle强烈建议不能进行DML操作,一旦进行dml操作会大大降低位图索引的执行效率。
SH@PROD1> set linesize 200;
SH@PROD1> set pagesize 200;
SH@PROD1> SELECT CH.CHANNEL_CLASS,C.CUST_CITY , T.CALENDAR_QUARTER_DESC,
2 SUM(S.AMOUNT_SOLD) SALES_AMOUNT
3 FROM SH.SALES S ,SH.TIMES T,SH.CUSTOMERS C,SH.CHANNELS CH
4 WHERE S.TIME_ID = T.TIME_ID
5 AND S.CUST_ID=C.CUST_ID
6 AND S.CHANNEL_ID = CH.CHANNEL_ID
7 AND C.CUST_STATE_PROVINCE ='CA'
8 AND CH.CHANNEL_DESC IN ('Internet','Catalog')
9 AND T.CALENDAR_QUARTER_DESC IN ('1999-Q1','1999-Q2')
10 GROUP BY CH.CHANNEL_CLASS,C.CUST_CITY , T.CALENDAR_QUARTER_DESC;
no rows selected
Execution Plan
----------------------------------------------------------
Plan hash value: 4231244587
-------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
-------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 254 | 18542 | 557 (1)| 00:00:07 | | |
| 1 | TEMP TABLE TRANSFORMATION | | | | | | | |
| 2 | LOAD AS SELECT | SYS_TEMP_0FD9D660D_1744F2 | | | | | | |
|* 3 | TABLE ACCESS FULL | CUSTOMERS | 383 | 9958 | 405 (1)| 00:00:05 | | |
| 4 | HASH GROUP BY | | 254 | 18542 | 152 (2)| 00:00:02 | | |
|* 5 | HASH JOIN | | 254 | 18542 | 128 (1)| 00:00:02 | | |
|* 6 | TABLE ACCESS FULL | TIMES | 183 | 2928 | 18 (0)| 00:00:01 | | |
|* 7 | HASH JOIN | | 254 | 14478 | 110 (1)| 00:00:02 | | |
|* 8 | HASH JOIN | | 254 | 10668 | 107 (0)| 00:00:02 | | |
|* 9 | TABLE ACCESS FULL | CHANNELS | 2 | 42 | 3 (0)| 00:00:01 | | |
| 10 | PARTITION RANGE SUBQUERY | | 254 | 5334 | 104 (0)| 00:00:02 |KEY(SQ)|KEY(SQ)|
| 11 | TABLE ACCESS BY LOCAL INDEX ROWID| SALES | 254 | 5334 | 104 (0)| 00:00:02 |KEY(SQ)|KEY(SQ)|
| 12 | BITMAP CONVERSION TO ROWIDS | | | | | | | |
| 13 | BITMAP AND | | | | | | | |
| 14 | BITMAP MERGE | | | | | | | |
| 15 | BITMAP KEY ITERATION | | | | | | | |
| 16 | BUFFER SORT | | | | | | | |
|* 17 | TABLE ACCESS FULL | CHANNELS | 2 | 26 | 3 (0)| 00:00:01 | | |
|* 18 | BITMAP INDEX RANGE SCAN | CHANNEL_IND | | | | |KEY(SQ)|KEY(SQ)|
| 19 | BITMAP MERGE | | | | | | | |
| 20 | BITMAP KEY ITERATION | | | | | | | |
| 21 | BUFFER SORT | | | | | | | |
|* 22 | TABLE ACCESS FULL | TIMES | 183 | 2928 | 18 (0)| 00:00:01 | | |
|* 23 | BITMAP INDEX RANGE SCAN | TIME_IND | | | | |KEY(SQ)|KEY(SQ)|
| 24 | BITMAP MERGE | | | | | | | |
| 25 | BITMAP KEY ITERATION | | | | | | | |
| 26 | BUFFER SORT | | | | | | | |
| 27 | TABLE ACCESS FULL | SYS_TEMP_0FD9D660D_1744F2 | 383 | 1915 | 2 (0)| 00:00:01 | | |
|* 28 | BITMAP INDEX RANGE SCAN | CUST_IND | | | | |KEY(SQ)|KEY(SQ)|
| 29 | TABLE ACCESS FULL | SYS_TEMP_0FD9D660D_1744F2 | 383 | 5745 | 2 (0)| 00:00:01 | | |
-------------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - filter("C"."CUST_STATE_PROVINCE"='CA')
5 - access("S"."TIME_ID"="T"."TIME_ID")
6 - filter("T"."CALENDAR_QUARTER_DESC"='1999-Q1' OR "T"."CALENDAR_QUARTER_DESC"='1999-Q2')
7 - access("S"."CUST_ID"="C0")
8 - access("S"."CHANNEL_ID"="CH"."CHANNEL_ID")
9 - filter("CH"."CHANNEL_DESC"='Catalog' OR "CH"."CHANNEL_DESC"='Internet')
17 - filter("CH"."CHANNEL_DESC"='Catalog' OR "CH"."CHANNEL_DESC"='Internet')
18 - access("S"."CHANNEL_ID"="CH"."CHANNEL_ID")
22 - filter("T"."CALENDAR_QUARTER_DESC"='1999-Q1' OR "T"."CALENDAR_QUARTER_DESC"='1999-Q2')
23 - access("S"."TIME_ID"="T"."TIME_ID")
28 - access("S"."CUST_ID"="C0")
Note
-----
- star transformation used for this statement
Statistics
----------------------------------------------------------
9 recursive calls
16 db block gets
1526 consistent gets
0 physical reads
820 redo size
497 bytes sent via SQL*Net to client
408 bytes received via SQL*Net from client
1 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
0 rows processed
SH@PROD1>
三、查询转换(Query Transformation):
查询转换又称为查询改写,Oracle在硬解析的时候解析产生执行计划之前需要对SQL文本进行等价改写,以提高SQL的执行效率。在Oracle 10g之前查询转换是独立于优化器之外的,即每条SQL都会进行查询转换,Oracle认为等价改写之后SQL执行效率更高,但有些时候并不是这样的。故在Oracle10g之后查询转换和CBO是密切相关的,只对某些类型的查询转换(子查询展开,复杂视图合并等)计算成本,即Oracle会分别计算经过查询转换后的等价改写SQL的成本和原始SQL的成本,只有当等价改写SQL的成本值小于未经查询转换的原始SQL的成本时,Oracle才会对目标SQL执行这些查询转换。
1.子查询展开(Subqury Unnesting)
子查询展开是指优化器不再将目标SQL中的子查询单过一个独立的独立单元来独立执行,而是将子查询转换为它自身和外部查询之间等价的表连接。子查询展开通常情况下都会提高原SQL的执行效率,若不做子查询展开,外部查询的每一个结果集作为一个单元传给子查询运行,并且会走FILTER类型的执行计划。
子查询展开类型:
①将子查询拆开,即将子查询中的表、视图从子查询中拿出来和外部查询中的表、视图进行表连接。Oracle10g以后经过子查询展开后的等价改写SQL的成本无论是否小于原来SQL的成本值。Oracle都会做子查询展开查询转换。
②将子查询转换成一个内嵌视图(Inline View),然后和外部查询中的表、视图进行表连接。Oracle10g以后经过子查询展开后的等价改写SQL的成本小于原来SQL的成本值,Oracle才会做查询转换。
子查询展开条件:
①SQL文本中where条件满足以下条件:
single-row(=、<、>、<=、>=和<>)
EXISTS
NOT EXISTS
IN
NOT IN
ANY
ALL
②查询转换一定是等价的,对于不拆开子查询会把它转换成一个内嵌视图来和外部查询中的表、视图进行连接,这种情况下一定是经过子查询展开后的等价改写SQL的成本小于原来SQL的成本值,Oracle才会做子查询展开。
例:在Oracle 10g以后之后将子查询转换成内嵌试图的成本小于原来SQL成本时才会进行做子查询展开方式的查询转换,明显一下示例SQL子查询展开方式的SQL成本和consistent gets小于做内嵌视图方式的查询转换。
子查询展开:
SQL> select a.*
2 from scott.dept a
3 where a.deptno in (select b.deptno from scott.emp b
4 where b.empno >'7500' );
实际上以上SQL进行查询转换后将原SQL等价转换成半连接方式:
select a.*
from scott.dept a,scott,emp b
where a.deptno semi=b.deptno
and b.empno >'7500'
Execution Plan
----------------------------------------------------------
Plan hash value: 287487506
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 3 | 81 | 5 (20)| 00:00:01 |
| 1 | MERGE JOIN SEMI | | 3 | 81 | 5 (20)| 00:00:01 |
| 2 | TABLE ACCESS BY INDEX ROWID | DEPT | 4 | 80 | 2 (0)| 00:00:01 |
| 3 | INDEX FULL SCAN | PK_DEPT | 4 | | 1 (0)| 00:00:01 |
|* 4 | SORT UNIQUE | | 11 | 77 | 3 (34)| 00:00:01 |
| 5 | TABLE ACCESS BY INDEX ROWID| EMP | 11 | 77 | 2 (0)| 00:00:01 |
|* 6 | INDEX RANGE SCAN | PK_EMP | 11 | | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - access("A"."DEPTNO"="B"."DEPTNO")
filter("A"."DEPTNO"="B"."DEPTNO")
6 - access("B"."EMPNO">7500)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
6 consistent gets
0 physical reads
0 redo size
768 bytes sent via SQL*Net to client
524 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
3 rows processed
no_unnest是不对该SQL做子查询展开,即使用第二种方式将子查询转换成内嵌试图的方式进行查询转换。
SQL> select a.*
2 from scott.dept a
3 where a.deptno in (select /*+ no_unnest */ b.deptno from scott.emp b
4 where b.empno >'7500' );
Execution Plan
----------------------------------------------------------
Plan hash value: 3633697989
---------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 20 | 7 (0)| 00:00:01 |
|* 1 | FILTER | | | | | |
| 2 | TABLE ACCESS FULL | DEPT | 4 | 80 | 3 (0)| 00:00:01 |
|* 3 | TABLE ACCESS BY INDEX ROWID| EMP | 2 | 14 | 2 (0)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | PK_EMP | 11 | | 1 (0)| 00:00:01 |
---------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter( EXISTS (SELECT /*+ NO_UNNEST */ 0 FROM "SCOTT"."EMP" "B" WHERE
"B"."EMPNO">7500 AND "B"."DEPTNO"=:B1))
3 - filter("B"."DEPTNO"=:B1)
4 - access("B"."EMPNO">7500)
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
15 consistent gets
0 physical reads
0 redo size
768 bytes sent via SQL*Net to client
524 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
3 rows processed
2.视图合并(View Mergging)
视图合并是指Oracle不在将视图中的作为一个单元,而是将视图内部中的SQL基表和外部查询中的表做连接。
(1)简单视图合并(Simple View Merging)
简单视图合并是指:不包含外连接,以及所带视图的定义SQL中不包含distinct、group by等聚集函数的视图合并。
简单视图合并Oracle会始终进行视图合并,而不管经过视图合并后的等价改写SQL的成本值时候小于原SQL的成本值。对于简单视图合并是将视图拆开会出现很多种情况(三表关联会出现3*2=6的可能性)
例:通过以下执行计划发现,Oracle已经默认做了简单视图合并。
SQL> select p.*
2 from test.v_1 v,test.product p
3 where p.object_name=v.table_name
4 and p.OBJECT_ID<'7000';
13 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 432321455
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 178 | 27590 | 199 (0)| 00:00:03 |
|* 1 | HASH JOIN | | 178 | 27590 | 199 (0)| 00:00:03 |
|* 2 | HASH JOIN | | 109 | 14388 | 151 (0)| 00:00:02 |
|* 3 | TABLE ACCESS FULL | SALES | 106 | 3604 | 31 (0)| 00:00:01 |
| 4 | TABLE ACCESS BY INDEX ROWID| PRODUCT | 6738 | 644K| 120 (0)| 00:00:02 |
|* 5 | INDEX RANGE SCAN | IND_A | 6738 | | 16 (0)| 00:00:01 |
|* 6 | TABLE ACCESS FULL | EMP | 3752 | 86296 | 48 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("A"."TABLE_NAME"="B"."TABLE_NAME")
2 - access("P"."OBJECT_NAME"="A"."TABLE_NAME")
3 - filter("A"."OWNER"='SYSTEM' AND "A"."TABLESPACE_NAME"='SYSAUX')
5 - access("P"."OBJECT_ID"<7000)
6 - filter("B"."LEAF_BLOCKS" IS NOT NULL)
Statistics
----------------------------------------------------------
8 recursive calls
0 db block gets
380 consistent gets
0 physical reads
0 redo size
2098 bytes sent via SQL*Net to client
523 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
13 rows processed
我们可以通过hint no_merge 来让Oracle优化器不进行视图合并,执行计划中出现如下view后边name是对象名称,说明没有进行简单视图合并,执行计划如下:
SQL> select /*+ no_merge(v)*/ p.*
2 from test.v_1 v,test.product p
3 where p.object_name=v.table_name
4 and p.OBJECT_ID<'7000';
13 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 1310787303
----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 178 | 20470 | 199 (0)| 00:00:03 |
|* 1 | HASH JOIN | | 178 | 20470 | 199 (0)| 00:00:03 |
| 2 | VIEW | V_1 | 173 | 2941 | 79 (0)| 00:00:01 |
|* 3 | HASH JOIN | | 173 | 9861 | 79 (0)| 00:00:01 |
|* 4 | TABLE ACCESS FULL | SALES | 106 | 3604 | 31 (0)| 00:00:01 |
|* 5 | TABLE ACCESS FULL | EMP | 3752 | 86296 | 48 (0)| 00:00:01 |
| 6 | TABLE ACCESS BY INDEX ROWID| PRODUCT | 6738 | 644K| 120 (0)| 00:00:02 |
|* 7 | INDEX RANGE SCAN | IND_A | 6738 | | 16 (0)| 00:00:01 |
----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("P"."OBJECT_NAME"="V"."TABLE_NAME")
3 - access("A"."TABLE_NAME"="B"."TABLE_NAME")
4 - filter("A"."OWNER"='SYSTEM' AND "A"."TABLESPACE_NAME"='SYSAUX')
5 - filter("B"."LEAF_BLOCKS" IS NOT NULL)
7 - access("P"."OBJECT_ID"<7000)
Statistics
----------------------------------------------------------
8 recursive calls
0 db block gets
381 consistent gets
0 physical reads
0 redo size
2106 bytes sent via SQL*Net to client
523 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
13 rows processed
(2)外连接视图合并(Outer Join View Merging)
外连接视图合并是指:针对那些使用了外连接,以及所带视图的定义SQL中不包含distinct、group by等聚合函数的目标申请的视图合并。
外连接对视图合并有很多限制,很多情况下内连接可以做的视图合并,转化成外连接之后就不会做视图合并,原因是在保证语义相同的情况下不能做视图转换。所以,只有在该视图作为外连接的驱动表或者他作为外连接的被驱动表但是视图定义SQL语句中只包含一个表时该视图和外部查询做外连接才可以做视图合并
例:当外连接视图作为驱动表时Oracle会直接进行视图合并:
SQL> select p.*
2 from test.v_1 v,test.product p
3 where p.object_name(+)=v.table_name
4 and p.OBJECT_ID<'7000';
13 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 1397898977
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 178 | 27590 | 180 (0)| 00:00:03 |
|* 1 | HASH JOIN | | 178 | 27590 | 180 (0)| 00:00:03 |
|* 2 | HASH JOIN | | 109 | 14388 | 132 (0)| 00:00:02 |
|* 3 | TABLE ACCESS BY INDEX ROWID| SALES | 106 | 3604 | 12 (0)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | OIND_C | 167 | | 1 (0)| 00:00:01 |
| 5 | TABLE ACCESS BY INDEX ROWID| PRODUCT | 6738 | 644K| 120 (0)| 00:00:02 |
|* 6 | INDEX RANGE SCAN | IND_A | 6738 | | 16 (0)| 00:00:01 |
|* 7 | TABLE ACCESS FULL | EMP | 3752 | 86296 | 48 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("A"."TABLE_NAME"="B"."TABLE_NAME")
2 - access("P"."OBJECT_NAME"="A"."TABLE_NAME")
3 - filter("A"."TABLESPACE_NAME"='SYSAUX')
4 - access("A"."OWNER"='SYSTEM')
6 - access("P"."OBJECT_ID"<7000)
7 - filter("B"."LEAF_BLOCKS" IS NOT NULL)
Statistics
----------------------------------------------------------
8 recursive calls
0 db block gets
303 consistent gets
0 physical reads
0 redo size
2098 bytes sent via SQL*Net to client
523 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
13 rows processed
当把外连接视图做驱动表时,由于该视图是两个表连接(sales和emp)的视图,所以Oracle不会进行视图合并
SQL> select p.*
2 from test.v_1 v,test.product p
3 where p.object_name=v.table_name(+)
4 and p.OBJECT_ID<'7000';
6888 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 970244956
------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 6738 | 756K| 180 (0)| 00:00:03 |
|* 1 | HASH JOIN RIGHT OUTER | | 6738 | 756K| 180 (0)| 00:00:03 |
| 2 | VIEW | V_1 | 173 | 2941 | 60 (0)| 00:00:01 |
|* 3 | HASH JOIN | | 173 | 9861 | 60 (0)| 00:00:01 |
|* 4 | TABLE ACCESS BY INDEX ROWID| SALES | 106 | 3604 | 12 (0)| 00:00:01 |
|* 5 | INDEX RANGE SCAN | OIND_C | 167 | | 1 (0)| 00:00:01 |
|* 6 | TABLE ACCESS FULL | EMP | 3752 | 86296 | 48 (0)| 00:00:01 |
| 7 | TABLE ACCESS BY INDEX ROWID | PRODUCT | 6738 | 644K| 120 (0)| 00:00:02 |
|* 8 | INDEX RANGE SCAN | IND_A | 6738 | | 16 (0)| 00:00:01 |
------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("P"."OBJECT_NAME"="V"."TABLE_NAME"(+))
3 - access("A"."TABLE_NAME"="B"."TABLE_NAME")
4 - filter("A"."TABLESPACE_NAME"='SYSAUX')
5 - access("A"."OWNER"='SYSTEM')
6 - filter("B"."LEAF_BLOCKS" IS NOT NULL)
8 - access("P"."OBJECT_ID"<7000)
Statistics
----------------------------------------------------------
8 recursive calls
0 db block gets
1217 consistent gets
0 physical reads
0 redo size
384837 bytes sent via SQL*Net to client
5572 bytes received via SQL*Net from client
461 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
6888 rows processed
(3)复杂视图合并(Compx View Merging)
复杂视图合并是指:包含外连接,以及视图定义的SQL包含distinct、group by等聚合函数的视图合并。
复杂视图合并和前两者合并都不太一样,由于包含了distinct、group by等聚合函数,而在执行语句时需要把视图中的基表和外部关联表进行关联,那就意味着聚合函数需要放在最后执行,所以这种情况下并不一定带来SQL语句执行效率的提升。如果distinct、group by等聚合函数能过滤掉大部分结果集,那么显然是先执行视图在关联外表的执行效率是最优化的。故在Oracle 10g以后对于复杂视图合并的前提是:只有当经过复杂视图合并后的等价改写SQL的成本值小于原SQL的成本时,Oracle才会对目标SQL执行复杂视图合并。
例:由于视图合并之后执行成本和合并前一样而且逻辑读更高,所以Oracle优化器没有对其进行视图合并。
SQL> select p.*
2 from test.v_2 v,test.product p
3 where p.object_name=v.table_name
4 and p.OBJECT_ID<'7000';
13 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 593319594
------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 178 | 31684 | 181 (1)| 00:00:03 |
| 1 | HASH GROUP BY | | 178 | 31684 | 181 (1)| 00:00:03 |
|* 2 | HASH JOIN | | 178 | 31684 | 180 (0)| 00:00:03 |
|* 3 | HASH JOIN | | 109 | 14388 | 132 (0)| 00:00:02 |
|* 4 | TABLE ACCESS BY INDEX ROWID| SALES | 106 | 3604 | 12 (0)| 00:00:01 |
|* 5 | INDEX RANGE SCAN | OIND_C | 167 | | 1 (0)| 00:00:01 |
| 6 | TABLE ACCESS BY INDEX ROWID| PRODUCT | 6738 | 644K| 120 (0)| 00:00:02 |
|* 7 | INDEX RANGE SCAN | IND_A | 6738 | | 16 (0)| 00:00:01 |
|* 8 | TABLE ACCESS FULL | EMP | 3752 | 168K| 48 (0)| 00:00:01 |
------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("A"."TABLE_NAME"="B"."TABLE_NAME")
3 - access("P"."OBJECT_NAME"="A"."TABLE_NAME")
4 - filter("A"."TABLESPACE_NAME"='SYSAUX')
5 - access("A"."OWNER"='SYSTEM')
7 - access("P"."OBJECT_ID"<7000)
8 - filter("B"."LEAF_BLOCKS" IS NOT NULL)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
300 consistent gets
0 physical reads
0 redo size
2172 bytes sent via SQL*Net to client
523 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
13 rows processed
SQL>
SQL> select /*+ no_merge(v)*/ p.*
2 from test.v_2 v,test.product p
3 where p.object_name(+)=v.table_name
4 and p.OBJECT_ID<'7000';
13 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 3289631185
-------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 178 | 20826 | 181 (1)| 00:00:03 |
|* 1 | HASH JOIN | | 178 | 20826 | 181 (1)| 00:00:03 |
| 2 | VIEW | V_2 | 173 | 3287 | 61 (2)| 00:00:01 |
| 3 | HASH GROUP BY | | 173 | 14878 | 61 (2)| 00:00:01 |
|* 4 | HASH JOIN | | 173 | 14878 | 60 (0)| 00:00:01 |
|* 5 | TABLE ACCESS BY INDEX ROWID| SALES | 106 | 4240 | 12 (0)| 00:00:01 |
|* 6 | INDEX RANGE SCAN | OIND_C | 167 | | 1 (0)| 00:00:01 |
|* 7 | TABLE ACCESS FULL | EMP | 3752 | 168K| 48 (0)| 00:00:01 |
| 8 | TABLE ACCESS BY INDEX ROWID | PRODUCT | 6738 | 644K| 120 (0)| 00:00:02 |
|* 9 | INDEX RANGE SCAN | IND_A | 6738 | | 16 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("P"."OBJECT_NAME"="V"."TABLE_NAME")
4 - access("A"."TABLE_NAME"="B"."TABLE_NAME")
5 - filter("A"."TABLESPACE_NAME"='SYSAUX')
6 - access("A"."OWNER"='SYSTEM')
7 - filter("B"."LEAF_BLOCKS" IS NOT NULL)
9 - access("P"."OBJECT_ID"<7000)
Statistics
----------------------------------------------------------
8 recursive calls
0 db block gets
304 consistent gets
0 physical reads
0 redo size
2106 bytes sent via SQL*Net to client
523 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
13 rows processed
3.星型转换(Star Transformation)
星型转换是Oracle 优化器处理表连接的另一种优化手段,他发生在星型查询中,并且存在位图索引。它是将原星型连接中针对各个维度表(Dimension Table)的限制条件通过等价改写的方式以额外的子查询施加到事实表(Fact Table)上,通过事实表中的位图索引对事实表进行索引扫描,进而提升效率。
SH@PROD1> set linesize 200;
SH@PROD1> set pagesize 200;
SH@PROD1> SELECT CH.CHANNEL_CLASS,C.CUST_CITY , T.CALENDAR_QUARTER_DESC,
2 SUM(S.AMOUNT_SOLD) SALES_AMOUNT
3 FROM SH.SALES S ,SH.TIMES T,SH.CUSTOMERS C,SH.CHANNELS CH
4 WHERE S.TIME_ID = T.TIME_ID
5 AND S.CUST_ID=C.CUST_ID
6 AND S.CHANNEL_ID = CH.CHANNEL_ID
7 AND C.CUST_STATE_PROVINCE ='CA'
8 AND CH.CHANNEL_DESC IN ('Internet','Catalog')
9 AND T.CALENDAR_QUARTER_DESC IN ('1999-Q1','1999-Q2')
10 GROUP BY CH.CHANNEL_CLASS,C.CUST_CITY , T.CALENDAR_QUARTER_DESC;
no rows selected
Execution Plan
----------------------------------------------------------
Plan hash value: 4231244587
-------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
-------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 254 | 18542 | 557 (1)| 00:00:07 | | |
| 1 | TEMP TABLE TRANSFORMATION | | | | | | | |
| 2 | LOAD AS SELECT | SYS_TEMP_0FD9D660D_1744F2 | | | | | | |
|* 3 | TABLE ACCESS FULL | CUSTOMERS | 383 | 9958 | 405 (1)| 00:00:05 | | |
| 4 | HASH GROUP BY | | 254 | 18542 | 152 (2)| 00:00:02 | | |
|* 5 | HASH JOIN | | 254 | 18542 | 128 (1)| 00:00:02 | | |
|* 6 | TABLE ACCESS FULL | TIMES | 183 | 2928 | 18 (0)| 00:00:01 | | |
|* 7 | HASH JOIN | | 254 | 14478 | 110 (1)| 00:00:02 | | |
|* 8 | HASH JOIN | | 254 | 10668 | 107 (0)| 00:00:02 | | |
|* 9 | TABLE ACCESS FULL | CHANNELS | 2 | 42 | 3 (0)| 00:00:01 | | |
| 10 | PARTITION RANGE SUBQUERY | | 254 | 5334 | 104 (0)| 00:00:02 |KEY(SQ)|KEY(SQ)|
| 11 | TABLE ACCESS BY LOCAL INDEX ROWID| SALES | 254 | 5334 | 104 (0)| 00:00:02 |KEY(SQ)|KEY(SQ)|
| 12 | BITMAP CONVERSION TO ROWIDS | | | | | | | |
| 13 | BITMAP AND | | | | | | | |
| 14 | BITMAP MERGE | | | | | | | |
| 15 | BITMAP KEY ITERATION | | | | | | | |
| 16 | BUFFER SORT | | | | | | | |
|* 17 | TABLE ACCESS FULL | CHANNELS | 2 | 26 | 3 (0)| 00:00:01 | | |
|* 18 | BITMAP INDEX RANGE SCAN | CHANNEL_IND | | | | |KEY(SQ)|KEY(SQ)|
| 19 | BITMAP MERGE | | | | | | | |
| 20 | BITMAP KEY ITERATION | | | | | | | |
| 21 | BUFFER SORT | | | | | | | |
|* 22 | TABLE ACCESS FULL | TIMES | 183 | 2928 | 18 (0)| 00:00:01 | | |
|* 23 | BITMAP INDEX RANGE SCAN | TIME_IND | | | | |KEY(SQ)|KEY(SQ)|
| 24 | BITMAP MERGE | | | | | | | |
| 25 | BITMAP KEY ITERATION | | | | | | | |
| 26 | BUFFER SORT | | | | | | | |
| 27 | TABLE ACCESS FULL | SYS_TEMP_0FD9D660D_1744F2 | 383 | 1915 | 2 (0)| 00:00:01 | | |
|* 28 | BITMAP INDEX RANGE SCAN | CUST_IND | | | | |KEY(SQ)|KEY(SQ)|
| 29 | TABLE ACCESS FULL | SYS_TEMP_0FD9D660D_1744F2 | 383 | 5745 | 2 (0)| 00:00:01 | | |
-------------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - filter("C"."CUST_STATE_PROVINCE"='CA')
5 - access("S"."TIME_ID"="T"."TIME_ID")
6 - filter("T"."CALENDAR_QUARTER_DESC"='1999-Q1' OR "T"."CALENDAR_QUARTER_DESC"='1999-Q2')
7 - access("S"."CUST_ID"="C0")
8 - access("S"."CHANNEL_ID"="CH"."CHANNEL_ID")
9 - filter("CH"."CHANNEL_DESC"='Catalog' OR "CH"."CHANNEL_DESC"='Internet')
17 - filter("CH"."CHANNEL_DESC"='Catalog' OR "CH"."CHANNEL_DESC"='Internet')
18 - access("S"."CHANNEL_ID"="CH"."CHANNEL_ID")
22 - filter("T"."CALENDAR_QUARTER_DESC"='1999-Q1' OR "T"."CALENDAR_QUARTER_DESC"='1999-Q2')
23 - access("S"."TIME_ID"="T"."TIME_ID")
28 - access("S"."CUST_ID"="C0")
Note
-----
- star transformation used for this statement
Statistics
----------------------------------------------------------
9 recursive calls
16 db block gets
1526 consistent gets
0 physical reads
820 redo size
497 bytes sent via SQL*Net to client
408 bytes received via SQL*Net from client
1 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
0 rows processed
SH@PROD1>
4.连接谓词推入(Join Predicate Pushdown)
连接谓词推入是Oracle 优化器处理表连接的另一种优化手段,它是指Oracle优化器会把视图中定义的SQL当成一个独立的单元来单独执行,这样能够使用视图中基表的索引来提升执行效率,然后把处于视图外部的查询连接条件推入到Oracle视图内部SQL中,进而与视图形成一种嵌套循环。值得注意的是,不是所有的连接谓词推入转换都能提升SQL的执行效率,因为外部查询的每一条记录都会与视图中的SQL关联一次,如果外部查询所在的结果集cardinality较大的话,即使视图基表使用索引,那带来的成本也是相当高的。只有当连接谓词推入后嵌套循环连接的等价改写SQL的成本小于原SQL的成本时,Oracle才会对目标SQL做连接谓词推入。
Oracle支持做连接谓词推入的视图类型:
·视图定义的SQL语句中包含UNion all/union的视图。
·视图定义的SQL语句中包含distinct的视图。
·视图定义的SQL语句中包含group by的视图。
·视图定义的SQL语句中包含外连接的视图。
·视图定义的SQL语句中包含反连接的视图。
·视图定义的SQL语句中包含半连接的视图。
例:从以下执行计划中可看出,Oracle优化器默认没有对该SQL进行视图合并,连接方式采用的是hash join。
SQL> select p.*
2 from test.v_4 v,test.product p
3 where p.object_name=v.table_name;
and p.OBJECT_ID<'7000';
7884 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 1520040170
---------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 12610 | 1416K| 425 (1)| 00:00:06 |
|* 1 | HASH JOIN | | 12610 | 1416K| 425 (1)| 00:00:06 |
| 2 | VIEW | V_4 | 7537 | 125K| 81 (3)| 00:00:01 |
| 3 | SORT UNIQUE | | 7537 | 161K| 81 (3)| 00:00:01 |
| 4 | UNION-ALL | | | | | |
| 5 | TABLE ACCESS FULL| SALES | 2800 | 56000 | 31 (0)| 00:00:01 |
| 6 | TABLE ACCESS FULL| EMP | 4737 | 106K| 48 (0)| 00:00:01 |
| 7 | TABLE ACCESS FULL | PRODUCT | 86047 | 8234K| 343 (1)| 00:00:05 |
---------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("P"."OBJECT_NAME"="V"."TABLE_NAME")
Statistics
----------------------------------------------------------
8 recursive calls
0 db block gets
2014 consistent gets
1230 physical reads
0 redo size
458324 bytes sent via SQL*Net to client
6298 bytes received via SQL*Net from client
527 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
7884 rows processed
接下来看一下使用视图合并的效果,push_pred hint是对视图中的SQL做连接谓词接入合并,连接方式采用nested pool,成本剧增,故Oracle没有做视图合并。
SQL> select /*+ merge(v) push_pred(v) */ p.*
2 from test.v_4 v,test.product p
3 where p.object_name=v.table_name;
and p.OBJECT_ID<'7000';
7884 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 3755235045
------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 12610 | 1231K| 6979K (3)| 23:16:00 |
| 1 | NESTED LOOPS | | 12610 | 1231K| 6979K (3)| 23:16:00 |
| 2 | TABLE ACCESS FULL | PRODUCT | 86047 | 8234K| 343 (1)| 00:00:05 |
| 3 | VIEW | V_4 | 1 | 2 | 81 (3)| 00:00:01 |
| 4 | SORT UNIQUE | | 2 | 43 | 81 (3)| 00:00:01 |
| 5 | UNION ALL PUSHED PREDICATE | | | | | |
|* 6 | TABLE ACCESS FULL | SALES | 1 | 20 | 31 (0)| 00:00:01 |
|* 7 | TABLE ACCESS FULL | EMP | 1 | 23 | 48 (0)| 00:00:01 |
------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
6 - filter("A"."TABLE_NAME"="P"."OBJECT_NAME")
7 - filter("B"."INDEX_NAME"="P"."OBJECT_NAME")
Statistics
----------------------------------------------------------
8 recursive calls
0 db block gets
22460020 consistent gets
1230 physical reads
0 redo size
458324 bytes sent via SQL*Net to client
6298 bytes received via SQL*Net from client
527 SQL*Net roundtrips to/from client
86047 sorts (memory)
0 sorts (disk)
7884 rows processed
5.连接因式分解(Join Factorization)
连接因式分解是优化器处理待UNION ALL的目标SQL的另一种优化手段(11.2.0.1 and later)。它是指对于UNION ALL的语句不在单独执行,而是将公共部分拿出来作为一个单独的结果集,然后在和原UNION ALL中剩下的部分表连接。它能很明显的提高SQL的执行效率。在11.2.0.1及以后的版本中即使视图语句中包含UNION ALL不能对其进行视图合并,也不会把视图当成一个独立单元来做连接谓词推入,因为他随时都会在连接因式分解。
SQL> select p.*
2 from test.v_5 v,test.product p
3 where p.object_name=v.table_name;
and p.OBJECT_ID<'7000';
8277 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 2561783368
---------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 17751 | 1993K| 1109 (1)| 00:00:14 |
|* 1 | HASH JOIN | | 17751 | 1993K| 1109 (1)| 00:00:14 |
| 2 | VIEW | V_5 | 10610 | 176K| 765 (1)| 00:00:10 |
| 3 | UNION-ALL | | | | | |
|* 4 | HASH JOIN | | 2685 | 141K| 374 (1)| 00:00:05 |
| 5 | TABLE ACCESS FULL| SALES | 2800 | 56000 | 31 (0)| 00:00:01 |
|* 6 | TABLE ACCESS FULL| PRODUCT | 2681 | 91154 | 343 (1)| 00:00:05 |
|* 7 | HASH JOIN | | 7925 | 371K| 391 (1)| 00:00:05 |
| 8 | TABLE ACCESS FULL| EMP | 4737 | 106K| 48 (0)| 00:00:01 |
| 9 | TABLE ACCESS FULL| PRODUCT | 86047 | 2100K| 343 (1)| 00:00:05 |
| 10 | TABLE ACCESS FULL | PRODUCT | 86047 | 8234K| 343 (1)| 00:00:05 |
---------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("P"."OBJECT_NAME"="V"."TABLE_NAME")
4 - access("A"."TABLE_NAME"="P"."OBJECT_NAME")
6 - filter("P"."OBJECT_TYPE"='TABLE')
7 - access("E"."INDEX_NAME"="P"."OBJECT_NAME")
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
4504 consistent gets
255 physical reads
0 redo size
465440 bytes sent via SQL*Net to client
6585 bytes received via SQL*Net from client
553 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
8277 rows processed
6.表扩展(Table Expansion)
表扩展是优化器针对处理分区表的SQL的一种优化手段(11.2.0.1 and later)。它是指当目标SQL中的某个局部索引变得不可用(index status is UNUSABLE)那么oracle会将目标SQL改写成安分区union ALL的形式,这样除了不可用索引的分区以外其他分区还是可以正常使用分区索引的。他的优势也是显而易见的能够大幅度提升SQL效率。
select * from sh.sales where
prod_id=30 and time_id
between to_date('2000-02-01','yyyy-mm-dd')
and to_date('2007-02-01','yyyy-mm-dd');
for Oracle 11gR2 before
SYS@PROD1> select * from sh.sales where
2 prod_id=30 and time_id
3 between to_date('2000-02-01','yyyy-mm-dd')
4 and to_date('2007-02-01','yyyy-mm-dd');
14848 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 2385610757
------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 14911 | 422K| 239 (1)| 00:00:03 | | |
| 1 | PARTITION RANGE ITERATOR | | 14911 | 422K| 239 (1)| 00:00:03 | 13 | 28 |
|* 2 | TABLE ACCESS BY LOCAL INDEX ROWID| SALES | 14911 | 422K| 239 (1)| 00:00:03 | 13 | 28 |
|* 3 | INDEX RANGE SCAN | PROD_ID_IND | 16701 | | 80 (0)| 00:00:01 | 13 | 28 |
------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("TIME_ID">=TO_DATE(' 2000-02-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND
"TIME_ID"<=TO_DATE(' 2007-02-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss'))
3 - access("PROD_ID"=30)
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
2083 consistent gets
0 physical reads
0 redo size
533356 bytes sent via SQL*Net to client
11299 bytes received via SQL*Net from client
991 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
14848 rows processed
7.表移除(Table Elimination)for Oracle 10g later
表移除是优化器处理带夺标连接的目标SQL的另一种优化手段(10.2.0.1 and alert)。它是指优化器会先把SQL中存在与否对执行结果都没影响的表移除,减少表的连接次数。提升SQL效率
SYS@PROD1> select ename from scott.emp ,scott.dept where emp.deptno=dept.deptno;
14 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 3956160932
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 14 | 126 | 3 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| EMP | 14 | 126 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("EMP"."DEPTNO" IS NOT NULL)
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
8 consistent gets
0 physical reads
0 redo size
595 bytes sent via SQL*Net to client
420 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
14 rows processed
8.Oracle处理SQL中的IN
在Oracle数据库中IN和OR是等价的,Oracle优化器会处理带IN的SQL转换为带OR的SQL,在Oracle数据库里,IN和OR是等价的,优化器在处理带IN的SQL时实际上是将其转换为OR的等价SQL。
select * from emp
where deptno in (10,20)
等价转换为
select * from emp
where deptno=10 or deptno=20;
(1)IN - List Lterator
IN - List Lterator是Oracle针对IN后边为常量的一种处理方法,Oracle会遍历SQL中IN后面的所有值然后跟SQL结果集去匹配,如果存在就返回结果集中的值,如果不存在就继续遍历下一个值。
注意:
①IN - List Lterator方式是IN后面接常量的首选处理方法,处理效率比 IN - List Expansion方式效率高。
②IN - List Lterator方式的前提是IN所在列上必须有索引。
③不能强制让Oracle奏IN - List Lterator类型的执行计划,Oracle没有相关的HINT,但可以通过联合摄制10142和10157事件来禁掉IN - List Lterator。
select * from sh.sales
where PROD_ID in (33,40,41);
SYS@PROD1> create index sh.prod_id_ind on sh.sales(prod_id) local;
Index created.
SYS@PROD1> exec dbms_stats.gather_table_stats('sh','sales');
PL/SQL procedure successfully completed.
SH@PROD1> select * from sh.sales
2 where PROD_ID in (33,40,41);
62311 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 1710189240
-------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
-------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 61010 | 1727K| 462 (1)| 00:00:06 | | |
| 1 | PARTITION RANGE ALL | | 61010 | 1727K| 462 (1)| 00:00:06 | 1 | 28 |
| 2 | INLIST ITERATOR | | | | | | | |
| 3 | TABLE ACCESS BY LOCAL INDEX ROWID| SALES | 61010 | 1727K| 462 (1)| 00:00:06 | 1 | 28 |
|* 4 | INDEX RANGE SCAN | PROD_ID_IND | 61010 | | 152 (0)| 00:00:02 | 1 | 28 |
-------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - access("PROD_ID"=33 OR "PROD_ID"=40 OR "PROD_ID"=41)
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
8768 consistent gets
0 physical reads
0 redo size
2232134 bytes sent via SQL*Net to client
46113 bytes received via SQL*Net from client
4156 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
62311 rows processed
SH@PROD1>
SYS@PROD1> drop index sh.prod_id_ind;
Index dropped.
SH@PROD1> select * from sh.sales
2 where PROD_ID in (33,40,41);
62311 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 1550251865
---------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
---------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 38285 | 1084K| 490 (2)| 00:00:06 | | |
| 1 | PARTITION RANGE ALL| | 38285 | 1084K| 490 (2)| 00:00:06 | 1 | 28 |
|* 2 | TABLE ACCESS FULL | SALES | 38285 | 1084K| 490 (2)| 00:00:06 | 1 | 28 |
---------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("PROD_ID"=33 OR "PROD_ID"=40 OR "PROD_ID"=41)
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
5869 consistent gets
0 physical reads
0 redo size
2232186 bytes sent via SQL*Net to client
46113 bytes received via SQL*Net from client
4156 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
62311 rows processed
SH@PROD1>
(2)IN - List EXpansion / OR Expansion
IN - list Expansion 是Oracle针对IN后面是常量的另外一种处理方法,实际上是优化器将IN后边的常量拆开然后把每个常量提出来形成一个分支,然后union ALL连接,实际上是吧SQL改写成union all的连接形式。
优点:
IN - list Expansion改写成union all分支以后各个分支可以各自走索引、分区修剪、表连接等相关的执行计划而不互相干扰。
缺点:
在未做IN - list Expansion改写前只需生成一个执行计划,IN - list Expansion改写之后会对每个分支生成一个执行计划,执行计划会随着IN后面的常量数增加而增加,一段IN后面的常量集合包含的元素数量非常多的时候,IN - list Expansion的解析时间非常长。
select /*+ use_concat*/ * from sh.sales
where PROD_ID in (33,40,41);
SYS@PROD1> select /*+ use_concat*/ * from sh.sales
2 where PROD_ID in (33,40,41);
62311 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 1710189240
-------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
-------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 62035 | 1756K| 468 (1)| 00:00:06 | | |
| 1 | PARTITION RANGE ALL | | 62035 | 1756K| 468 (1)| 00:00:06 | 1 | 28 |
| 2 | INLIST ITERATOR | | | | | | | |
| 3 | TABLE ACCESS BY LOCAL INDEX ROWID| SALES | 62035 | 1756K| 468 (1)| 00:00:06 | 1 | 28 |
|* 4 | INDEX RANGE SCAN | PROD_ID_IND | 62035 | | 152 (0)| 00:00:02 | 1 | 28 |
-------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - access("PROD_ID"=33 OR "PROD_ID"=40 OR "PROD_ID"=41)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
8782 consistent gets
0 physical reads
0 redo size
2232106 bytes sent via SQL*Net to client
46114 bytes received via SQL*Net from client
4156 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
62311 rows processed
SYS@PROD1> alter session set events '10142 trace name context forever';
Session altered.
SYS@PROD1> alter session set events '10157 trace name context forever';
Session altered.
SYS@PROD1> exec dbms_stats.gather_table_stats('sh','sales',cascade=>true,no_invalidate=>false);
PL/SQL procedure successfully completed.
SYS@PROD1> set autotrace trace;
SYS@PROD1> select /*+ use_concat*/ * from sh.sales
2 where PROD_ID in (33,40,41);
62311 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 2873107102
-------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
-------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 62035 | 1756K| 528 (1)| 00:00:07 | | |
| 1 | CONCATENATION | | | | | | | |
| 2 | PARTITION RANGE ALL | | 14974 | 424K| 135 (0)| 00:00:02 | 1 | 28 |
| 3 | TABLE ACCESS BY LOCAL INDEX ROWID| SALES | 14974 | 424K| 135 (0)| 00:00:02 | 1 | 28 |
|* 4 | INDEX RANGE SCAN | PROD_ID_IND | 14974 | | 59 (0)| 00:00:01 | 1 | 28 |
| 5 | PARTITION RANGE ALL | | 22872 | 647K| 191 (0)| 00:00:03 | 1 | 28 |
| 6 | TABLE ACCESS BY LOCAL INDEX ROWID| SALES | 22872 | 647K| 191 (0)| 00:00:03 | 1 | 28 |
|* 7 | INDEX RANGE SCAN | PROD_ID_IND | 22872 | | 75 (0)| 00:00:01 | 1 | 28 |
| 8 | PARTITION RANGE ALL | | 24189 | 685K| 201 (0)| 00:00:03 | 1 | 28 |
| 9 | TABLE ACCESS BY LOCAL INDEX ROWID| SALES | 24189 | 685K| 201 (0)| 00:00:03 | 1 | 28 |
|* 10 | INDEX RANGE SCAN | PROD_ID_IND | 24189 | | 78 (0)| 00:00:01 | 1 | 28 |
-------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - access("PROD_ID"=41)
7 - access("PROD_ID"=33)
10 - access("PROD_ID"=40)
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
8782 consistent gets
0 physical reads
0 redo size
2232106 bytes sent via SQL*Net to client
46113 bytes received via SQL*Net from client
4156 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
62311 rows processed
(3)IN - List Filer
IN - List Filer 是针对IN后面是子查询的另一种处理方法,优化器会把IN后面的子查询所对应的结果集当做过滤条件,并且奏FILTER类型的执行计划。
select * from sh.sales
where PROD_ID in (select PROD_ID from sh.CUSTOMERS where CUST_LAST_NAME='Ryan');
SYS@PROD1> select * from sh.sales
2 where PROD_ID in (select PROD_ID from sh.CUSTOMERS where CUST_LAST_NAME='Ryan');
918843 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 1844385983
--------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
--------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 12762 | 361K| 1571 (1)| 00:00:19 | | |
|* 1 | FILTER | | | | | | | |
| 2 | PARTITION RANGE ALL| | 918K| 25M| 490 (2)| 00:00:06 | 1 | 28 |
| 3 | TABLE ACCESS FULL | SALES | 918K| 25M| 490 (2)| 00:00:06 | 1 | 28 |
|* 4 | FILTER | | | | | | | |
|* 5 | TABLE ACCESS FULL | CUSTOMERS | 2 | 16 | 15 (0)| 00:00:01 | | |
--------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter( EXISTS (SELECT 0 FROM "SH"."CUSTOMERS" "CUSTOMERS" WHERE :B1=:B2 AND
"CUST_LAST_NAME"='Ryan'))
4 - filter(:B1=:B2)
5 - filter("CUST_LAST_NAME"='Ryan')
Statistics
----------------------------------------------------------
2 recursive calls
0 db block gets
63945 consistent gets
0 physical reads
0 redo size
33489026 bytes sent via SQL*Net to client
674236 bytes received via SQL*Net from client
61258 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
918843 rows processed
happy new year !
— C.A.P. 2020 于北京