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条记录。