TGViewer
SQL Ready | Базы Данных SQL Ready | Базы Данных @sql_ready · 17.5K subscribers
Post #1879 2.86K
LIMIT без ORDER BY — почему результат нестабилен!

Есть таблица:
orders(id, customer_id, amount, created_at)


И вот такой запрос:
SELECT * FROM orders LIMIT 10;


Интуитивно хочется думать, что это первые 10 или последние 10. На деле — это просто неопределённые 10 строк в неопределённом порядке.

Без ORDER BY база не обязана возвращать данные в каком-то фиксированном порядке. Сегодня это один набор строк, завтра — другой. Особенно если поменялся план выполнения или появился индекс.

Типичный кейс — хочу последние заказы:
SELECT * 
FROM orders
ORDER BY created_at DESC
LIMIT 10;


Уже лучше: теперь это 10 самых новых по created_at.

Но тут есть тонкость — если несколько строк имеют одинаковый created_at, порядок между ними не детерминирован. Иногда это всплывает в самых неожиданных местах (например, в пагинации).

Поэтому обычно добавляют второй критерий:
SELECT * 
FROM orders
ORDER BY created_at DESC, id DESC
LIMIT 10;


Если id уникальный — результат становится детерминированным для текущего среза данных. Где это особенно критично: пагинация, API, отчёты, кэш.

Без стабильного порядка начинаются фантомные баги: строки то появляются, то исчезают, то дублируются.

OFFSET и его ограничения:
SELECT * 
FROM orders
ORDER BY created_at DESC, id DESC
LIMIT 10 OFFSET 20;


Добавились новые записи — и страницы уже не совпадают с тем, что было секунду назад.

Поэтому в проде чаще используют keyset pagination:
SELECT * 
FROM orders
WHERE (created_at, id) < ('2026-01-10', 1050)
ORDER BY created_at DESC, id DESC
LIMIT 10;


Фактически: дай следующие записи после этой точки.

Работает быстрее и ведёт себя предсказуемо при изменениях данных (Важно: синтаксис с (col1, col2) поддерживается не во всех СУБД; универсальный вариант — через OR.)

И ещё частая ошибка:
SELECT * 
FROM orders
ORDER BY created_at
LIMIT 1;


Кажется, что это последняя запись, а по факту — самая старая (по умолчанию используется ASC в большинстве СУБД).

🔥 Вся суть: LIMIT отвечает только за количество. Порядок — это всегда ORDER BY. Без составного индекса (created_at, id) такие запросы на больших таблицах начинают ощутимо тормозить.

➡️ SQL Ready | #практика
  • 🔥 15
  • 👍 9
  • 🤝 8
  • ❤ 2
More from @sql_ready
  1. Oct 7, 2026📂 Шпаргалка по паттернам распределённых систем! Например, Replication повышает доступност…
  2. Oct 6, 2026Проверяйте связи с учётом периода! В PostgreSQL 18 связь может учитывать ещё и период его…
  3. Oct 6, 2026⚡️5 фундаментальных курсов по ИБ по цене одного Это предложение для тех, кто готов войти в…
  4. Oct 6, 2026👍 Устройство PostgreSQL — подробная документация на русском языке! Материалы посвящены не…
  5. Oct 5, 2026COUNT(*) в PostgreSQL: MVCC, visibility map и стоимость выполнения! В PostgreSQL точный CO…
  6. Oct 5, 2026Как оплачивать зарубежные сервисы в 2026 году? Можно бегать между посредниками и бояться б…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →