MYSQL RAND(), как получить рандомную строку из БД

Чем дольше занимаюсь кодингом, тем чаще втыкаюсь в такие вещи, которые сто раз уже использовал, а на 101 сломал голову.

Есть таблица, в ней N тысяч записей, нужно вытащить одну рандомно, кой-чего с ней сделать, и если все ок то ее использовать, если нет то вернуть на место с соответствующей отметкой и взять другую, пока не будет найдена подходящая по условиям.
Я, особо не мудря, делаю цикл while до условия, пока не найдена подходящая запись. В цикле такой запрос к БД

SELECT * FROM `table` WHERE 1 ORDER BY RAND() LIMIT 1

По задумке, это возвращает каждый раз новую запись. Но у mysql есть такая приятная особенность — она кеширует запросы и в цикле каждый будет возвращаться одно и тоже поле. Наверно, если разрывать соединение, тогда будет возвращаться разное число. Я таких вещей в душе не знал, поэтому немного удивился, погуглив, что это самый дибильный способ вытащить рандомную запись из базы. На очень больших БД это завесит сервер напрочь.

Вариант получше заключался в том, чтобы вытащить максимальное значение записей и получить рандомное число в его пределах, затем запросить из базы строку с LIMIT рандомное число.

//запрос макс количества записей в переменную
SELECT FLOOR(RAND() * COUNT(*)) AS `offset` FROM `table`

//далее запрос с этой переменной
SELECT * FROM `table` LIMIT $somevar, 1

Он же по тестам получился самый быстрый.

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

SELECT * FROM `table` WHERE id >= (SELECT FLOOR( MAX( id ) * RAND( ) ) FROM `proxy` ) ORDER BY id LIMIT 1

Все варианты расписаны тут.

Единственное, что не устроило во втором варианте, это два запроса к базе.

3.93 avg. rating (78% score) - 14 votes

17 комментариев

  • Зачем так ломать себе голову? Есть много алььтернативных способов это сделать. Жаль что я пока не дошел до этого. Вскоре зайду и напушу как именно!

  • Хорошие альтернативы для ORDER BY RAND()

  • MySQL Error! pocemu? v cem pricina?
    ————————

  • Работает! Правда иногда криво, но работает Спасиб буду делать плагин:)

  • По моему это не единственный способ, где то я еще видел на форуме программистов по моему.

  • А для чего нужно было, для каких целей, достать рандомную строку из таблицы? Наверно, у меня нет фантазии, но я не могу придумать задачу, для которой это нужно было делать)))

  • RAND () — зло

    SELECT id FROM `table` — полученный массив в кэш на 2-3 часа
    потом <?php shuffle($array); ?>

    • Michael_XIII, а если скрипт прекращает работу а потом ее опять начинает итак 100500 раз, каждый раз тащить всю выборку из базы?

  • Мне проще аппендицит вырезать, чем понять как это в базе данных сделать…

  • Ну как бы загнать в мемкэш массив id.шников, особенно если они не меняются. Но RAND() — это медленно

  • что бы не кэшировал sql запрос надо писать
    SELECT SQL_NO_CACHE ….. и все

    • Евгений, пробовал, в течении одного подключения и маленького промежутка времени все равно не помогает. Если в цикле 1000 запросов делаешь, выдаст разных ответов только штук 10 отсилы. Поэтому люди и велик изобретают.

  • Но RAND() – это медленно
    Быстро, если индексы корректно прописать. Во всяком случае быстрее чем сливать сначала в Memcached и потом оттуда подчитывать.

  • Ох, уже прежде чем других учить, хотя бы сами погляди план запросов.
    Первый сделает фуллскан, почему зря, + генерация случайного числа для каждой записи, сервер помрет на таблице из пары миллионов записей.
    ——
    Второй запрос: первая его часть хоть и сможет использовать индекс, так не сможет взять оптимизироваться и взять значение количества записей из системной таблицы.
    А вторая его часть — опять же большая потеря времени — уж так работает LIMIT.
    ——
    Третий запрос потеряет индекс, условие WHERE будет сканить всю таблицу, можете убедиться поглядев план запроса.
    Спасибо за внимание.

  • Совершенно верно, оба способа кривые, второй к тому же рассчитан на ID без пропусков, а в реальной жизни такого не бывает. Если с этим смириться, а таблица MyISAM, то лучше объединить оба способа:

    set @count=FLOOR(RAND() * (SELECT COUNT(*) FROM `table`);
    SELECT * FROM `table` LIMIT @count, 1

  • $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, 14 » );

    все работает прекрасно но есть одно но. бывает такое что выводиться меньше чем 14 записей. Подскажите как исправить это???

    • А там есть 14 результатов?

css.php