此脚本依赖统计信息。
如果一个索引选择性很低,说明列数据分布不均衡。当 SQL 走了数据分布不均衡列的索引,很容易走错执行计划,此时我们应该检查 SQL 语句中是否有其他过滤条件,如果有其他过滤条件,可以考虑建立组合索引,将选择性高的列作为引导列;如果没有其他过滤条件,应该检查列是否有收集直方图。以下脚本抓出走了低选择性索引的 SQL。
select c.sql_id,
c.sql_text,
b.index_name,
e.table_name,
trunc(d.num_distinct / e.num_rows * 100, 2) selectivity,
d.num_distinct,
e.num_rows
from v$sql_plan a,
(select *
from (select index_owner,
index_name,
table_owner,
table_name,
column_name,
count(*) over(partition by index_owner, index_name, table_owner, table_name) cnt
from dba_ind_columns)
where cnt = 1) b,
v$sql c,
dba_tab_col_statistics d,
dba_tables e
where a.object_owner = b.index_owner
and a.object_name = b.index_name
and b.index_owner = 'SCOTT'
and a.access_predicates is not null
and a.sql_id = c.sql_id
and a.child_number = c.child_number
and d.owner = e.owner
and d.table_name = e.table_name
and b.table_owner = e.owner
and b.table_name = e.table_name
and d.column_name = b.column_name
and d.table_name = b.table_name
and d.num_distinct / e.num_rows < 0.1;
我们在 Scott 账户中执行如下 SQL 并且查看执行计划。
select * from t where owner='SYS';
23654 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 2480948561
-------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost(%CPU)|Time |
-------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2346 | 222K| 68 (0)|00:00:01|
| 1 | TABLE ACCESS BY INDEX ROWID| T | 2346 | 222K| 68 (0)|00:00:01|
|* 2 | INDEX RANGE SCAN | IDX_OWNER | 2346 | | 6 (0)|00:00:01|
-------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("OWNER"='SYS')
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
3819 consistent gets
0 physical reads
0 redo size
2680901 bytes sent via SQL*Net to client
17756 bytes received via SQL*Net from client
1578 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
23654 rows processed
我们使用脚本将以上 SQL 抓出。
select c.sql_id,
c.sql_text,
b.index_name,
e.table_name,
trunc(d.num_distinct / e.num_rows * 100, 2) selectivity,
d.num_distinct,
e.num_rows
from v$sql_plan a,
(select *
from (select index_owner,
index_name,
table_owner,
table_name,
column_name,
count(*) over(partition by index_owner, index_name, table_owner, table_name) cnt
from dba_ind_columns)
where cnt = 1) b,
v$sql c,
dba_tab_col_statistics d,
dba_tables e
where a.object_owner = b.index_owner
and a.object_name = b.index_name
and b.index_owner = 'SCOTT'
and a.access_predicates is not null
and a.sql_id = c.sql_id
and a.child_number = c.child_number
and d.owner = e.owner
and d.table_name = e.table_name
and b.table_owner = e.owner
and b.table_name = e.table_name
and d.column_name = b.column_name
and d.table_name = b.table_name
and d.num_distinct / e.num_rows < 0.1;
SQL_ID SQL_TEXT INDEX_NAME TABLE_NAME SELECTIVITY NUM_DISTINCT NUM_ROWS
----------- --------------------------- ---------- ----------- ---- -------- --------
6gzd8z5vm5k0t select * from t where owner='SYS' IDX_OWNER T .04 31 72734