TGViewer
SQL Ready | Базы Данных SQL Ready | Базы Данных @sql_ready · 17.5K subscribers
Post #2005 1.77K
Разбираем почему ORDER BY RANDOM() на больших таблицах не лучшая идея!

Если нужно вытащить случайные строки из таблицы, многие пишут так, например в PostgreSQL:
SELECT *
FROM products
ORDER BY RANDOM()
LIMIT 10;


На первый взгляд всё отлично: перемешали строки и взяли первые 10. Но под капотом всё не так красиво. Обычно база читает много строк, а часто вообще всю таблицу, затем вычисляет случайное значение для каждой строки, сортирует результат и только после этого применяет LIMIT.

То есть даже если нужна всего одна строка:
SELECT *
FROM products
ORDER BY RANDOM()
LIMIT 1;


на большой таблице это всё равно может оказаться тяжёлым запросом. Если строк миллионы, начинаются проблемы с CPU, памятью, сортировками и latency. Индексы здесь обычно не помогают.

Один из более дешёвых вариантов — случайный выбор через id:
SELECT *
FROM products
WHERE id >= 1 + FLOOR(RANDOM() * (SELECT MAX(id) FROM products))::int
ORDER BY id
LIMIT 1;


Этот вариант уже может использовать индекс по id. Но есть нюанс: если после удалений в id много дырок, распределение будет неидеальным.

Например:
id
1
2
3
10000
10001


В таком случае чаще будет выпадать первая существующая строка после большого разрыва — здесь это 10000. То есть выборка получается смещённой.

Ещё один вариант — случайный OFFSET:
SELECT *
FROM products
OFFSET FLOOR(RANDOM() * (SELECT COUNT(*) FROM products))::int
LIMIT 1;


Звучит неплохо. Но у OFFSET тоже есть проблема: чем больше offset, тем больше строк базе придётся пропустить. Плюс в PostgreSQL COUNT(*) на большой таблице тоже может быть дорогой операцией.

В PostgreSQL есть ещё TABLESAMPLE:
SELECT *
FROM products TABLESAMPLE SYSTEM (1);


Или:
SELECT *
FROM products TABLESAMPLE BERNOULLI (1);


На больших таблицах это часто работает заметно быстрее. Но важно понимать: TABLESAMPLE выбирает примерный процент таблицы, а не конкретное количество строк.

Если нужно, например, 10 строк, обычно добавляют LIMIT:
SELECT *
FROM products TABLESAMPLE SYSTEM (1)
LIMIT 10;


Но и тут есть нюанс: если sample слишком маленький, запрос может вернуть меньше 10 строк.

И здесь тоже есть компромисс между скоростью и качеством случайной выборки. SYSTEM работает быстрее, но выбирает данные блоками, поэтому распределение менее равномерное. BERNOULLI ближе к построчной случайной выборке, но обычно дороже. В общем, мысль простая:
ORDER BY RANDOM()


выглядит красиво и удобно. Но на больших таблицах это один из тех запросов, которые начинают стоить очень дорого.

🔥 Именно такие простые запросы часто становятся причиной деградации производительности в продакшн.

➡️ SQL Ready | #практика
  • 👍 11
  • 🔥 10
  • 🤝 7
  • ❤ 1
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 →