Как я могу ускорить row_number в Oracle?
-
05-07-2019 - |
Вопрос
У меня есть 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
Но это не дает правильных результатов.Есть ли какой-нибудь простой способ ускорить это?Или мне придется потратить еще немного времени на инструмент ОБЪЯСНЕНИЯ?
Решение
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
не обнуляется и не добавляет 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
это входит в подзапрос.
Другие советы
Похоже, запрос на нумерацию страниц.
Из этой статьи ASKTOM (около 90% вниз по странице):
Кроме того, ваши запросы не совпадают, поэтому я не уверен, в чем заключается преимущество сравнения затрат одного на другое.
Ваш столбец ORDER BY проиндексирован? Если нет, то это хорошее место для начала.
Отчасти проблема в том, насколько велики пролеты от начала до конца и где они живут. Скажем, у вас есть миллион строк в таблице, и вы хотите строки с 567 890 по 567 900, тогда вам придется смириться с тем фактом, что потребуется пройти по всей таблице, отсортировав почти все по идентификатору, и выяснить, какие строки попадают в этот диапазон.
Короче говоря, это большая работа, поэтому оптимизатор стоит дорого.
Индекс также не очень помогает. Индекс дает порядок, но в лучшем случае дает вам место для начала, а затем вы продолжаете читать, пока не дойдете до 567 900-й записи.
Если вы показываете своему конечному пользователю по 10 элементов за раз, возможно, стоит извлечь 100 лучших из БД, а затем разбить эти 100 на десять частей приложения.
Проводите больше времени с инструментом EXPLAIN PLAN. Если вы видите TABLE SCAN, вам нужно изменить свой запрос.
Ваш запрос мало что значит для меня. Запросы по ROWID, похоже, напрашиваются на неприятности. В этом запросе нет реляционной информации. Это реальный запрос, с которым у вас возникли проблемы, или пример, который вы создали для иллюстрации своей проблемы?