SQL> create table test(object_id NUMBER,object_name varchar2(10)); Table created. SQL> begin 2 for i in 1 .. 1000000 3 loop 4 insert into test values (1,'TEST'); 5 end loop; 6 end; 7 / PL/SQL procedure successfully completed. SQL> begin 2 for i in 1 .. 300 3 loop 4 insert into test values (2,'TEST2'); 5 end loop; 6 end; 7 / PL/SQL procedure successfully completed. SQL> begin 2 for i in 1 .. 49700 3 loop 4 insert into test values (2,i||'TEST2'); 5 end loop; 6 end; 7 / SQL> select count(*), object_id from test group by object_id; COUNT(*) OBJECT_ID ---------- ---------- 1000000 1 50000 2 SQL> select count(*) from test where object_name='TEST2' and object_id=2; COUNT(*) ---------- 300 SQL> select count(*) from test where object_name<>'TEST2' and object_id=2; COUNT(*) ---------- 49700 第一种情况: TABLE ACCESS BY INDEX ROWID 前面有 *,并且INDEX RANGE SCAN返回5w条,5W个rowid,过滤object_name之后只剩下 300条 那么就建组合索引 在object_id列创建索引 SQL> create index test_idx1 on test(OBJECT_ID); Index created. BEGIN DBMS_STATS.GATHER_TABLE_STATS(ownname => 'TEST', tabname => 'TEST', estimate_percent => 100, method_opt => 'for all columns size skewonly', no_invalidate => FALSE, degree => 8, cascade => TRUE); END; SQL> select * from test where object_id=2 and object_name='TEST2'; 300 rows selected. Execution Plan ---------------------------------------------------------- Plan hash value: 2624864549 ----------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ----------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 8 | 198 (1)| 00:00:03 | |* 1 | TABLE ACCESS BY INDEX ROWID| TEST | 1 | 8 | 198 (1)| 00:00:03 | |* 2 | INDEX RANGE SCAN | TEST_IDX1 | 50000 | | 100 (0)| 00:00:02 | ----------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("OBJECT_NAME"='TEST2') 2 - access("OBJECT_ID"=2) Statistics ---------------------------------------------------------- 1 recursive calls 0 db block gets 270 consistent gets 0 physical reads 0 redo size 4441 bytes sent via SQL*Net to client 628 bytes received via SQL*Net from client 21 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 300 rows processed SQL> select count(*) from test; COUNT(*) ---------- 1050000 SQL> select count(*) from test where object_id=2; COUNT(*) ---------- 50000 SQL> select count(*) from test where object_id=2 and object_name='TEST2'; COUNT(*) ---------- 300 TEST表总共1050000行,其中INDEX RANGE SCAN后返回50000行,过滤object_name之后只剩下 300条 那么就建组合索引 建立组合索引; SQL> select * from test where object_id=2 and object_name='TEST2'; 300 rows selected. Execution Plan ---------------------------------------------------------- Plan hash value: 415678261 ------------------------------------------------------------------------------ | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 1 | 8 | 3 (0)| 00:00:01 | |* 1 | INDEX RANGE SCAN| TEST_IDX2 | 1 | 8 | 3 (0)| 00:00:01 | ------------------------------------------------------------------------------ Predicate Information (identified by operation id): --------------------------------------------------- 1 - access("OBJECT_ID"=2 AND "OBJECT_NAME"='TEST2') Statistics ---------------------------------------------------------- 1 recursive calls 0 db block gets 24 consistent gets 0 physical reads 0 redo size 4441 bytes sent via SQL*Net to client 628 bytes received via SQL*Net from client 21 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 300 rows processed 如果组合起来的话不需要过滤,不需要回表,直接返回300行 第2种情况: INDEX RANGE SCAN 返回5W 回表过滤后返回数据还很多,不需要创建组合索引的情况。 begin for i in 1 .. 1000000 loop insert into test values (1,'TEST'); end loop; end; begin for i in 1 .. 49000 loop insert into test values (2,'TEST2'); end loop; end; begin for i in 1 .. 100 loop insert into test values (2,'TEST3'); end loop; end; SQL> select count(*),object_id,object_name from test where object_id=2 group by object_id,object_name 2 3 ; COUNT(*) OBJECT_ID OBJECT_NAM ---------- ---------- ---------- 49000 2 TEST2 100 2 TEST3 SQL> select * from test where object_id=2 and object_name='TEST2'; 49000 rows selected. Execution Plan ---------------------------------------------------------- Plan hash value: 2624864549 ----------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ----------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 2293 | 18344 | 188 (1)| 00:00:03 | |* 1 | TABLE ACCESS BY INDEX ROWID| TEST | 2293 | 18344 | 188 (1)| 00:00:03 | |* 2 | INDEX RANGE SCAN | TEST_IDX1 | 49100 | | 98 (0)| 00:00:02 | ----------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("OBJECT_NAME"='TEST2') 2 - access("OBJECT_ID"=2) Statistics ---------------------------------------------------------- 1 recursive calls 0 db block gets 6715 consistent gets 98 physical reads 0 redo size 660310 bytes sent via SQL*Net to client 36345 bytes received via SQL*Net from client 3268 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 49000 rows processed INDEX RANGE SCAN 返回49100行,过滤"OBJECT_NAME"='TEST2'后返回49000行 创建组合索引: SQL> create index test_Idx2 on test(object_id,object_name); Index created. SQL> select * from test where object_id=2 and object_name='TEST2'; 49000 rows selected. Execution Plan ---------------------------------------------------------- Plan hash value: 415678261 ------------------------------------------------------------------------------ | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 2293 | 18344 | 9 (0)| 00:00:01 | |* 1 | INDEX RANGE SCAN| TEST_IDX2 | 2293 | 18344 | 9 (0)| 00:00:01 | ------------------------------------------------------------------------------ Predicate Information (identified by operation id): --------------------------------------------------- 1 - access("OBJECT_ID"=2 AND "OBJECT_NAME"='TEST2') Statistics ---------------------------------------------------------- 1 recursive calls 0 db block gets 3398 consistent gets 139 physical reads 0 redo size 660310 bytes sent via SQL*Net to client 36345 bytes received via SQL*Net from client 3268 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 49000 rows processed 可以看到就算INDEX RANGE SCAN返回49100行,通过过滤"OBJECT_NAME"='TEST2',虽然只过滤了100行,但还是走组合索引效率高。 ------------------------------------------------------------------------------------------------------------------- 那什么情况会出现组合索引比单列索引效率低呢? select XLBHZ XLBHZ,ZDBHZ ZDBHZ, jyrqz, decode(JCZBZ, '0', sum(JYBSZ), 0) TOKEN_JKLZZ, decode(JCZBZ, '1', sum(JYBSZ), 0) TOKEN_CKLZZ, 0 CASH_JKLZZ, 0 CASH_CKLZZ, 0 TFT_JKLZZ, 0 TFT_CKLZZ from brt_hzjcztok where 1=1 AND XLBHZ IN ('0001','0002') AND JYRQZ>='20130727' AND JYRQZ<='20130728' group by jyrqz, JCZBZ,XLBHZ,ZDBHZ Execution Plan ---------------------------------------------------------- Plan hash value: 1433290359 --------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 3757 | 93925 | 2604 (1)| 00:00:32 | | 1 | SORT GROUP BY NOSORT | | 3757 | 93925 | 2604 (1)| 00:00:32 | | 2 | TABLE ACCESS BY INDEX ROWID| BRT_HZJCZTOK | 3757 | 93925 | 2604 (1)| 00:00:32 | |* 3 | INDEX RANGE SCAN | IDX_HZJCZTOK_UNION | 3757 | | 20 (0)| 00:00:01 | --------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 3 - access("JYRQZ">='20130727' AND "JYRQZ"<='20130728') filter("XLBHZ"='0001' OR "XLBHZ"='0002') 原先索引: IDX_HZJCZTOK_UNION(JYRQZ, JCZBZ, XLBHZ, ZDBHZ) Statistics ---------------------------------------------------------- 0 recursive calls 0 db block gets 2670 consistent gets 0 physical reads 0 redo size 10149 bytes sent via SQL*Net to client 646 bytes received via SQL*Net from client 16 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 224 rows processed Execution Plan ---------------------------------------------------------- Plan hash value: 2641846428 ----------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ----------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 3757 | 93925 | 203 (5)| 00:00:03 | | 1 | HASH GROUP BY | | 3757 | 93925 | 203 (5)| 00:00:03 | |* 2 | TABLE ACCESS FULL| BRT_HZJCZTOK | 3757 | 93925 | 201 (4)| 00:00:03 | ----------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - filter("JYRQZ">='20130727' AND "JYRQZ"<='20130728' AND ("XLBHZ"='0001' OR "XLBHZ"='0002')) Statistics ---------------------------------------------------------- 1 recursive calls 0 db block gets 947 consistent gets 0 physical reads 0 redo size 10333 bytes sent via SQL*Net to client 646 bytes received via SQL*Net from client 16 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 224 rows processed 同个sql,一个强制索引,一个没有强制走全表,你觉得这2个执行计划哪个好? --select count(*) from brt_hzjcztok where JYRQZ>='20130727' AND JYRQZ<='20130728' 3885 全部数据呢 ---select count(*) from brt_hzjcztok 128023 SQL> select XLBHZ,count(*) from brt_hzjcztok group by XLBHZ order by count(*) desc; XLBH COUNT(*) ---- ---------- 0002 64131 0001 63849 0100 43 create index brt_hzjcztok_idx1 on brt_hzjcztok(JYRQZ,XLBHZ); 或者直接创建 create index brt_hzjcztok_idx1 on brt_hzjcztok(JYRQZ); 原来网友自己创建的索引: IDX_HZJCZTOK_UNION(JYRQZ, JCZBZ, XLBHZ, ZDBHZ) ------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------- create index brt_hzjcztok_idx1 on brt_hzjcztok(JYRQZ,XLBHZ); Execution Plan ---------------------------------------------------------- Plan hash value: 497111649 -------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 3757 | 93925 | 85 (3)| 00:00:02 | | 1 | HASH GROUP BY | | 3757 | 93925 | 85 (3)| 00:00:02 | | 2 | TABLE ACCESS BY INDEX ROWID| BRT_HZJCZTOK | 3757 | 93925 | 83 (0)| 00:00:02 | |* 3 | INDEX RANGE SCAN | BRT_HZJCZTOK_IDX1 | 3757 | | 16 (0)| 00:00:01 | -------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 3 - access("JYRQZ">='20130727' AND "JYRQZ"<='20130728') filter("XLBHZ"='0001' OR "XLBHZ"='0002') Statistics ---------------------------------------------------------- 0 recursive calls 0 db block gets 79 consistent gets 0 physical reads 0 redo size 10363 bytes sent via SQL*Net to client 646 bytes received via SQL*Net from client 16 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 224 rows processed SQL> / DROP INDEX brt_hzjcztok_idx1 create index brt_hzjcztok_idx1 on brt_hzjcztok(JYRQZ); 224 rows selected. Execution Plan ---------------------------------------------------------- Plan hash value: 497111649 -------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 3757 | 93925 | 48 (5)| 00:00:01 | | 1 | HASH GROUP BY | | 3757 | 93925 | 48 (5)| 00:00:01 | |* 2 | TABLE ACCESS BY INDEX ROWID| BRT_HZJCZTOK | 3757 | 93925 | 46 (0)| 00:00:01 | |* 3 | INDEX RANGE SCAN | BRT_HZJCZTOK_IDX1 | 3757 | | 12 (0)| 00:00:01 | -------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - filter("XLBHZ"='0001' OR "XLBHZ"='0002') 3 - access("JYRQZ">='20130727' AND "JYRQZ"<='20130728') Statistics ---------------------------------------------------------- 1 recursive calls 0 db block gets 44 consistent gets 0 physical reads 0 redo size 10333 bytes sent via SQL*Net to client 646 bytes received via SQL*Net from client 16 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 224 rows processed 这里是--select count(*) from brt_hzjcztok where JYRQZ>='20130727' AND JYRQZ<='20130728' 3885 走INDEX RANGE SCAN后返回3885条记录后,在过滤43条记录。