Ух ты ... уже есть много ответов, но я думаю, что некоторые из них пропустили то, что я думаю, в чем смысл твоего вопроса.
В вашей таблице будут миллионы строк, и ваш составной индекс (CAR_TYPE, COLOR, CAR_BRAND) будет не очень избирательным. Вы ищете способ получить одну строку с последним SUBMIT_DT для данной записи в вашем составном индексе, не просматривая ВСЕ совпадения из этого индекса.
Ответ: добавьте SUBMIT_DT DESC
к вашему составному индексу
Давайте настроим тест:
create table matt_objects as select * from dba_objects;
-- This is our analog of your composite index
create index matt_objects_n1 on matt_objects ( object_type, owner );
exec dbms_stats.gather_table_stats(user,'MATT_OBJECTS');
Теперь давайте проследим это утверждение:
select object_name
from matt_objects
where object_type = 'TABLE'
and owner = 'INV'
order by last_ddl_time desc
fetch first 1 row only;
---------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 88 | 17 (6)| 00:00:01 |
|* 1 | VIEW | | 1 | 88 | 17 (6)| 00:00:01 |
|* 2 | WINDOW SORT PUSHED RANK | | 162 | 7290 | 17 (6)| 00:00:01 |
| 3 | TABLE ACCESS BY INDEX ROWID BATCHED| MATT_OBJECTS | 162 | 7290 | 16 (0)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | MATT_OBJECTS_N1 | 162 | | 3 (0)| 00:00:01 |
---------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("from$_subquery$_002"."rowlimit_$$_rownumber"<=1)
2 - filter(ROW_NUMBER() OVER ( ORDER BY INTERNAL_FUNCTION("LAST_DDL_TIME") DESC )<=1)
4 - access("OBJECT_TYPE"='TABLE' AND "OWNER"='INV')
Результат (из автотрассировки): 72 согласованных буфера чтения получает
Теперь давайте заменим ваш составной индекс на тот, который поможет нам больше:
drop index matt_objects_n1;
create index matt_objects_n1 on matt_objects ( object_type, owner, last_ddl_time desc );
exec dbms_stats.gather_table_stats(user,'MATT_OBJECTS');
.. и давайте снова проследим ту же фразу:
--------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 88 | 54 (2)| 00:00:01 |
| 1 | SORT ORDER BY | | 1 | 88 | 54 (2)| 00:00:01 |
|* 2 | VIEW | | 1 | 88 | 53 (0)| 00:00:01 |
|* 3 | WINDOW NOSORT STOPKEY | | 162 | 7290 | 53 (0)| 00:00:01 |
| 4 | TABLE ACCESS BY INDEX ROWID| MATT_OBJECTS | 162 | 7290 | 53 (0)| 00:00:01 |
|* 5 | INDEX RANGE SCAN | MATT_OBJECTS_N1 | 162 | | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("from$_subquery$_002"."rowlimit_$$_rownumber"<=1)
3 - filter(ROW_NUMBER() OVER ( ORDER BY SYS_OP_DESCEND("LAST_DDL_TIME"))<=1)
5 - access("OBJECT_TYPE"='TABLE' AND "OWNER"='INV')
Результат (из автотрассировки): 5 последовательных операций чтения
Этот индекс очень помог. Заметили, что план отличается? «WINDOW SORT PUSHED RANK» заменен на «WINDOW NOSORT STOPKEY». С учетом того, что индекс уже отсортирован так, как вы хотите (в порядке убывания), Oracle знает, что он может читать строки индекса по порядку и останавливаться после первого, выполняя запрос с гораздо меньшими усилиями.
Интересно отметить, что стоимость 2-го запроса выше, чем стоимость 1-го запроса, хотя производительность 2-го запроса более чем в 10 раз выше. Это просто говорит о том, что «стоимость» является приблизительной, и иногда ее следует брать с крошкой соли.