2014-10-12 2 views
0

Я создал запрос, делая это в ORACLE:функции на основе индекса с использованием SubStr и Instr

SELECT SUBSTR(title,1,INSTR(title,' ',1,1)) AS first_word, COUNT(*) AS word_count 
FROM FILM 
GROUP BY SUBSTR(title,1,INSTR(title,' ',1,1)) 
HAVING COUNT(*) >= 20;  

Результаты после запуска: 539 rows selected. Elapsed: 00:00:00.22

мне нужно улучшить производительность этого и создан function-based index так, как:

CREATE INDEX INDX_FIRSTWRD ON FILM(SUBSTR(title,1,INSTR(title,' ',1,1))); 

После выполнения того же запроса в верхней части этот пост, я все равно получаю такую ​​же производительность: 539 rows selected. Elapsed: 00:00:00.22

Не применяется ли индекс или перезаписывается, или я делаю что-то неправильно?

Спасибо за любую помощь, которую вы могли бы предоставить. :)

РЕДАКТИРОВАТЬ:

Execution Plan: 
---------------------------------------------------------- 
Plan hash value: 2033354507 

---------------------------------------------------------------------------- 
| Id | Operation   | Name | Rows | Bytes | Cost (%CPU)| Time  | 
---------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT |  | 20000 | 2968K| 138 (2)| 00:00:02 | 
|* 1 | FILTER    |  |  |  |   |   | 
| 2 | HASH GROUP BY  |  | 20000 | 2968K| 138 (2)| 00:00:02 | 
| 3 | TABLE ACCESS FULL| FILM | 20000 | 2968K| 136 (0)| 00:00:02 | 
---------------------------------------------------------------------------- 

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

1 - filter(COUNT(*)>=20) 


Statistics 
---------------------------------------------------------- 
     0 recursive calls 
     0 db block gets 
    471 consistent gets 
     0 physical reads 
     0 redo size 
    14030 bytes sent via SQL*Net to client 
    908 bytes received via SQL*Net from client 
    37 SQL*Net roundtrips to/from client 
     0 sorts (memory) 
     0 sorts (disk) 
    539 rows processed 
+1

Означает ли в плане запроса, что индекс используется? Поскольку вам нужно читать каждую строку из таблицы (или индекса), я бы не ожидал, что индекс будет очень полезен, если таблица 'film' существенно больше индекса. –

+0

@JustinCave Как показать план запроса для этого запроса, потому что, когда я ввожу EXPLAIN PLAN FOR перед запросом, я просто получаю результат Explained. – FirstLegion

+0

Если вы используете SQL * Plus, запустите 'set autotrace on' перед запуском вашего оператора. Какую версию Oracle вы используете? –

ответ

2

Проблема в том, что значение, которое вы используете для индекса, может быть нулевым - если в заголовке нет места (т. Е. Это однословное название типа «Челюсти»), то ваш substr оценивает значение null. Это, вероятно, не то, что вы хотите, кстати - вы, вероятно, хотите, чтобы конечная позиция была обусловлена ​​наличием пространства вообще, но это выходит за рамки вопроса. (И даже если вы исправите эту логику, Oracle все равно не сможет доверять тому, что результат не может быть нулевым, даже если базовый столбец не может быть недействителен). Редактировать: см. Ниже для получения дополнительной информации об использовании nvl для обработки названий одного слова.

Поскольку нули не включены в индексы, строки с одним заголовком не будут индексироваться. Но вы запрашиваете все строки, и Oracle знает, что индекс не содержит все строки, поэтому он не может использовать индекс для выполнения запроса - даже если вы добавляете подсказку, указывающую на него, он должен игнорировать этот намек ,

Единственный раз, когда будет использоваться индекс, если вы включите фильтр, который ссылается на индексированное значение также и явно или неявно исключить аннулирует, например:

SELECT SUBSTR(title,1,INSTR(title,' ',1,1)) AS first_word, COUNT(*) AS word_count 
FROM FILM 
WHERE SUBSTR(title,1,INSTR(title,' ',1,1)) IS NOT NULL 
GROUP BY SUBSTR(title,1,INSTR(title,' ',1,1)) 
HAVING COUNT(*) >= 20;  

(который также, вероятно, не то, что вы действительно хочет).

