此脚本依赖统计信息。

    我们在第 1 章中讲到,回表次数太多会严重影响 SQL 性能。当执行计划中发生了回表再过滤并且过滤字段的选择性比较高,我们可以将过滤字段包含在索引中避免回表再过滤,从而减少回表次数,提升查询性能。以下脚本抓出回表再过滤选择性较高的列。

    1. select a.sql_id,
    2. a.sql_text,
    3. f.table_name,
    4. c.size_mb,
    5. e.column_name,
    6. round(e.num_distinct / f.num_rows * 100, 2) selectivity
    7. from v$sql a,
    8. v$sql_plan b,
    9. (select owner, segment_name, sum(bytes / 1024 / 1024) size_mb
    10. from dba_segments
    11. group by owner, segment_name) c,
    12. dba_tab_col_statistics e,
    13. dba_tables f
    14. where a.sql_id = b.sql_id
    15. and a.child_number = b.child_number
    16. and b.object_owner = c.owner
    17. and b.object_name = c.segment_name
    18. and e.owner = f.owner
    19. and e.table_name = f.table_name
    20. and b.object_owner = f.owner
    21. and b.object_name = f.table_name
    22. and instr(b.filter_predicates, e.column_name) > 0
    23. and (e.num_distinct / f.num_rows) > 0.1
    24. and c.owner = 'SCOTT'
    25. and b.operation = 'TABLE ACCESS'
    26. and b.options = 'BY INDEX ROWID'
    27. and e.owner = 'SCOTT'
    28. order by 4 desc;

    我们在 Scott 账户中运行如下 SQL。

    1. select * from t2 where object_id<1000 and object_name like 'T%';
    1. 26 rows selected.
    2. Execution Plan
    3. ----------------------------------------------------------
    4. Plan hash value: 921640168
    5. -------------------------------------------------------------------------------------
    6. | Id | Operation | Name | Rows | Bytes | Cost(%CPU)|Time |
    7. -------------------------------------------------------------------------------------
    8. | 0 | SELECT STATEMENT | | 12 | 1164 | 19 (0)|00:00:01|
    9. |* 1 | TABLE ACCESS BY INDEX ROWID| T2 | 12 | 1164 | 19 (0)|00:00:01|
    10. |* 2 | INDEX RANGE SCAN | IDX_T2_ID | 917 | | 4 (0)|00:00:01|
    11. -------------------------------------------------------------------------------------
    12. Predicate Information (identified by operation id):
    13. ---------------------------------------------------
    14. 1 - filter("OBJECT_NAME" LIKE 'T%')
    15. 2 - access("OBJECT_ID"<1000)
    16. Statistics
    17. ----------------------------------------------------------
    18. 1 recursive calls
    19. 0 db block gets
    20. 19 consistent gets
    21. 0 physical reads
    22. 0 redo size
    23. 2479 bytes sent via SQL*Net to client
    24. 430 bytes received via SQL*Net from client
    25. 3 SQL*Net roundtrips to/from client
    26. 0 sorts (memory)
    27. 0 sorts (disk)
    28. 26 rows processed

    执行计划中发生了回表再过滤,过滤字段的选择性较高,我们利用脚本将以上 SQL 抓出。

    1. select a.sql_id,
    2. a.sql_text,
    3. f.table_name,
    4. c.size_mb,
    5. e.column_name,
    6. round(e.num_distinct / f.num_rows * 100, 2) selectivity
    7. from v$sql a,
    8. v$sql_plan b,
    9. (select owner, segment_name, sum(bytes / 1024 / 1024) size_mb
    10. from dba_segments
    11. group by owner, segment_name) c,
    12. dba_tab_col_statistics e,
    13. dba_tables f
    14. where a.sql_id = b.sql_id
    15. and a.child_number = b.child_number
    16. and b.object_owner = c.owner
    17. and b.object_name = c.segment_name
    18. and e.owner = f.owner
    19. and e.table_name = f.table_name
    20. and b.object_owner = f.owner
    21. and b.object_name = f.table_name
    22. and instr(b.filter_predicates, e.column_name) > 0
    23. and (e.num_distinct / f.num_rows) > 0.1
    24. and c.owner = 'SCOTT'
    25. and b.operation = 'TABLE ACCESS'
    26. and b.options = 'BY INDEX ROWID'
    27. and e.owner = 'SCOTT'
    28. order by 4 desc;
    1. SQL_ID SQL_TEXT TABLE_NAME SIZE_MB COLUMN_NAME SELECTIVITY
    2. ------------ ---------------------------- ---------- ------- ----------- ----------
    3. faqathsuy5w3d select * from t2 where object_id T2 9 OBJECT_NAME 0.94
    4. <1000 and object_name like 'T%'