2016-01-25 2 views
0

У меня есть запрос, который очень плохо работает из-за полного сканирования таблицы. Я проверил статистику, перестроил индексы, но не работал.Индекс не используется оптимизатором

SQL заявление:

select distinct NA_DIR_EMAIL d, NA_DIR_EMAIL r 
from gcr_items , gcr_deals 
where gcr_deals.GCR_DEALS_ID=gcr_items.GCR_DEALS_ID 
and 
gcr_deals.bu_id=:P0_BU_ID 
and 
decode(:P55_DIRECT,'ALL','Y',trim(upper(NA_ORG_OWNER_EMAIL)))= 
decode(:P55_DIRECT,'ALL','Y',trim(upper(:P55_DIRECT))) 
order by 1 

Execution Plan : 
Plan hash value: 3180018891 

------------------------------------------------------------------------- 
| Id | Operation     | Name    | Rows | Time  | 
------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT   |     |  8 | 00:11:42 | 
| 1 | SORT ORDER BY   |     |  8 | 00:11:42 | 
| 2 | HASH UNIQUE    |     |  8 | 00:11:42 | 
|* 3 | HASH JOIN    |     | 7385 | 00:11:42 | 
|* 4 |  VIEW     | index$_join$_002 | 10462 | 00:00:05 | 
|* 5 |  HASH JOIN   |     |  |   | 
|* 6 |  INDEX RANGE SCAN | GCR_DEALS_IDX12 | 10462 | 00:00:01 | 
| 7 |  INDEX FAST FULL SCAN| GCR_DEALS_IDX1 | 10462 | 00:00:06 | 
|* 8 |  TABLE ACCESS FULL  | GCR_ITEMS  | 7386 | 00:11:37 | 
------------------------------------------------------------------------- 

Predicate Information (identified by operation id): 
--------------------------------------------------- 

    3 - access("GCR_DEALS"."GCR_DEALS_ID"="GCR_ITEMS"."GCR_DEALS_ID") 
    4 - filter("GCR_DEALS"."BU_ID"=TO_NUMBER(:P0_BU_ID)) 
    5 - access(ROWID=ROWID) 
    6 - access("GCR_DEALS"."BU_ID"=TO_NUMBER(:P0_BU_ID)) 
    8 - filter(DECODE(:P55_DIRECT,'ALL','Y',TRIM(UPPER("NA_ORG_OWNER_EMAI 
       L")))=DECODE(:P55_DIRECT,'ALL','Y',TRIM(UPPER(:P55_DIRECT)))) 
+1

Можете ли вы опубликовать план выполнения и индексы на ваших таблицах? – Aleksej

+2

Не зная, какие индексы присутствуют на ваших столах и не видя плана выполнения, невозможно угадать, что происходит. Измените свой вопрос и укажите: A) DDL для обеих таблиц, B) все индексы на обеих таблицах, C) план выполнения и D) количество строк в каждой таблице. Благодарю. –

+0

'gcr_items' содержит только 7386 строк - я сомневаюсь, что доступ к индексу действительно улучшит производительность, поскольку, по-видимому, вы выбираете все строки из этой таблицы –

ответ

0

В начале часть условия в WHERE должен быть разложенным пункт (или «декомпилируемый» - или «reengeenered») в более простую форму без использования decode функции, которые форма может быть понятно оптимизатором запросов:

AND 
decode(:P55_DIRECT,'ALL','Y',trim(upper(NA_ORG_OWNER_EMAIL)))= 
decode(:P55_DIRECT,'ALL','Y',trim(upper(:P55_DIRECT))) 

в:

AND (
    :P55_DIRECT = 'ALL' 
    OR 
    trim(upper(:P55_DIRECT)) = trimm(upper(NA_ORG_OWNER_EMAIL)) 
) 

Для поиска строки в таблице на основе значений, хранящихся в индексе, Oracle использует метод доступа с именем Индекс сканирования, увидеть эту ссылку для подробностей:
https://docs.oracle.com/cd/B19306_01/server.102/b14211/optimops.htm#i52300

Одним из наиболее распространенных доступа метод Индекс Диапазон сканирования смотрите здесь:
https://docs.oracle.com/cd/B19306_01/server.102/b14211/optimops.htm#i45075

в документации сказано (в последнем ссылке), что:

оптимизатор использует сканирование диапазона, когда он находит один или более ведущих столбцов индекса, указанного в условиях, таких как следующее:

col1 =: b1

col1 <: б1

col1>: б1

И сочетание предыдущих условий для ведущих столбцов в индексе

