Чем дольше занимаюсь кодингом, тем чаще втыкаюсь в такие вещи, которые сто раз уже использовал, а на 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
Все варианты расписаны тут.
Единственное, что не устроило во втором варианте, это два запроса к базе.
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 результатов?