Как я могу ускорить номер строки в Oracle?

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

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

по сути, это порядок по частям, который замедляет работу. Если я должен был удалить его, стоимость объяснения снижается на порядок (более 1000 раз). Я пробовал это:

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

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

5 ответов


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 и терпеть неудачу, etc., таким образом, наконец, строки не будут возвращены вообще.

это должно быть обработано, поставив ROWNUM ' s в подзапросе.


похоже на запрос разбиения на страницы.

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

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

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


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


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

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

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

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


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

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