Производительность Oracle ROWNUM - PullRequest
8 голосов
/ 03 февраля 2012

Для запроса top-n строк в Oracle обычно используется ROWNUM.Поэтому следующий запрос выглядит нормально (получает последние 5 платежей):

select a.paydate, a.amount
from (
  select t.paydate, t.amount
  from payments t
  where t.some_id = id
  order by t.paydate desc
) a
where rownum <= 5;

Но для очень больших таблиц он неэффективен - для меня он выполняется в течение ~ 10 минут.Поэтому я попробовал другие запросы, и в итоге я получил этот, который выполняется менее секунды:

select *
from (
  select  a.*, rownum
  from (select t.paydate, t.amount
        from payments t
        where t.some_id = id
        order by t.paydate desc) a
)
where rownum <= 5;

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

SELECT STATEMENT, GOAL = ALL_ROWS   7   5   175
COUNT STOPKEY           
VIEW    7   5   175
TABLE ACCESS BY INDEX ROWID 7   316576866   6331537320
INDEX FULL SCAN DESCENDING  4   6   

А для второго:

SELECT STATEMENT, GOAL = ALL_ROWS   86  5   175
COUNT STOPKEY           
VIEW    86  81  2835
COUNT           
VIEW    86  81  1782
SORT ORDER BY   86  81  1620
TABLE ACCESS BY INDEX ROWID 85  81  1620
INDEX RANGE SCAN    4   81  

Очевидно, что INDEX FULL SCAN DESCENDING делает первый запрос неэффективным для больших таблиц.Но я не могу реально дифференцировать логику двух запросов, глядя на них.Может ли кто-нибудь объяснить мне логические различия между двумя запросами на человеческом языке?

Заранее спасибо!

1 Ответ

3 голосов
/ 03 февраля 2012

Прежде всего, как упомянуто в комментарии Алекса, я не уверен, что ваша вторая версия на 100% гарантированно даст вам правильные строки - поскольку «средний» блок запроса не имеет явного order by Oracle не обязана передавать строки во внешний блок запроса в каком-либо определенном порядке. Однако, похоже, нет какой-либо конкретной причины, по которой это могло бы изменить порядок передачи строк из самого внутреннего блока, поэтому на практике это, вероятно, сработает.

И именно поэтому Oracle выбирает другой план для второго запроса - логически не может применить операцию STOPKEY к самому внутреннему блоку запроса.

Я думаю, что в первом случае оптимизатор предполагает, что значения id хорошо распределены, и для любого заданного значения, вероятно, будут некоторые очень недавние транзакции. Поскольку он может видеть, что ему нужно только найти 5 самых последних совпадений, он рассчитывает, что, по-видимому, более эффективно сканировать строки в порядке убывания paydate с использованием индекса, искать соответствующий идентификатор и другие данные из таблицы. и остановитесь, когда найдете первые 5 совпадений. Я подозреваю, что вы увидите очень разную производительность для этого запроса в зависимости от конкретного значения идентификатора, который вы используете - если идентификатор имеет много недавних действий, строки должны быть найдены очень быстро, но если это не так, сканирование индекса возможно, придется сделать гораздо больше работы.

Во втором случае я считаю, что он не может применить оптимизацию STOPKEY к самому внутреннему блоку из-за дополнительного уровня вложенности. В этом случае полное сканирование индекса станет гораздо менее привлекательным, поскольку всегда нужно будет сканировать весь индекс. Поэтому он выбирает поиск индекса по id (я предполагаю) с последующей фактической сортировкой по дате. Если данное значение id соответствует небольшому подмножеству строк, это, вероятно, будет более эффективным, но если вы дадите id, у которого множество строк разбросано по всей таблице, я ожидаю, что оно станет медленнее, так как ему придется обращаться и сортировать много строк.

Итак, я бы предположил, что в ваших тестах использовались значения id, в которых относительно мало строк, которые не являются слишком свежими. Если это будет типичный вариант использования, то второй запрос, вероятно, лучше для вас (опять же, с учетом предостережения, что я не уверен, что технически гарантированно будет получен правильный набор результатов). Но если типичные значения с большей вероятностью будут иметь много совпадающих строк и / или с большей вероятностью будут иметь 5 самых последних строк, тогда первый запрос и план могут быть лучше.

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