Вопрос проверяет, умеешь ли ты реализовать пагинацию и понимаешь ли различия между offset-пагинацией и курсорной пагинацией.
Для постраничного чтения чаще всего используют limit() и offset() вместе с order_by(). На больших таблицах предпочтительнее курсорная пагинация: фильтрация по ключу (id > last_id) и limit(). В SQLAlchemy это делается через select(...).where(...).order_by(...).limit(...). Важно, чтобы порядок был стабильным, иначе страницы могут «прыгать».
Постраничное чтение — это способ брать данные порциями фиксированного размера, чтобы не перегружать память и сеть, и чтобы запросы были предсказуемыми.
Pagination — разбиение результата запроса на страницы (chunks) с возможностью получать их по очереди.
Основные методы:
limit(n) — ограничить число строк в ответе.
offset(k) — пропустить первые k строк.
order_by(...) — стабилизировать порядок.
stmt = (
select(User)
.order_by(User.created_at, User.id)
.limit(50)
.offset(100)
)
users = session.execute(stmt).scalars().all()
Что важно:
Всегда добавляй order_by, иначе «страницы» могут быть нестабильными.
На больших таблицах большой offset может стать узким местом.
Методы SQLAlchemy те же (where, order_by, limit), но логика другая.
page_size = 50
last_created_at = prev_last_created_at
last_id = prev_last_id
stmt = (
select(User)
.where(
(User.created_at > last_created_at)
| ((User.created_at == last_created_at) & (User.id > last_id))
)
.order_by(User.created_at, User.id)
.limit(page_size)
)
page = session.execute(stmt).scalars().all()
Почему так:
Если сортировка не уникальна (created_at может повторяться), добавляют второй ключ (id) для стабильности.
Иногда «постраничность» нужна не для UI, а для безопасной обработки.
stmt = select(User.id).order_by(User.id)
for (user_id,) in session.execute(stmt):
handle(user_id)
Если нужен UI-режим «страницы 1,2,3» — часто используют limit/offset. Если нужно надежно и быстро читать большие таблицы — обычно выбирают keyset pagination через where + order_by + limit.