此脚本不依赖统计信息。

    我们在第 1 章中讲到,回表次数太多会严重影响 SQL 性能。当 SQL 走索引回表只访问表中少部分字段,我们可以将这些字段与过滤条件组合起来建立为一个组合索引,这样就能避免回表,从而提升查询性能。下面脚本抓出回表只访问少数字段的 SQL。

    1. select a.sql_id,
    2. a.sql_text,
    3. d.table_name,
    4. REGEXP_COUNT(b.projection, ']') ||'/'|| d.column_cnt column_cnt,
    5. c.size_mb,
    6. b.FILTER_PREDICATES filter
    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. (select owner, table_name, count(*) column_cnt
    13. from dba_tab_cols
    14. group by owner, table_name) d
    15. where a.sql_id = b.sql_id
    16. and a.child_number = b.child_number
    17. and b.object_owner = c.owner
    18. and b.object_name = c.segment_name
    19. and b.object_owner = d.owner
    20. and b.object_name = d.table_name
    21. and c.owner = 'SCOTT'
    22. and b.operation = 'TABLE ACCESS'
    23. and b.options = 'BY INDEX ROWID'
    24. and REGEXP_COUNT(b.projection, ']')/d.column_cnt<0.25
    25. order by 5 desc;

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

    1. select object_name from t2 where object_id<1000;
    1. 942 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 | | 917 | 27510 | 19 (0)|00:00:01|
    9. | 1 | TABLE ACCESS BY INDEX ROWID| T2 | 917 | 27510 | 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. 2 - access("OBJECT_ID"<1000)
    15. Statistics
    16. ----------------------------------------------------------
    17. 0 recursive calls
    18. 0 db block gets
    19. 141 consistent gets
    20. 0 physical reads
    21. 0 redo size
    22. 24334 bytes sent via SQL*Net to client
    23. 1102 bytes received via SQL*Net from client
    24. 64 SQL*Net roundtrips to/from client
    25. 0 sorts (memory)
    26. 0 sorts (disk)
    27. 942 rows processed

    因为上面 SQL 回表只访问了 1 个字段,我们可以利用脚本将上面 SQL 抓出。

    1. select a.sql_id,
    2. a.sql_text,
    3. d.table_name,
    4. REGEXP_COUNT(b.projection, ']') ||'/'|| d.column_cnt column_cnt,
    5. c.size_mb,
    6. b.FILTER_PREDICATES filter
    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. (select owner, table_name, count(*) column_cnt
    13. from dba_tab_cols
    14. group by owner, table_name) d
    15. where a.sql_id = b.sql_id
    16. and a.child_number = b.child_number
    17. and b.object_owner = c.owner
    18. and b.object_name = c.segment_name
    19. and b.object_owner = d.owner
    20. and b.object_name = d.table_name
    21. and c.owner = 'SCOTT'
    22. and b.operation = 'TABLE ACCESS'
    23. and b.options = 'BY INDEX ROWID'
    24. and REGEXP_COUNT(b.projection, ']')/d.column_cnt<0.25
    25. order by 5 desc;
    1. SQL_ID SQL_TEXT TABLE_NAME COLUMN_CNT SIZE_MB FILTER
    2. -------------- -------------------------- ---------- ---------- ---------- ------
    3. bzyprvnc41ak8 select object_name from t2 T2 1/15 9
    4. where object_id<1000