Неправильный индекс используется при выборе верхних строк - PullRequest
8 голосов
/ 02 августа 2011

У меня есть простой запрос, который выбирает 200 верхних строк, упорядоченных по одному из столбцов, отфильтрованных по другому проиндексированному столбцу.Путаница заключается в том, что план запроса в PL / SQL Developer показывает, что этот индекс используется только , когда я выбираю все строки, например:

SELECT * FROM
(
 SELECT *
 FROM cr_proposalsearch ps
 WHERE UPPER(ps.customerpostcode) like 'MK3%'
 ORDER BY ps.ProposalNumber DESC
)
WHERE ROWNUM <= 200

План показывает, что ониспользует индекс CR_PROPOSALSEARCH_I1, который является индексом для двух столбцов: PROPOSALNUMBER & UPPER (CUSTOMERNAME), для выполнения которого требуется 0,985s : query with ROWNUM

Если я избавлюсь от условия ROWNUM,План - это то, что я ожидаю, и он выполняется в 0,343 с : query without ROWNUM

Где index XIF25CR_PROPOSALSEARCH is on CR_PROPOSALSEARCH (UPPER(CUSTOMERPOSTCODE));

Каким образом?

РЕДАКТИРОВАТЬ: я собрал статистику по таблице cr_proposalsearch, и оба плана запросов теперь показывают, что они используют индекс XIF25CR_PROPOSALSEARCH.

Ответы [ 4 ]

8 голосов
/ 02 августа 2011

Включение ROWNUM изменяет расчеты оптимизатора, в отношении которых наиболее эффективен путь.

Когда вы выполняете запрос top-n, подобный этому, это не обязательно означает, что Oracle получит все строки полностьюотсортируйте их, а затем верните лучшие.Операция COUNT STOPKEY в плане выполнения указывает, что Oracle будет выполнять только базовые операции, пока не найдет количество запрошенных вами строк.

Оптимизатор рассчитал, что полный запрос получит и отсортирует 77 тыс. Строк,Если бы он использовал этот план для запроса top-n, ему пришлось бы выполнить большую сортировку этих строк, чтобы найти топ-200 (необязательно полностью сортировать их, так как не заботился бы о точном порядкестрок за вершиной, но он должен просмотреть все эти строки).

План запроса top-n использует другой индекс, чтобы вообще не выполнять сортировку.Он рассматривает каждую строку по порядку, проверяет, соответствует ли она предикату, и если да, возвращает его.Когда возвращается 200 строк, все готово.Его расчеты показали, что это будет более эффективно для получения небольшого количества строк.(Конечно, это может быть неверно; вы еще не сказали, какова относительная производительность этих запросов.)

Если бы оптимизатор выбрал этот план, когда вы запрашиваете все строки, он должен был быПрочитать весь индекс в порядке убывания, получая каждую строку из таблицы по ROWID, чтобы проверить соответствие предикату.Это приведет к большому количеству дополнительных операций ввода-вывода и проверке множества строк, которые не будут возвращены.Так что в этом случае он решает, что использование индекса на customerpostcode более эффективно.

Если вы постепенно увеличиваете количество строк, возвращаемых из запроса top-n, вы, вероятно, найдете переломный моментгде план переключается с первого на второй.Только из стоимости двух планов, я думаю, это может быть около 1200 строк.

4 голосов
/ 02 августа 2011

Если вы уверены, что ваша статистика актуальна и индекс достаточно избирателен, вы можете указать оракулу использовать индекс

SELECT  *
FROM   (SELECT /*+ index(ps XIF25CR_PROPOSALSEARCH) */  *
        FROM     cr_proposalsearch ps
        WHERE    UPPER (ps.customerpostcode) LIKE 'MK3%'
        ORDER BY ps.proposalnumber DESC)
WHERE  ROWNUM <= 200

(я бы рекомендовал этот подход только в крайнем случае)

Если бы я делал это, я бы сначала запросил tkprof запроса, чтобы увидеть, сколько фактически он выполняет,

например: стоимость сканирования диапазона индекса может быть слишком высокой

забыл упомянуть .... Вы должны проверить фактическое количество элементов:

SELECT count(*)  FROM cr_proposalsearch ps  WHERE UPPER(ps.customerpostcode) like 'MK3%' 

, а затем сравните его с количеством элементов в плане запроса.

1 голос
/ 02 августа 2011

Это условие:

WHERE UPPER(ps.customerpostcode) like 'MK3%'

не является непрерывным, то есть вы не можете сохранить для него один заказанный диапазон.

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

  1. Порядок по номеру, затем фильтр по коду.
  2. Фильтрация по коду, затем упорядочение по номеру.

Метод 1 может использовать индекс для числа, который дает вам линейное время выполнения (верхние 100 строк будут выбраны * в 1016 * раз быстрее, чем верхние 200, при условии, что число и код делаютне коррелирует).

Метод 2 может использовать сканирование диапазона для грубой фильтрации по коду (условие диапазона будет что-то вроде code >= 'MK3' AND code < 'MK4'), однако, требуется сортировка, так как порядок числане может быть сохранен в составном индексе.

Время сортировки зависит также от количества выбранных вами верхних строк, но эта зависимость, в отличие от метода для 1, не является линейной (вам всегда нужно по крайней мереодно сканирование диапазона).

Однако условие фильтрации в методе 2 достаточно избирательно для RANGE SCAN с последующей сортировкой, чтобы быть более эффективным, чем FULL SCAN для всей таблицы.

Это означает, что существует переломный момент: для этого условия: ROWNUM <= X существует значение X, поэтому метод 2 становится более эффективным при превышении этого значения.

Обновление:

Если вы всегда ищете хотя бы по 3 первым символам, вы можете создать такой индекс:

SUBSTRING(UPPER(customerpostcode), 1, 3), proposalnumber

и использовать его в этом запросе:

SELECT  *
FROM    (
        SELECT  *
        FROM    cr_proposalsearch ps
        WHERE   SUBSTRING(UPPER(customerpostcode, 1, 3)) = SUBSTRING(UPPER(:searchquery), 1, 3)
                AND UPPER(ps.customerpostcode) LIKE UPPER(:searchquery) || '%'
        ORDER BY
                proposalNumber DESC
        )
WHERE   rownum <= 200

Таким образом, порядок номеров будет сохраняться отдельно для каждого набора кодов, разделяющих первые 3 букв, что даст вам более плотное сканирование индекса.

1 голос
/ 02 августа 2011

У вас нет подходящего индекса. Индекс CR_PROPOSALSEARCH_I1 можно использовать для извлечения строк в порядке убывания атрибута PROPOSALNUMBER. Вероятно, он выбран потому, что Oracle может избежать извлечения всех совпадающих строк, отсортировать их в соответствии с предложением ORDER BY и затем отбросить все строки, кроме первых.

Без условия ROWNUM Oracle использует индекс XIF25CR_PROPOSALSEARCH (вы не сообщили о нем никаких подробностей), поскольку он, вероятно, довольно избирателен в отношении предложения WHERE. Но это потребует, чтобы отсортировать результат впоследствии. Вероятно, это более эффективный план, основанный на предположении, что вы получите все строки.

Поскольку любой индекс является компромиссом (один лучше для сортировки, другой лучше для применения предложения WHERE), такие детали, как ROWNUM, определяют, какой план выполнения выбирает Oracle.

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