Оптимизация ORDER BY RAND()

Игорь Грегорченко

Как выбрать случайную запись из таблицы в Mysql?
SELECT id FROM files **ORDER BY rand()** LIMIT 1;

Но такие запросы работают очень медленно. Посмотрим на EXPLAIN:
**EXPLAIN** SELECT id FROM files ORDER BY rand() LIMIT 1;

Увидим, что Mysql создает временную таблицу и использует сортировку всех данных. Такой запрос будет работать все медленнее при наполнении таблицы:

+----+-----+------+----------------------------------------------+
| id | ... | rows | Extra                                        |
+----+-----+------+----------------------------------------------+
|  1 | ... | ***4921 | Using index; Using temporary; Using filesort*** |
+----+-----+------+----------------------------------------------+

Правильным решением будет использование индекса и избавление от ORDER BY RAND(). Для этого нужно:

  • Определить максимальное значение ID (да, такая колонка должна быть и она должна быть ключом) в таблице.
  • Получить любое случайное число от нуля до максимального ID.
  • Выбрать первую запись из таблицы, где ID больше указанного случайного числа, отсортировав ее по этой же колонке.

Если перевести все в запрос:
SELECT f.id FROM files f

JOIN ( SELECT RAND() * (SELECT MAX(id) FROM files) AS max_id ) AS m

WHERE f.id >= m.max_id

ORDER BY f.id ASC

LIMIT 1;
## Эффективная замена ORDER BY RAND()

Как это работает

  • Во вложенном запросе мы определяем максимальное значение ID. Допустим оно будет 100000.
  • Дальше умножаем это значение на функцию RAND(). Она возвращает значение от 0 до 1. Пусть в примере будет 0.5. Тогда результат умножения будет 50000.
  • После этого это значение с помощью JOIN прибавляется в каждой строке оригинальной таблицы.
  • Фильтр f.id >= m.max_id выберет первую попавшуюся запись, ID которой будет больше 50000.
  • Поскольку мы использовали сортировку ORDER BY f.id ASC, все пропущенные записи будут иметь значение меньше 50000.
  • Это значит, что мы выбрали случайную запись из всей таблицы. Но в отличие от ORDER BY RAND(), мы использовали сортировку и фильтрацию по индексу ID (а значит эффективно).

Скорость такого запроса будет в несколько раз быстрее, чем оригинального:

mysql> SELECT id FROM files ORDER BY rand() LIMIT 1;
+-------+
| id    |
+-------+
| 72643 |
+-------+
***1 row in set (0.17 sec)***

Ускоренная версия:

mysql> SELECT f.id FROM files f JOIN ( SELECT rand() * (SELECT max(id) from files) AS max_id ) AS m WHERE f.id >= m.max_id ORDER BY f.id ASC LIMIT 1;
+-------+
| id    |
+-------+
| 86949 |
+-------+
**1 row in set (0.00 sec)**

Теперь работает быстро и не зависит от размера таблицы.

Останні статті

Обучение Power BI – какие онлайн курсы аналитики выбрать

Сегодня мы поговорим о том, как выбрать лучшие курсы Power BI в Украине, особенно для…

13.01.2024

Work.ua назвал самые конкурентные вакансии в IТ за 2023 год

В 2023 году во всех крупнейших регионах конкуренция за вакансию выросла на 5–12%. Не исключением…

08.12.2023

Украинская IT-рекрутерка создала бесплатный трекер поиска работы

Unicorn Hunter/Talent Manager Лина Калиш создала бесплатный трекер поиска работы в Notion, систематизирующий все этапы…

07.12.2023

Mate academy отправит работников в 10-дневный оплачиваемый отпуск

Edtech-стартап Mate academy принял решение отправить своих работников в десятидневный отпуск – с 25 декабря…

07.12.2023

Переписки, фото, история браузера: киевский программист зарабатывал на шпионаже

Служба безопасности Украины задержала в Киеве 46-летнего программиста, который за деньги устанавливал шпионские программы и…

07.12.2023

Как вырасти до сеньйора? Девелопер создал популярную подборку на Github

IT-специалист Джордан Катлер создал и выложил на Github подборку разнообразных ресурсов, которые помогут достичь уровня…

07.12.2023