Это бесконечная тема для меня, и мне интересно, могу ли я что-то упустить из виду.По сути, я использую два типа операторов SQL в приложении:
- Регулярные запросы с пределом «отката»
- Сортированные и постраничные запросы
Сейчас,мы говорим о некоторых запросах к таблицам с несколькими миллионами записей, объединенных еще в 5 таблиц с несколькими миллионами записей.Ясно, что мы вряд ли хотим получить их все, поэтому у нас есть два вышеупомянутых метода для ограничения пользовательских запросов.
Случай 1 действительно прост.Мы просто добавляем дополнительный фильтр ROWNUM
:
WHERE ...
AND ROWNUM < ?
Это довольно быстро, поскольку CBO Oracle примет этот фильтр во внимание для своего плана выполнения и, вероятно, применяет операцию FIRST_ROWS
(аналогично принудительнойпо подсказке /*+FIRST_ROWS*/
.
Случай 2 , однако немного сложнее с Oracle, так как нет условия LIMIT ... OFFSET
, как в других RDBMS. Поэтому мы вкладываем наш "бизнес""запрос в технической оболочке как таковой:
SELECT outer.* FROM (
SELECT * FROM (
SELECT inner.*, ROWNUM as RNUM, MAX(ROWNUM) OVER(PARTITION BY 1) as TOTAL_ROWS
FROM (
[... USER SORTED business query ...]
) inner
)
WHERE ROWNUM < ?
) outer
WHERE outer.RNUM > ?
Обратите внимание, что поле TOTAL_ROWS
рассчитано, чтобы узнать, сколько страниц у нас будет, даже без извлечения всех данных. Теперь этот пейджинговый запрос обычно вполне удовлетворителен.Но время от времени (как я уже сказал, при запросе записей 5M +, возможно, включая неиндексированные поиски), это выполняется в течение 2-3 минут.
EDIT : обратите внимание, что потенциальная возможностьУзкое место не так легко обойти, потому что сортировка, которая должна быть применена перед подкачкой!
Интересно, это современное моделирование LIMIT ... OFFSET
, включая TOTAL_ROWS
вOracle,или есть лучшее решение, которое будет быстрее по конструкции, например, используя оконную функцию ROW_NUMBER()
вместо псевдостолбца ROWNUM
?