此脚本不依赖统计信息。

    当 where 子查询没能 unnest,执行计划中就会出现 FILTER,对于此类 SQL,我们应该在上线之前对其进行改写,避免执行计划中出现 FILTER,以下脚本可以抓出 where 子查询没能 unnest 的 SQL。

    1. select parsing_schema_name schema, sql_id, sql_text
    2. from v$sql
    3. where parsing_schema_name = 'SCOTT'
    4. and (sql_id, child_number) in
    5. (select sql_id, child_number
    6. from v$sql_plan
    7. where operation = 'FILTER'
    8. and filter_predicates like '%IS NOT NULL%'
    9. minus
    10. select sql_id, child_number
    11. from v$sql_plan
    12. where object_owner = 'SYS');

    我们在 Scott 账户中运行如下 SQL 并且查看执行计划。

    1. select *
    2. from dept
    3. where exists (select null
    4. from emp
    5. where dept.deptno = emp.deptno
    6. start with empno = 7698
    7. connect by prior empno = mgr);
    1. DEPTNO DNAME LOC
    2. ---------- ---------- ---------------------------------------
    3. 30 SALES CHICAGO
    4. Elapsed: 00:00:00.00
    5. Execution Plan
    6. ----------------------------------------------------------
    7. Plan hash value: 4210865686
    8. -------------------------------------------------------------------------------------
    9. | Id|Operation |Name|Rows|Bytes| Cost(%CPU)|Time |
    10. -------------------------------------------------------------------------------------
    11. | 0|SELECT STATEMENT | | 1| 20| 9 (0)|00:00:01|
    12. |* 1| FILTER | | | | | |
    13. | 2| TABLE ACCESS FULL |DEPT| 4| 80| 3 (0)|00:00:01|
    14. |* 3| FILTER | | | | | |
    15. |* 4| CONNECT BY NO FILTERING WITH SW (UNIQUE)| | | | | |
    16. | 5| TABLE ACCESS FULL |EMP | 14| 154| 3 (0)|00:00:01|
    17. -------------------------------------------------------------------------------------
    18. Predicate Information (identified by operation id):
    19. ---------------------------------------------------
    20. 1 - filter( EXISTS (SELECT 0 FROM "EMP" "EMP" WHERE "EMP"."DEPTNO"=:B1 START WITH
    21. "EMPNO"=7698 CONNECT BY "MGR"=PRIOR "EMPNO"))
    22. 3 - filter("EMP"."DEPTNO"=:B1)
    23. 4 - access("MGR"=PRIOR "EMPNO")
    24. filter("EMPNO"=7698)
    25. Statistics
    26. ----------------------------------------------------------
    27. 0 recursive calls
    28. 0 db block gets
    29. 36 consistent gets
    30. 0 physical reads
    31. 0 redo size
    32. 550 bytes sent via SQL*Net to client
    33. 419 bytes received via SQL*Net from client
    34. 2 SQL*Net roundtrips to/from client
    35. 8 sorts (memory)
    36. 0 sorts (disk)
    37. 1 rows processed

    以上 SQL 执行计划中出现了 FILTER,我们通过脚本抓出走了 FILTER 的 SQL。

    1. select parsing_schema_name schema, sql_id, sql_text
    2. from v$sql
    3. where parsing_schema_name = 'SCOTT'
    4. and (sql_id, child_number) in
    5. (select sql_id, child_number
    6. from v$sql_plan
    7. where operation = 'FILTER'
    8. and filter_predicates like '%IS NOT NULL%'
    9. minus
    10. select sql_id, child_number
    11. from v$sql_plan
    12. where object_owner = 'SYS');
    1. SCHEMA SQL_ID SQL_TEXT
    2. ---------- --------------- ---------------------------------------------
    3. SCOTT 8rmn2fn149y2z select * from dept where exists (select null from emp
    4. where dept.deptno = emp.deptno start with em
    5. pno = 7698 connect by prior empno = mgr)