探索执行计划中filter的原理

    之前写过一篇文章,filter造成的性能问题,开头是这么写的:

    filter这个词总让人很费解,它下一级可以挂 一个子节点,二个子节点,三个子节点...。

     挂一个子节点意思过滤,如对全表进行扫描后,按照条件过滤,丢弃不满足条件的数据。

     挂二个子节点类似是nest loop。

     挂三个子节点类似1和2做nest loop,结果集再与3做nest loop(这个是我推测的)。

     为了证明当时的观点,我们下面来做个试验:
filter下挂一个节点:
        挂一个节点时,下面的例子可以看到,对表扫描一次,就是对返回的结果集进行过滤,不过有趣的是当条件为假时,不会对表进行扫描。
SQL> select * from emp e where e.hiredate > sysdate - 10000 and  e.hiredate < sysdate;
     EMPNO ENAME      JOB              MGR HIREDATE              SAL       COMM     DEPTNO
---------- ---------- --------- ---------- -------------- ---------- ---------- ----------
      7788 SCOTT      ANALYST         7566 19-4月 -87           3000                    20
      7876 ADAMS      CLERK           7788 23-5月 -87           1100                    20
SQL> alter session set statistics_level=all;
会话已更改。
SQL> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------
SQL_ID  982y5g2z9sn6f, child number 0
-------------------------------------
select * from emp e where e.hiredate > sysdate - 10000 and  e.hiredate <
sysdate
Plan hash value: 3896240783
-------------------------------------------------------------------------------------
| Id  | Operation          | Name | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------
|*  1 |  FILTER            |      |      1 |        |      2 |00:00:00.01 |       8 |
|*  2 |   TABLE ACCESS FULL| EMP  |      1 |      2 |      2 |00:00:00.01 |       8 |
-------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter(SYSDATE@!-10000    2 - filter(("E"."HIREDATE">SYSDATE@!-10000 AND "E"."HIREDATE"    
SQL> select * from emp e where e.hiredate > sysdate - 10000 and  e.hiredate < sysdate - 10000;
未选定行
SQL> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------
SQL_ID  0pywmdg63gf0m, child number 0
-------------------------------------
select * from emp e where e.hiredate > sysdate - 10000 and  e.hiredate
< sysdate - 10000
Plan hash value: 3896240783
---------------------------------------------------------------------------
| Id  | Operation          | Name | Starts | E-Rows | A-Rows |   A-Time   |
---------------------------------------------------------------------------
|*  1 |  FILTER            |      |      1 |        |      0 |00:00:00.01 |
|*  2 |   TABLE ACCESS FULL| EMP  |      0 |      1 |      0 |00:00:00.01 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter(SYSDATE@!-10000    2 - filter(("E"."HIREDATE">SYSDATE@!-10000 AND
              "E"."HIREDATE"

filter下挂两个节点:

        挂两个节点时,emp扫描了一次有14条记录,类似于nested loops, 而扫描如果是nested loop的话,则应该是14次,但是此处dept是3次。这是因为filter维护了一个hash table,如果有重复的记录可以减少扫描次数。此处由于emp中的deptno只有三个值,所以只扫描3次。

SQL> select /*+rule*/count(*) from emp
     where not exists (select 1
              from dept
             where dept.dname = 'SALE'
               and dept.deptno = emp.deptno);
  COUNT(*)
----------
        14

SQL> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));
PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------------------------
SQL_ID  0wjfqsm43j4z5, child number 0
-------------------------------------
select /*+rule*/count(*) from emp  where not exists (select 1           from dept
         where dept.dname = 'SALE'            and dept.deptno = emp.deptno)
Plan hash value: 3536434453
------------------------------------------------------------------------------------------
| Id  | Operation                     | Name    | Starts | A-Rows |   A-Time   | Buffers |
------------------------------------------------------------------------------------------
|   1 |  SORT AGGREGATE               |         |      1 |      1 |00:00:00.01 |      13 |
|*  2 |   FILTER                      |         |      1 |     14 |00:00:00.01 |      13 |
|   3 |    TABLE ACCESS FULL          | EMP     |      1|     14 |00:00:00.01 |       7 |
|*  4 |    TABLE ACCESS BY INDEX ROWID| DEPT    |      3 |      0 |00:00:00.01 |       6 |
|*  5 |     INDEX UNIQUE SCAN         | PK_DEPT |      3 |      3 |00:00:00.01 |       3 |
------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter( IS NULL)
   4 - filter("DEPT"."DNAME"='SALE')
   5 - access("DEPT"."DEPTNO"=:B1)
   

filter下挂三个节点:

       挂三个节点时,每当emp返回一条数据,都会驱动dept和bouns分别执行一次(注意的是在执行连接时每次只能两个表进行连接),由于filter使用了缓存技术,所以看到的dept只是扫描了3次,bonus扫描了14次。

SQL> select /*+rule*/count(*) from emp
     where not exists (select 1
              from dept
             where dept.dname = 'SALE'
               and dept.deptno = emp.deptno)
       and not exists (select 1 from bonus where bonus.ename = emp.ename);
  COUNT(*)
----------
        14
SQL> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  2wss1r100vqd9, child number 0
-------------------------------------
select /*+rule*/* from emp  where not exists (select 1           from dept
   where dept.dname = 'SALE'            and dept.deptno = emp.deptno)    and not
exists (select 1 from bonus where bonus.ename = emp.ename)
Plan hash value: 1445856646
-----------------------------------------------------------------------------------------
| Id  | Operation                    | Name    | Starts | A-Rows |   A-Time   | Buffers |
-----------------------------------------------------------------------------------------
|*  1 |  FILTER                      |         |      1 |     14 |00:00:00.01 |      56 |
|   2 |   TABLE ACCESS FULL          | EMP     |      1 |     14 |00:00:00.01 |       8 |
|*  3 |   TABLE ACCESS BY INDEX ROWID| DEPT    |      3 |      0 |00:00:00.01 |       6 |
|*  4 |    INDEX UNIQUE SCAN         | PK_DEPT |      3 |      3 |00:00:00.01 |       3 |
|*  5 |   TABLE ACCESS FULL          | BONUS   |     14 |      0 |00:00:00.01 |      42 |
-----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter(( IS NULL AND  IS NULL))
   3 - filter("DEPT"."DNAME"='SALE')
   4 - access("DEPT"."DEPTNO"=:B1)
   5 - filter("BONUS"."ENAME"=:B1)

SQL> select count(*) from emp
     where not exists (select 1
              from dept
             where dept.dname = 'SALE'
               and dept.deptno = emp.deptno);
  COUNT(*)
----------
        14

你可能感兴趣的:(Oracle,SQL优化)