Выборка случайной записи из таблицы базы данных MySQL
Простейшим способом отбора записей из базы данных MySQL является “ORDER BY RAND()” в условии запроса.
SELECT * FROM `table` ORDER BY RAND() LIMIT 0,1;
Проблемой этого метода есть то, что он очень медленный. Причиной такого медленного выполнения есть то, что MySQL создает временную таблицу со всеми результирующими записями и назначает каждой из них случайный индекс сортировки. Результат сортируется и возвращается.
Есть несколько приемов ускорить это.
Основная идея это получить случайный номер и тогда выбрать определенную запись используя этот номер.
В случае если все номера имеют уникальные ID мы будем выбирать случайный номер между наименьшим и наибольшим ID и затем выберем запись с ID что соответствует этому номеру. Что бы сделать этот способ рабочим когда ID распределены не равномерно будем использовать “>=” оператор вместо “=” в последнем запросе.
Получить минимальное и максимальное значение ID в заданной таблице мы будем использовать MAX() и MIN() обобщающие функции. Эти функции будут возвращать минимальное и максимальное значение в определенных группах. Группа в нашем случае это все значения ID колонки в нашей таблице.
$range_result = mysql_query( ” SELECT MAX(`id`) AS max_id , MIN(`id`) AS min_id FROM `table` “);
$range_row = mysql_fetch_object( $range_result );
$random = mt_rand( $range_row->min_id , $range_row->max_id );
$result = mysql_query( ” SELECT * FROM `table` WHERE `id` >= $random LIMIT 0,1 “);
Как вы заметили этот метод ограничивается таблицами с уникальными ID для каждой записи. Что делать если у нас другая таблица?
Решением может быть использование MySQL LIMIT условия. LIMIT принимает два аргумента. Первый аргумент определяет сдвиг первой записи для выборки, и второй определяет максимальное количество записей для выборки. По умолчанию для сдвига назначается число 0 (не 1).
Вычислять сдвиг к первой записи мы будем генерируя случайный номер между 0 и 1 используя MySQL’s RAND() функцию. Затем мы умножим получившееся на количество записей в таблице, которые получим используя COUNT() функцию. Далее LIMIT аргументы должны задаваться как целые а не дробные значения поэтому заокруглим получившееся значение используя FLOOR() функцию. FLOOR() это арифметическая функция что вычисляет наибольее целое значение что меньше или равно заданному. Результирующий код будет выглядеть так:
$offset_result = mysql_query( ” SELECT FLOOR(RAND() * COUNT(*)) AS `offset` FROM `table` “);
$offset_row = mysql_fetch_object( $offset_result );
$offset = $offset_row->offset;
$result = mysql_query( ” SELECT * FROM `table` LIMIT $offset, 1 ” );
В MySQL 4.1 и более поздних версиях мы можем комбинировать два предыдущих метода использую подзапросы:
SELECT * FROM `table` WHERE id >= (SELECT FLOOR( MAX(id) * RAND()) FROM `table` ) ORDER BY id LIMIT 1;
Это решение имеет тоже слабое место что и Решение 2, то есть оно работает только с таблицами с уникальными ID.
Помните причину по который мы начали искать альтернативный способ выборки случайной записи? Скорость! Итак как сравнить эти методы по времени выполнения. Я не буду вдаваться в описание железа и настроек программного обеспечения. Приблизительный результат:
- Медленнее всех оказался метод для Решения 1. Это будет 100% времени на выполнение.
- Решение 2 – занял 79%
- Решение 3 – 13%
- Решение 4 – 16%
Победителем оказалось Решение 3.