客户发来一个比较长的SQL,说是跑了几小时还没出结果;这是制造业的一个统计型SQL,发来SQL文本一看SQL比较长要翻好几屏,一时半会也梳理不清;按照DBA的思路先尝试查看执行计划,看是否能通过索引或执行计划的改变在不修改SQL(也就不需要了解SQL业务逻辑)情况下优化;从执行计划来看cost 13676,具体的执行步骤在INDEX SKIP SCAN部分消耗比较大;
关于索引跳跃扫描 index skip scan,它是有一定的适用场景的,如需要满足数据库版本9i及以后CBO模式且有统计信息,适用所有类型复合B树索引(包括唯一和非唯一索引)。当在WHERE条件中没有对目标索引的前导列指定查询条件,但又对非前导列指定了查询条件时依然可以使用该索引(实际是ORACLE对这种SQL进行了等价改写,对索引的前导列所有distinct值做了遍历,以此值做查询再合并结果)。
在ORACLE中索引跳跃扫描适用于:目标索引前导列的distinct值数量少,后续非前导列的可选择性好的情况。 这个环境中WHERE条件中的WHERE MSGFID =列的选择率不太好(总共150万行数据,distinct值约170个),当前存在的组合索引是前导列选择率较好的;与用户沟通了解此表的DML操作也较多,也不适用创建位图索引;让用户尝试创建一个单独包含MSGFID 列的索引,效果还是比较好的,修改后的执行计划使用index range scan,70秒可以执行完成,能满足业务需求;
如下实验测试一下索引跳跃扫描 index skip scan的效率:
测试环境linux+oracle 11.2.0.4:SQL> create table test4 as select * from dba_objects; Table created. SQL> exec dbms_stats.gather_table_stats('BYS','TEST4'); PL/SQL procedure successfully completed.
使用dba_objects的三个列,共 87250数据,owner 对应用户约10多个用户,object_type对应类型约10多个;OBJECT_ID是惟一的。
1.首选测试不使用索引时的执行计划,COST=348:
2.测试test4(owner,OBJECT_TYPE)做组合索引时INDEX SKIP SCAN执行计划,COST=143:
3.测试test4(OBJECT_ID,OBJECT_TYPE)做组合索引时INDEX SKIP SCAN执行计划,COST=331:
4.测试test4(OBJECT_TYPE)做单列索引时INDEX SKIP SCAN执行计划,COST=87:
测试结果比较明显;ORACLE中索引跳跃扫描适用于目标索引前导列的distinct值数量少,后续非前导列的可选择性好的情况。 ================== 1.首选测试不使用索引时的执行计划,COST=348:
SQL> select count(object_name) from test4 where object_TYPE='TABLE';
COUNT(OBJECT_NAME)
------------------
3181
Execution Plan
----------------------------------------------------------
Plan hash value: 2292881717
----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 34 | 348 (1)| 00:00:05 |
| 1 | SORT AGGREGATE | | 1 | 34 | | |
|* 2 | TABLE ACCESS FULL| TEST4 | 1983 | 67422 | 348 (1)| 00:00:05 |
----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("OBJECT_TYPE"='TABLE')
2.测试test4(owner,OBJECT_TYPE)做组合索引时INDEX SKIP SCAN执行计划,,COST=143:
SQL> create index idx03_test4 on test4(owner,OBJECT_TYPE);
Index created.
SQL> select count(object_name) from test4 where object_TYPE='TABLE';
COUNT(OBJECT_NAME)
------------------
3181
Execution Plan
----------------------------------------------------------
Plan hash value: 2478250731
--------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 34 | 143 (0)| 00:00:02 |
| 1 | SORT AGGREGATE | | 1 | 34 | | |
| 2 | TABLE ACCESS BY INDEX ROWID| TEST4 | 1983 | 67422 | 143 (0)| 00:00:02 |
|* 3 | INDEX SKIP SCAN | IDX03_TEST4 | 1983 | | 60 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("OBJECT_TYPE"='TABLE')
filter("OBJECT_TYPE"='TABLE')
3.测试test4(OBJECT_ID,OBJECT_TYPE)做组合索引时INDEX SKIP SCAN执行计划,COST=331:
SQL> create index idx01_test4 on test4(OBJECT_ID,OBJECT_TYPE);
Index created.
SQL> select count(object_name) from test4 where object_TYPE='TABLE';
COUNT(OBJECT_NAME)
------------------
3181
Execution Plan
----------------------------------------------------------
Plan hash value: 1133214586
--------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 34 | 331 (0)| 00:00:04 |
| 1 | SORT AGGREGATE | | 1 | 34 | | |
| 2 | TABLE ACCESS BY INDEX ROWID| TEST4 | 1983 | 67422 | 331 (0)| 00:00:04 |
|* 3 | INDEX SKIP SCAN | IDX01_TEST4 | 1983 | | 300 (0)| 00:00:04 |
-------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("OBJECT_TYPE"='TABLE')
filter("OBJECT_TYPE"='TABLE')
4.测试test4(OBJECT_TYPE)做单列索引时INDEX SKIP SCAN执行计划,COST=87:
SQL> create index idx02_test4 on test4(OBJECT_TYPE);
Index created.
SQL> select count(object_name) from test4 where object_TYPE='TABLE';
COUNT(OBJECT_NAME)
------------------
3181
Execution Plan
----------------------------------------------------------
Plan hash value: 1992435611
--------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 34 | 87 (0)| 00:00:02 |
| 1 | SORT AGGREGATE | | 1 | 34 | | |
| 2 | TABLE ACCESS BY INDEX ROWID| TEST4 | 1983 | 67422 | 87 (0)| 00:00:02 |
|* 3 | INDEX RANGE SCAN | IDX02_TEST4 | 1983 | | 6 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("OBJECT_TYPE"='TABLE')