Индекс действительно светится, когда базе данных не нужно переходить к каждой строке в таблице, чтобы получить ваши результаты.Так что COUNT(*)
не лучший пример.Возьмем для примера:
alter session set statistics_level = 'ALL';
create table mytable as select * from all_objects;
select * from mytable where owner = 'SYS' and object_name = 'DUAL';
---------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 300 |00:00:00.01 | 12 |
| 1 | TABLE ACCESS FULL| MYTABLE | 1 | 19721 | 300 |00:00:00.01 | 12 |
---------------------------------------------------------------------------------------
Итак, здесь база данных выполняет полное сканирование таблицы (TABLE ACCESS FULL
), что означает, что она должна посещать каждую строку в базе данных, что означает, что она должна загружать всеблок с диска.Много ввода / вывода.Оптимизатор догадался, что он найдет 15000 строк, но я знаю, что есть только одна.
Сравните это с этим:
create index myindex on mytable( owner, object_name );
select * from mytable where owner = 'SYS' and object_name = 'JOB$';
select * from table( dbms_xplan.display_cursor( null, null, 'ALLSTATS LAST' ));
----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads |
----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 | 3 | 2 |
| 1 | TABLE ACCESS BY INDEX ROWID| MYTABLE | 1 | 2 | 1 |00:00:00.01 | 3 | 2 |
|* 2 | INDEX RANGE SCAN | MYINDEX | 1 | 1 | 1 |00:00:00.01 | 2 | 2 |
----------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("OWNER"='SYS' AND "OBJECT_NAME"='JOB$')
Здесь, потому что есть индекс, он делает INDEX RANGE SCAN
, чтобы найти значения rowid для таблицы, которые соответствуют нашим критериям.Затем он переходит к самой таблице (TABLE ACCESS BY INDEX ROWID
) и ищет только те строки, которые нам нужны, и может сделать это эффективно, потому что у него есть rowid.
И даже лучше, если вам что-то нужно искатьэто полностью в индексе, сканирование даже не должно возвращаться к базовой таблице.Индекса достаточно:
select count(*) from mytable where owner = 'SYS';
select * from table( dbms_xplan.display_cursor( null, null, 'ALLSTATS LAST' ));
------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads |
------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 | 46 | 46 |
| 1 | SORT AGGREGATE | | 1 | 1 | 1 |00:00:00.01 | 46 | 46 |
|* 2 | INDEX RANGE SCAN| MYINDEX | 1 | 8666 | 9294 |00:00:00.01 | 46 | 46 |
------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("OWNER"='SYS')
Поскольку мой запрос включал столбец владельца и он содержится в индексе, ему никогда не нужно возвращаться к базовой таблице, чтобы что-то там искать.Таким образом, сканирования индекса достаточно, затем выполняется агрегирование для подсчета строк.Этот сценарий несколько менее идеален, потому что индекс включен (владелец, имя_объекта), а не только владелец, но это определенно лучше, чем полное сканирование таблицы на главной таблице.