Как я могу ускорить row_number в Oracle? - PullRequest
11 голосов
/ 06 мая 2009

У меня есть SQL-запрос, который выглядит примерно так:

SELECT * FROM(
    SELECT
        ...,
        row_number() OVER(ORDER BY ID) rn
    FROM
        ...
) WHERE rn between :start and :end

По сути, это часть ORDER BY, которая замедляет процесс. Если бы я убрал его, стоимость EXPLAIN снизилась бы на порядок (более 1000x). Я пробовал это:

SELECT 
    ...
FROM
    ...
WHERE
    rownum between :start and :end

Но это не дает правильных результатов. Есть ли простой способ ускорить это? Или мне придется потратить еще немного времени с инструментом EXPLAIN?

Ответы [ 5 ]

13 голосов
/ 06 мая 2009

ROW_NUMBER довольно неэффективно в Oracle.

Подробнее о производительности смотрите в статье в моем блоге:

Для вашего конкретного запроса я бы порекомендовал заменить его на ROWNUM и убедиться, что используется индекс:

SELECT  *
FROM    (
        SELECT  /*+ INDEX_ASC(t index_on_column) NOPARALLEL_INDEX(t index_on_column) */
                t.*, ROWNUM AS rn
        FROM    table t
        ORDER BY
                column
        )
WHERE rn >= :start
      AND rownum <= :end - :start + 1

В этом запросе будет использоваться COUNT STOPKEY

Также убедитесь, что column не имеет значения nullable, или добавьте условие WHERE column IS NOT NULL.

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

Обратите внимание, что вы не можете использовать ROWNUM BETWEEN :start and :end без подзапроса.

ROWNUM всегда назначается последним и проверяется последним, поэтому ROWNUM всегда приходят в порядок без пробелов.

Если вы используете ROWNUM BETWEEN 10 and 20, первая строка, удовлетворяющая всем остальным условиям, станет кандидатом на возвращение, временно назначена с ROWNUM = 1 и провалит тест ROWNUM BETWEEN 10 AND 20.

Тогда следующей строкой будет кандидат, назначенный с ROWNUM = 1 и с ошибкой и т. Д., Поэтому, наконец, строки вообще не будут возвращаться.

Это можно обойти, поместив ROWNUM в подзапрос.

5 голосов
/ 06 мая 2009

Похоже, запрос на нумерацию страниц.

Из этой статьи ASKTOM (около 90% вниз по странице):

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

Кроме того, ваши запросы не совпадают, поэтому я не уверен, в чем преимущество сравнения затрат одного на другое.

1 голос
/ 06 мая 2009

Отчасти проблема в том, насколько велики пролеты от «начала» до «конца» и где они «живут». Скажем, у вас есть миллион строк в таблице, и вы хотите, чтобы строки с 567 890 по 567 900 были вынуждены согласиться с тем фактом, что потребуется пройти по всей таблице, отсортировав почти все по идентификатору, и определите, какие строки попадают в этот диапазон.

Короче говоря, это много работы, поэтому оптимизатор стоит дорого.

Это также не то, что индекс может сильно помочь. Индекс дает порядок, но в лучшем случае дает вам место для начала, а затем вы продолжаете читать, пока не дойдете до 567 900-й записи.

Если вы показываете своему конечному пользователю по 10 элементов за раз, возможно, стоит взять 100 лучших из БД, а затем приложение разбить эти 100 на десять частей.

1 голос
/ 06 мая 2009

Ваш столбец ORDER BY проиндексирован? Если нет, то это хорошее место для начала.

0 голосов
/ 06 мая 2009

Проведите больше времени с инструментом EXPLAIN PLAN. Если вы видите TANLE SCAN, вам нужно изменить свой запрос.

Ваш запрос мало что значит для меня. Запросы по ROWID, похоже, напрашиваются на неприятности. В этом запросе нет реляционной информации. Это реальный запрос, с которым у вас возникли проблемы, или пример, который вы составили для иллюстрации своей проблемы?

...