col1, как «поиск ASD%», не должен находиться в ведущей позиции , иначе условие col1, подобное «% ASD», не приведет к сканированию диапазона .

Вышесказанным означает, что оптимизатор может использовать индекс для поиска строк только для условий запроса, которые содержат основные операторы Comparision: = < > <= >= LIKE, которые используются для сравнения простых значений с простыми именами столбцов.

В документации, в которой четко не сказано - и вы должны сделать вывод, что она читает между линиями, - это факт, что когда какая-либо функция используется в состоянии, в форме function(column_name) или function(expression_involving_column_names), тогда сканирование диапазона индекса не может быть использовано.
В этом случае оптимизатор запросов должен оценивать это выражение отдельно для каждой строки таблицы, поэтому должен читать все строки (выполнять полное сканирование таблицы).


Краткий вывод и эмпирическое правило:

функции в ИНЕКЕ может помешать оптимизатору использовать индексирует

Если вы видите какую-то функцию где-нибудь в WHERE , то это признак того, что вы являетесь , работающим красным светом
STOP немедленно и три раза подумайте, как эта функция влияет на оптимизатор запросов и производительность вашего запроса , и попробуйте переписать условие в форму, которую оптимизатор способен понять.


Теперь посмотрят на нашем переписано условии:

AND (
    :P55_DIRECT = 'ALL' 
    OR 
    trim(upper(:P55_DIRECT)) = trimm(upper(NA_ORG_OWNER_EMAIL)) 
) 

и СТОП - Есть еще две функций trim и upper примененных к колонку под названием NA_ORG_OWNER_EMAIL. Нам нужно подумать, как они могут повлиять на оптимизатор запросов.

Я предполагаю, что вы создали простой индекс для одного столбца:
CREATE INDEX somename ON GCR_ITEMS(NA_ORG_OWNER_EMAIL).
Если да, то индекс содержит только простые значения NA_ORG_OWNER_EMAIL.
Но запрос пытается найти значения trimm(upper(NA_ORG_OWNER_EMAIL)), которые не хранятся в индексе, поэтому этот индекс не может быть использован в этом случае.

Это условие требует, основанный индекса функции:
https://docs.oracle.com/cd/E11882_01/appdev.112/e41502/adfns_indexes.htm#ADFNS00505

CREATE INDEX somename ON GCR_ITEMS(trim(upper(NA_ORG_OWNER_EMAIL)))


К сожалению, даже индекс функции на основе все равно не поможет, потому что условие в запросе слишком общее - если значение: P55_DIRECT = ВСЕ запрос должен извлекать все строки из таблицы (выполнить полное сканирование таблицы), в противном случае должен использовать индекс для поиска значения внутри него.

Это потому, что запрос запланирован (подумайте об этом как «скомпилированный») оптимизатором запросов только один раз, во время его первого выполнения. Затем план хранится в кеше и используется для выполнения запроса для всех последующих исполнений. Значение параметра не известно заранее, поэтому в плане должны учитываться все возможные случаи, поэтому всегда будет выполняться полное сканирование таблицы.
В 1 есть новая функция «Адаптивная оптимализация запроса»:
https://docs.oracle.com/database/121/TGSQL/tgsql_optcncpt.htm#TGSQL94982
где оптимизатор запросов анализирует каждый параметры запроса на каждый работает, и в состоянии обнаружить, что план не является оптимальным для некоторых параметров среды выполнения, и выберите лучшие «подпланы» в зависимости от значения фактического параметра ... но вы должны использовать 12c и дополнительно платить за Enterprise Edition, потому что только это издание включает эту функцию. И все же неясно, будет ли адаптивный план работать в этом случае или нет.

Что вы можете сделать, не оплачивая 12c EE, - это ОТДЕЛАТЬ этот общий запрос в два отдельных варианта: один для случая, когда: P55_DIRECT = ALL, а другой для остальных случаев и запускает соответствующий вариант на клиенте (ваш приложение) в зависимости от значения этого параметра.

Версия для: P55_DIRECT = ALL, который будет выполнять полное сканирование таблицы

where gcr_deals.GCR_DEALS_ID=gcr_items.GCR_DEALS_ID 
and 
gcr_deals.bu_id=:P0_BU_ID 
order by 1 

и версии для других случаев, которые будет использовать индекс на основе функции:

where gcr_deals.GCR_DEALS_ID=gcr_items.GCR_DEALS_ID 
and 
gcr_deals.bu_id=:P0_BU_ID 
and 
trim(upper(:P55_DIRECT)) = trimm(upper(NA_ORG_OWNER_EMAIL)) 
order by 1 
Смежные вопросы