Это бесконечная тема для меня, и мне интересно, могу ли я что-то игнорировать. По сути, я использую два типа операторов SQL в приложении:
- Регулярные запросы с "резервным" лимитом
- Отсортированные и запрограммированные запросы
Теперь мы говорим о некоторых запросах против таблиц с несколькими миллионами записей, соединенных с еще 5 таблицами с несколькими миллионами записей. Понятно, что мы вряд ли хотим получить их все, поэтому у нас есть два вышеописанных метода ограничения пользовательских запросов.
Случай 1 действительно прост. Мы добавим дополнительный фильтр ROWNUM
:
WHERE ...
AND ROWNUM < ?
Это довольно быстро, так как Oracle CBO учтет этот фильтр для своего плана выполнения и, вероятно, применит операцию FIRST_ROWS
(похожую на ту, которая применяется в подсказке /*+FIRST_ROWS*/
.
Случай 2, однако это немного сложнее с Oracle, так как нет предложения LIMIT ... OFFSET
, как в других СУБД. Поэтому мы вставляем наш "бизнес-запрос" в техническую оболочку как таковую:
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 минут.
РЕДАКТИРОВАТЬ. Обратите внимание, что потенциальное узкое место не так просто обойти, потому что сортировка должна быть применена до подкачки!
Мне интересно, это современное моделирование LIMIT ... OFFSET
, включая TOTAL_ROWS
в Oracle, или есть лучшее решение, которое будет быстрее по дизайну, например. используя ROW_NUMBER()
функцию окна вместо псевдо-столбца ROWNUM
?