Навигация по первому числу символов в Oracle: запросы на подсчет не используют функциональные индексы - PullRequest
2 голосов
/ 09 февраля 2011

Oracle 9i. У нас есть большая таблица (~ 1 млн строк), содержащая наш контент, который содержит столбцы заголовка и автора. Мы хотели бы написать представления, которые предлагают навигацию A-Z для заголовков и авторов к этому контенту (A: 1300, B: 45000, ...)

Сначала немного подготовки без индексации:

select * from content where substr(upper(title),0,1) = 'M'

работает немного лучше, чем

select * from content where upper(title) LIKE 'M%'

ExPlan для тех:

TABLE ACCESS content FULL Cost=1624

оба довольно быстрые, даже без индексов. Теперь медленная часть:

select count(*) from CONTENT WHERE substr(upper(TITLE),0,1) = 'A';

ExPlan:

SORT AGGREGATE else like above.

Теперь кумуляция (это то, что мы хотим, это очень медленно):

select substr(upper(title),0,1) , count(*) from content group by substr(upper(title),0,1);

ExPlan:

SORT: GROUP BY COST=8069 / TABLE ACCESS on CONTENT FULL COST=1624

Итак, я начал создавать функциональный индекс:

create index CONTENT_TITLE_LETTER_IDX on CONTENT(substr(upper(TITLE),0,1));

Это значительно ускоряет запрос количества букв:

select count(*) from CONTENT WHERE substr(upper(TITLE),0,1) = 'A';

ExPlan (он отвечает почти в реальном времени):

SORT AGGREGATE COST=1 / INDEX CONTENT_TITLE_LETTER_IDX RANGE SCAN COST=1

Но запрос кумуляции, который в основном запрашивает одно и то же, не использует индекс (он показывает тот же План объяснения, что и выше). Я попробовал подсказку:

select /*+ index(CONTENT CONTENT_TITLE_LETTER_IDX) */ substr(upper(title),0,1) , count(*) from content group by substr(upper(title),0,1);

но это все еще очень медленно. Я предполагаю, что это может быть связано с неупорядоченным индексом, но я предполагаю, что даже если бы я запустил цикл вокруг всех 26 возможных букв, один запрос (= 'буква') был бы быстрее!

Кто знает, как заставить Oracle использовать этот индекс (или альтернативный способ, кроме создания столбцов или таблиц с одним символом)?

Ответы [ 3 ]

3 голосов
/ 09 февраля 2011

Еще раз взглянем на ваш запрос:

select substr(upper(title),0,1) , count(*) 
from content 
group by substr(upper(title),0,1)

Обратите внимание на отсутствие какого-либо предложения where.Фактически, вы указываете ядру базы данных взять всех строк и посчитать, сколько строк имеется для каждой начальной буквы.Вы не можете пропустить ни одну строку, потому что иначе вы не можете сосчитать ее.Я не думаю, что индекс хранит такую ​​информацию с готовностью, поэтому полное сканирование - самое быстрое, что вы можете иметь.Если вы просили указать конкретную букву, использование сканирования диапазона в индексе может иметь смысл.

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

3 голосов
/ 09 февраля 2011

Хорошо, это отвечает на вопрос, но на самом деле не решает проблему (спасибо Геррат, мы почти в одно и то же время):

Я посмотрел на этот интересный вопрос , который сказал мне, что CBO может отклонить индексы, содержащие значения NULL. Принимая во внимание, что наша таблица содержит заголовок, который начинается с не-буквенных символов и пробелов (но никогда не бывают нулевыми ...), я попробовал следующее:

select   substr(upper(title),0,1), count(*) 
from content
 where substr(upper(title),0,1) >= 'A' AND substr(upper(title),0,1) <= 'Z'
group by substr(upper(title),0,1);

, что резко снижает затраты:

SORT GROUP BY NOSORT COST=16 / CONTENT ACCESS BY INDEX ROWID COST=6 / CONTENT_TITLE_LETTER_IDX RANGE SCAN COST=2

с 8069 до 16

Интересно, что условный запрос теперь даже на 1013 * медленнее (в среднем 2,7 с), чем необусловленный (в среднем 1,5 с). Добавление большего числа условий, как указано в 9000, значительно ускоряет процесс, даже если он вообще не использует буквенный индекс. Спасибо за это понимание!

0 голосов
/ 09 февраля 2011

Я не могу обещать вам, что это будет быстрее, но вы можете попробовать:

select /*+ index(CONTENT CONTENT_TITLE_LETTER_IDX) */ substr(upper(title),0,1), 
count(*) from content 
where substr(upper(title),0,1) in ('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 
'X', 'Y', 'Z')
group by substr(upper(title),0,1);

В аналогичном тесте, который я проводил, использовался индекс (больше ничего я не пытался, но)стоимость была на самом деле выше, чем полное сканирование таблицы).

Убедитесь, что ваша таблица и индекс также проанализированы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...