Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
SELECT * FROM 'people' WHERE 'cid' IN (SELECT 'id' FROM 'cities' WHERE %condtion%)
— преобразование вложенных подзапросов в join (тоже есть отдельный хинт unnest / no_unnest).
Этот способ трансформации запроса впервые появился в Oracle 10.2, но в достаточно ограниченном виде. Он поддерживал только inner join, написанный в традиционном (не-ANSI) стиле Oracle.Непонятно, что речь? Вообще-то почти все ANSI-запросы трансформируются оптимизатором оракла в оракловые(исключений немного: native full outer join и outer с предикатами по столбцам двух и более разных таблиц).
не могли бы Вы рассказать, в какой момент выполнения запроса происходит это преобразование?
outer с предикатами по столбцам двух и более разных таблиц
---------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 164 | 3608 | 167 (0)| 00:00:01 |
|* 1 | FILTER | | | | | |
| 2 | TABLE ACCESS BY INDEX ROWID BATCHED| XT_BIG_TAB | 164 | 3608 | 167 (0)| 00:00:01 |
|* 3 | INDEX SKIP SCAN | IX_BIG_TAB | 164 | | 166 (0)| 00:00:01 |
---------------------------------------------------------------------------------------------------
1 - filter(SYSDATE@!>=SYSDATE@!-.01)
3 - access("T"."DT">=SYSDATE@!-.01 AND "T"."DT"<=SYSDATE@!)
filter("T"."DT">=SYSDATE@!-.01 AND "T"."DT"<=SYSDATE@!)
-----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 164 | 4100 | 22 (0)| 00:00:01 |
|* 1 | FILTER | | | | | |
| 2 | NESTED LOOPS | | | | | |
| 3 | NESTED LOOPS | | 164 | 4100 | 22 (0)| 00:00:01 |
| 4 | INDEX FULL SCAN | SYS_C00459678 | 10 | 30 | 1 (0)| 00:00:01 |
|* 5 | INDEX RANGE SCAN | IX_BIG_TAB | 16 | | 2 (0)| 00:00:01 |
| 6 | TABLE ACCESS BY INDEX ROWID| XT_BIG_TAB | 16 | 352 | 3 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------
1 - filter(SYSDATE@!>=SYSDATE@!-.01)
5 - access("T"."NUMS_ID"="N"."ID" AND "T"."DT">=SYSDATE@!-.01 AND
"T"."DT"<=SYSDATE@!)
-------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 164 | 3608 | 13 (0)| 00:00:01 |
|* 1 | FILTER | | | | | |
| 2 | TABLE ACCESS BY INDEX ROWID| XT_BIG_TAB | 164 | 3608 | 13 (0)| 00:00:01 |
|* 3 | INDEX SKIP SCAN | IX_BIG_TAB | 164 | | 12 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------
1 - filter(SYSDATE@!>=SYSDATE@!-.01)
3 - access("T"."DT">=SYSDATE@!-.01 AND "T"."DT"<=SYSDATE@!)
filter("T"."DT">=SYSDATE@!-.01 AND "T"."DT"<=SYSDATE@!)
-------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 164 | 4100 | 13 (0)| 00:00:01 |
|* 1 | FILTER | | | | | |
| 2 | NESTED LOOPS | | 164 | 4100 | 13 (0)| 00:00:01 |
| 3 | TABLE ACCESS BY INDEX ROWID| XT_BIG_TAB | 164 | 3608 | 13 (0)| 00:00:01 |
|* 4 | INDEX SKIP SCAN | IX_BIG_TAB | 164 | | 12 (0)| 00:00:01 |
|* 5 | INDEX UNIQUE SCAN | SYS_C0013510913 | 1 | 3 | 0 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------
1 - filter(SYSDATE@!>=SYSDATE@!-.01)
4 - access("T"."DT">=SYSDATE@!-.01 AND "T"."DT"<=SYSDATE@!)
filter("T"."DT">=SYSDATE@!-.01 AND "T"."DT"<=SYSDATE@!)
5 - access("T"."NUMS_ID"="N"."ID")
Кстати, в Oracle 11.2 запросы получаются немного другие и их cost одинаков:Вообще-то этот пример я как раз делал на 11.2. У вас, наверное какая-нибудь низкая версия типа 11.2.0.1 — 11.2.0.2 — это заметно по 0 в стоимости INDEX UNIQUE SCAN, насколько помню это в каком-то промежуточном патче было исправлено.
/*+ leading(n t) use_nl(t) index(t (NUMS_ID, DT)) NO_ELIMINATE_JOIN(n) */Соответственно при IRS мы будем заходить сразу с верхними и нижними границами по каждому из 10 значений полученных из xt_nums. А Index skip scan, при сработавшем Join elimination, вычитывает весь индекс IX_BIG_TAB, а не спускается по дереву как IRS.
Вообще-то этот пример я как раз делал на 11.2. У вас, наверное какая-нибудь низкая версия типа 11.2.0.1 — 11.2.0.2 — это заметно по 0 в стоимости INDEX UNIQUE SCAN, насколько помню это в каком-то промежуточном патче было исправлено.
-------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 164 | 4100 | 22 (0)| 00:00:01 |
|* 1 | FILTER | | | | | |
| 2 | NESTED LOOPS | | 164 | 4100 | 22 (0)| 00:00:01 |
| 3 | NESTED LOOPS | | 164 | 4100 | 22 (0)| 00:00:01 |
| 4 | INDEX FULL SCAN | SYS_C0013510913 | 10 | 30 | 1 (0)| 00:00:01 |
|* 5 | INDEX RANGE SCAN | IX_BIG_TAB | 16 | | 2 (0)| 00:00:01 |
| 6 | TABLE ACCESS BY INDEX ROWID| XT_BIG_TAB | 16 | 352 | 3 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------
1 - filter(SYSDATE@!>=SYSDATE@!-.01)
5 - access("T"."NUMS_ID"="N"."ID" AND "T"."DT">=SYSDATE@!-.01 AND "T"."DT"<=SYSDATE@!)
Я предполагал, что INDEX SKIP SCAN (ISS) логически разобьет индекс IX_BIG_TAB на 10 небольших индексов и уже последовательно пройдется по ним, используя access(«T».«DT»>=SYSDATE@!-.01 AND «T».«DT»<=SYSDATE@!). Т.е. сделает подобие IRS 10 раз, и, логически получится тоже самое что и в шагах 3-5 из запроса без join elimination.
Видимо различия в каких-то еще параметрах.проще трассировку 10053 сделать
Основное отличие в том, что Oracle заранее не знает, какие значения у лидирующего столбца в индексе, чтобы спускаться по дереву к каждому левому значению, и поэтому не знает где очередное начинается и где заканчивается, поэтому ему нужно отлавливать, когда значение лидирующего в branch-блоках изменяется и брать его уже для прохода по leaf-блокам, а остальные leaf-блоки skip'ает.
В принципе можете прочитать тут и статью и комментарии: richardfoote.wordpress.com/2008/03/10/index-skip-scan-does-index-column-order-matter-any-more-warning-sign/

Oracle join elimination