SQL Fiddle для запросов с фильтром и без него, а также с указателем индекса и без него. (Нажмите ссылку «План выполнения» для каждого раздела результатов, чтобы узнать, выполняет ли она полное сканирование таблицы или полное сканирование индекса).

И another Fiddle, показывающий, что индекс не может использоваться даже с фильтром, если фильтр по-прежнему разрешает нулевые значения, опять же, поскольку они не находятся в индексе.


С SylvainLeroux поднес, Oracle не достаточно умен, чтобы знать, вычисленное значение не может быть пустым, если вы coalesce это, даже если основной столбец не является нулевым (как function-based index или as a virtual column). Возможно, потому что для оценки может быть много филиалов. Но это достаточно умно, если использовать более простую и запатентованную nvl вместо:

CREATE INDEX INDX_FIRSTWRD 
ON FILM(NVL(SUBSTR(title,1,INSTR(title,' ',1,1)),title)); 

SELECT NVL(SUBSTR(title,1,INSTR(title,' ',1,1)),title) AS first_word, 
    COUNT(*) AS word_count 
FROM FILM 
GROUP BY NVL(SUBSTR(title,1,INSTR(title,' ',1,1)),title) 
HAVING COUNT(*) >= 20;  

Но только если title определяются как не-нуль. И coalesce делает работа если the virtual column is also declared not-null (спасибо Sylvain).

SQL Fiddle with a function-based index и another with a virtual column.

+0

Отличное объяснение! Один вопрос, хотя: «Даже если вы исправите эту логику, Oracle все равно не сможет доверять тому, что результат не может быть пустым». Будет ли Oracle достаточно умен, чтобы понять, что результат не может быть NULL при использовании 'COALESCE (SUBSTR (...), title) '(подразумевая название' NOT NULL')? –

+1

@SylvainLeroux - хороший вопрос, я не могу проверить это прямо сейчас, но я ожидал бы так - до тех пор, пока название само не является нулевым, как вы говорите. Я постараюсь не забыть проверять и обновлять завтра. –

+1

@SylvainLeroux - [явно не] (http://sqlfiddle.com/#!4/bb164/2). Также [с виртуальным столбцом] (http://sqlfiddle.com/#!4/feced/1). Ха, но он достаточно умен с 'nvl' вместо' coalesce', [как FBI] (http://sqlfiddle.com/#!4/ac653/1) и [как виртуальный столбец] (http://sqlfiddle.com/#!4/72a96f/1). –

0

539 строк выбрано. Прошедшее: 00: 00: 00,22

Вы действительно думаете, что вам нужно настроить запрос, который возвращает 539 строк в меньше, чем второй? 220 миллисекунд, точно! Подумайте об этом.

В вашем случае, я думаю CBO делает наилучшую возможную вещь. И именно по этой причине он не использует index. Потому что, чтобы читать every row из таблицы, использование индекса является накладными расходами. Он должен прочитать индекс, а затем сделать table access by rowid. Вероятно, в вашей маленькой таблице он мог бы прочитать всю таблицу с меньшим IO для получения данных.

Если таблица достаточно мала, чтобы быть в одном блоке, то для получения требуемых данных от single block требуется one IO с full table scan.

Вы можете попробовать проверить план объяснения, указав запрос на использование индекса и посмотреть, действительно ли что-то действительно улучшается. Помните, что вы излишне стараетесь улучшить производительность запроса, который выполняется менее чем за секунду!

+3

0,22 секунды равно 220 миллисекундам, а не 22. В этом случае, поскольку вся информация, необходимая запросу, будет указана в индексе, я бы ожидал, что оптимизатор выполнит полное сканирование индекса и проигнорирует таблицу (нет необходимости в 'доступ к таблице по rowid'). Это может быть не намного более значительным, чем выполнение полного сканирования на столе, однако, в зависимости от размера строк в таблице «фильм». –

+0

Да, это была опечатка. Исправлено сейчас. Я согласен, что доступ к таблице с помощью rowid был бы слишком накладным. OP не упомянул общее количество строк, которые имеет таблица. У вас есть хороший вопрос о размере строки, для которой потребуется задействовать несколько блоков.Я просто думаю, что в случае OP это не более чем один «IO» для извлечения требуемых данных из одного блока с «полным сканированием таблицы». –

Смежные вопросы