Пагинация (постраничный вывод) обеспечивает обработку больших результатов SELECT без излишней нагрузки на сервер и приложение. Главные подходы:
Пример (LIMIT/OFFSET)
SELECT * FROM Orders ORDER BY OrderID LIMIT 100 OFFSET 1000;
Этот запрос возвращает 1011–1110-ю записи. Но OFFSET заставляет сервер всё равно пересортировать и пропустить первые 1000 строк — поэтому при глубокой пагинации он становится медленным.
Пример (Keyset/Seek method)
SELECT * FROM Orders WHERE OrderID > 1110 ORDER BY OrderID LIMIT 100;
Такой запрос быстро ищет следующую страницу, не затрачивая ресурсы на подсчёт OFFSET, особенно эффективно при наличии индекса по OrderID.
Подвох: "Почему пагинация через LIMIT/OFFSET может плохо работать при больших объёмах данных и как это улучшить?"
Ответ: Трудность в том, что OFFSET требует серверу сканировать и сортировать все предыдущие строки — получается, что глубже по страницам – тем медленнее. Оптимизация — перейти на keyset/seek paging: выбирать не по смещению, а по ключу последней записи предыдущей страницы.
-- Получаем следующую страницу по ключу SELECT * FROM Orders WHERE OrderID > @LastOrderID ORDER BY OrderID LIMIT 100;
История
Проект: Маркетплейс, база заказов за 5 лет (50 млн строк)
Ошибка: Использовали OFFSET для пагинации заказов старых пользователей. Запросы с OFFSET > 1 млн начинали выполняться по 30–60 секунд. Это повлияло на отчёты и API-интерфейс — из-за этого нагружались CPU-сервера и росло время ожидания в очереди.
История
Проект: Корпоративная CRM, отчёты по клиентам.
Ошибка: В пагинации не учитывали порядок сортировки и не использовали индексы. Падали производительность и контроль целостности выборок — пользователи получали одни и те же строки на разных страницах при изменениях в таблице.
История
Проект: Финансовая платформа, дашборды.
Ошибка: Сложные пагинации строились через генерируемый динамический SQL без bind-переменных, что привело к SQL-инъекциям и проблемам с поддержкой транзакционности между страницами данных.