Производительность ORDER BY RAND() LIMIT
15 мая 2012 MySQL 12294 просмотра
Для получения нескольких случайных записей из таблицы я всегда использовал всем известную конструкцию ORDER BY RAND() LIMIT. На днях я столкнулся с проблемой в производительности этого запроса. Таблица содержала более миллиона строк. И мне нужно было срочно найти решение. Под катом альтернативный вариант выбора нескольких случайных строк.

Исходные данные.
Создадим таблицу и наполним ее большим количеством случайных чисел. Сущность test_value будет содержать значение php-функции uniqid(). Для этого теста я добавил 1 миллион записей.
CREATE TABLE test_rand(
    test_value VARCHAR (20) NOT NULL
)
ENGINE = MYISAM
Выберем 5 случайных записей первым методом.
SELECT *
FROM test_rand
ORDER BY RAND()
LIMIT 5
При тестировании запрос выполнялся 5 раз. Среднее время выполнения 1.06 с. Теперь попробуем другой вариант, суть его в следующем. Мы не сортируем строки и не обрезаем результат. Это похоже на цикл, для каждой записи мы имеем 2 переменные @count и @limit. @count уменьшается на 1 для каждой просматриваемой строки, @limit уменьшается для каждой выбранной строки.
SELECT test_value
FROM (
    SELECT @count := COUNT(*) + 1, @limit := 5
    FROM test_rand
) AS vars
STRAIGHT_JOIN (
    SELECT r.test_value, @limit := @limit - 1
    FROM test_rand AS r
    WHERE
        (@count := @count - 1)
        AND RAND() < @limit / @count
) AS i
Среднее время выполнения данного запроса 0.27 секунд. Прирост примерно в 4 раза при наших исходных данных. Стоит отметить, что в InnoDB таблицах такой разницы можно не заметить, так как COUNT() работает намного быстрее в MyISAM. Такой вариант лучше использовать для таблиц с большим количеством данных, так как он основан на вероятностном подходе. Как всегда решать вам. Если в таблице 10 строк и вам нужно выбрать 1 случайную, то с этим отлично справится ORDER BY RAND(). Если в таблице несколько миллионов записей, тогда уже стоит подумать.