





Может быть, ты мог бы сделать что-нибудь вроде
SELECT * FROM table
WHERE id=
(FLOOR(RAND() *
(SELECT COUNT(*) FROM table)
)
);
Предполагается, что все ваши идентификационные номера являются последовательными без пробелов.
На самом деле вы можете захотеть CEIL вместо FLOOR, в зависимости от того, начинается ли ваш идентификатор с 0 или 1
Это предполагает, что выражение кэшируется и не пересчитывается для каждой строки.
В первичном ключе есть пробелы, так как некоторые строки удаляются.
Добавьте столбец, содержащий вычисленное случайное значение, в каждую строку и используйте его в предложении упорядочивания, ограничиваясь одним результатом при выборе. Это работает быстрее, чем сканирование таблицы, вызываемое ORDER BY RANDOM().
Обновлять: Вам все равно нужно вычислить какое-то случайное значение перед выдачей оператора SELECT при извлечении, конечно, например
SELECT * FROM `foo` WHERE `foo_rand` >= {some random value} LIMIT 1
Я думала об этом. Добавьте новый индексированный столбец и при создании строки назначьте ему случайное целое число. Но проблема в том, что я храню ненужные данные, и вам все равно придется сделать что-то еще, чтобы фактически получить из него случайную строку, поскольку данные случайного столбца статичны.
Почему это -2, а у Сезара Би +17? Мне они кажутся почти такими же.
Должен ли он быть «ВЫБРАТЬ * ИЗ foo ГДЕ foo_rand> = {какое-то случайное значение} ЗАКАЗАТЬ ПО foo_rand LIMIT 1»?
Что делать, если ваше {некоторое случайное значение} больше, чем наивысшее заранее сгенерированное случайное число в таблице. Вы вернете пустой набор записей.
Классический "ВЫБРАТЬ идентификатор ИЗ таблицы ORDER BY RAND () LIMIT 1" на самом деле подходит.
См. Следующий отрывок из руководства MySQL:
Если вы используете LIMIT row_count с ORDER BY, MySQL завершит сортировку, как только найдет первые строки row_count отсортированного результата, вместо сортировки всего результата.
Но он все равно должен присвоить случайный номер каждой записи, не так ли? Я спрашиваю, потому что это объяснение не имеет для меня особого смысла: как он будет возвращать первые N отсортированных строк, если весь набор результатов не отсортирован: S
@igelkott, все еще проблема с производительностью, я думаю, это не нормально
Простой, но медленный способ (подходит для небольших столов)
SELECT * from TABLE order by RAND() LIMIT 1
Это произведет случайное значение для всех строк в таблице, сортировку, а затем захват одной строки. Это не быстро.
Правда. Тем не менее, это быстрое время разработки. (и во время ответа :-)). Я оставлю его здесь для пользователей небольших таблиц, которым он может понадобиться
"smallish" может быть на удивление маленьким (я столкнулся с проблемами с таблицей ввода 20k на виртуальном хосте), и отслеживание такого рода проблем может быть головной болью королевский. Сделайте себе одолжение и с самого начала используйте правильный алгоритм.
Это приведет к большой потере производительности для больших таблиц. Отметьте этот аналогичный вопрос stackoverflow.com/questions/1244555/…
Возьмите все идентификаторы, выберите из него случайный и получите полную строку.
Если вы знаете, что идентификаторы являются последовательными без дыр, вы можете просто взять максимум и вычислить случайный идентификатор.
Если тут и там есть дыры, но в основном последовательные значения, и вас не волнует слегка искаженная случайность, возьмите максимальное значение, вычислите идентификатор и выберите первую строку с идентификатором, равным или превышающим тот, который вы вычислили. Причина перекоса заключается в том, что идентификатор, следующий за такими дырами, будет иметь больше шансов быть выбранным, чем тот, который следует за другим идентификатором.
Если вы заказываете случайным образом, у вас будет ужасное сканирование таблицы, и слово быстро не применимо к такому решению.
Не делайте этого, и вы не должны заказывать по GUID, у него та же проблема.
При заказе вы сделаете полное сканирование стола. Лучше всего, если вы выполните счетчик выбора (*), а затем получите случайную строку = rownum между 0 и последним реестром
В псевдокоде:
sql "select id from table"
store result in list
n = random(size of list)
sql "select * from table where id = " + list[n]
Это предполагает, что id является уникальным (первичным) ключом.
Если идентификаторы не меняются часто, вы даже можете сохранить список идентификаторов в памяти, чтобы ускорить работу.
Что, если есть миллиард строк? Это означает, что ваша переменная списка огромна.
Я знал, что должен быть способ сделать это с помощью одного запроса быстро. И вот оно:
Быстрый способ без использования внешнего кода, спасибо
http://jan.kneschke.de/projects/mysql/order-by-rand/
SELECT name
FROM random AS r1 JOIN
(SELECT (RAND() *
(SELECT MAX(id)
FROM random)) AS id)
AS r2
WHERE r1.id >= r2.id
ORDER BY r1.id ASC
LIMIT 1;
Обратите внимание на компромисс: чтобы быть уверенным в получении результата с первой попытки, с большей вероятностью будут выбраны любые ключи, которым предшествуют пробелы. Например, для двух записей с ключами 1 и 10 запись с ключом 10 будет выбрана в 90% случаев.
Да, вы можете получить лучшее распределение, если ключи будут без пробелов и без предложений WHERE и ORDER BY. Проверьте статью, там все довольно хорошо объяснено. Я не хотел воровать все это, поэтому не стал задавать другие вопросы, плюсы и минусы каждого из них.
Этот запрос почему-то не возвращает данные в какой-то момент, когда вы указываете какой-то дополнительный параметр, например WHERE r1.id> = r2.id AND r1.some_field = 1, в то время как some_field содержит data = 1. Есть идеи, как это решить?
Вот решение, которое работает довольно быстро и получает лучшее случайное распределение, независимо от того, являются ли значения id смежными или начинаются с 1.
SET @r := (SELECT ROUND(RAND() * (SELECT COUNT(*) FROM mytable)));
SET @sql := CONCAT('SELECT * FROM mytable LIMIT ', @r, ', 1');
PREPARE stmt1 FROM @sql;
EXECUTE stmt1;
Как получить строку, возвращаемую этим SQL-запросом, с помощью PHP? Установка $query, как указано выше, а затем выполнение обычного mysql_query($query) не возвращает никаких результатов. Спасибо.
Это 1,5 сканирования таблицы - 1 для COUNT(*) (при условии InnoDB), что-то меньшее, чем полное сканирование для OFFSET @r. Но он отлично работает случайным образом и не зависит от свойств идентификатора.
@RickJames, верно. Другое решение - пронумеровать строки новым столбцом, заполненным серийными целыми числами. Тогда можно получить максимальное значение с помощью MAX () вместо COUNT (), а затем выбрать его по индексу, не пытаясь справиться с пробелами. Хотя это решение требует перенумерации по мере того, как строки приходят и уходят.
MediaWiki использует интересный трюк (для функции Wikipedia Special: Random): в таблице со статьями есть дополнительный столбец со случайным числом (генерируемым при создании статьи). Чтобы получить случайную статью, сгенерируйте случайное число и получите статью со следующим большим или меньшим (не помню, какое) значение в столбце случайных чисел. С индексом это может быть очень быстро. (А MediaWiki написана на PHP и разработана для MySQL.)
Такой подход может вызвать проблемы, если полученные числа плохо распределены; IIRC, это было исправлено в MediaWiki, поэтому, если вы решите сделать это таким образом, вам следует взглянуть на код, чтобы увидеть, как это делается в настоящее время (возможно, они периодически регенерируют столбец случайных чисел).
Это прекрасная идея. Есть ли статья или другой ресурс, подробно описывающий это?
это хорошая идея, но для N желаемых результатов может не сработать, я думаю, потому что вы можете получить меньше результатов или порядок может быть таким же.
Хорошая идея. Но в запросе нам все равно нужно сортировать по случайному столбцу, верно? Предположим, что случайный столбец - это random_number, тогда запрос будет иметь вид: «SELECT * FROM mytable WHERE random_number> $ rand ORDER BY random_number LIMIT 1». Это намного быстрее, чем ORDER BY RAND ()?
Вам нужно будет установить степень ограничения на максимальное количество случайных чисел относительно текущего количества записей. Затем увеличивайте этот предел со степенью корреляции с количеством строк в таблице по мере его увеличения. Пример - когда записей мало. Скажем, у вас есть 3. Без ограничения случайного числа вы можете, скажем, иметь 2 очень маленьких числа и одно большое. Наименьшее из трех почти никогда не будет вызвано, когда разрыв между минимальным, самим и средним числом настолько мал. Что делать, если min = 0, max = 100 с 3 записями, а присвоенным rand # было 49, 50, 51?
Я этого не понимаю. Чем это отличается от случайного выбора числа от 1 до max (id) и выбора записи с этим идентификатором? Зачем нужен лишний столбец?
Это действительно отличная идея, в восторге!
Создайте индекс для столбца, содержащего случайное число. Тогда SELECT id FROM table WHERE randNumb >= RAND() ORDER BY randNumb LIMIT 5 получит индексное ускорение. Если вы используете MyISAM, вам может потребоваться составной индекс на (randNumb, id).
э. это смещение очень сильно. рассмотрим исходный набор из трех страниц: одна случайная оказалась на 0,1, другая - на 0,3, а последняя - на 0,9. это означает, что шанс равен 0,1; у одного шанс 0,2; у одного шанс 0,6; и последний шанс 0,1 распределяется, однако он обрабатывает убытки верхней линии. в лучшем случае - 6-кратное искажение; худшее это 7х. "но со временем это выровняется!" взгляд на индекс показывает, что после почти двух десятилетий существования одного из крупнейших сайтов на Земле, этого не произошло. он фактически получает хуже с течением времени, поскольку близкие совпадения сокращают существование других страниц. ужасная идея
SET @COUNTER=SELECT COUNT(*) FROM your_table;
SELECT PrimaryKey
FROM your_table
LIMIT 1 OFFSET (RAND() * @COUNTER);
Сложность первого запроса составляет O (1) для таблиц MyISAM.
Второй запрос сопровождает полное сканирование таблицы. Сложность = O (n)
Только для этого ведите отдельную таблицу. Вы также должны вставлять те же строки в эту таблицу при каждой вставке в исходную таблицу. Предположение: без DELETE.
CREATE TABLE Aux(
MyPK INT AUTO_INCREMENT,
PrimaryKey INT
);
SET @MaxPK = (SELECT MAX(MyPK) FROM Aux);
SET @RandPK = CAST(RANDOM() * @MaxPK, INT)
SET @PrimaryKey = (SELECT PrimaryKey FROM Aux WHERE MyPK = @RandPK);
Если DELETE разрешены,
SET @delta = CAST(@RandPK/10, INT);
SET @PrimaryKey = (SELECT PrimaryKey
FROM Aux
WHERE MyPK BETWEEN @RandPK - @delta AND @RandPK + @delta
LIMIT 1);
Общая сложность - O (1).
Я немного новичок в SQL, но как насчет создания случайного числа в PHP и использования
SELECT * FROM the_table WHERE primary_key >= $randNr
это не решает проблемы с отверстиями в столе.
Но вот поворот в предложении лассевков:
SELECT primary_key FROM the_table
Используйте mysql_num_rows () в PHP, чтобы создать случайное число на основе приведенного выше результата:
SELECT * FROM the_table WHERE primary_key = rand_number
Кстати, насколько медленен SELECT * FROM the_table:
.
Создание случайного числа на основе mysql_num_rows() и последующее перемещение указателя данных в эту точку mysql_data_seek(). Насколько медленно это будет происходить на больших таблицах с миллионом строк?
Взгляните на эта ссылка Яна Кнешке или этот ТАК ответ, поскольку они оба обсуждают один и тот же вопрос. Ответ SO также включает различные варианты и дает несколько хороших предложений в зависимости от ваших потребностей. Ян перебирает все возможные варианты и рабочие характеристики каждого из них. В итоге он предлагает следующий наиболее оптимизированный метод, с помощью которого это можно сделать в MySQL select:
SELECT name
FROM random AS r1 JOIN
(SELECT (RAND() *
(SELECT MAX(id)
FROM random)) AS id)
AS r2
WHERE r1.id >= r2.id
ORDER BY r1.id ASC
LIMIT 1;
HTH,
-Дипин
Для выбора нескольких случайных строк из данной таблицы (скажем, «слова») наша команда придумала такую красоту:
SELECT * FROM
`words` AS r1 JOIN
(SELECT MAX(`WordID`) as wid_c FROM `words`) as tmp1
WHERE r1.WordID >= (SELECT (RAND() * tmp1.wid_c) AS id) LIMIT n
Есть еще один способ создания случайных строк, используя только запрос и без порядка с помощью rand (). Он включает в себя пользовательские переменные. См. как создавать случайные строки из таблицы
если вы не удаляете строку в этой таблице, наиболее эффективный способ:
(если вы знаете минимальный идентификатор, просто пропустите его)
SELECT MIN(id) AS minId, MAX(id) AS maxId FROM table WHERE 1
$randId=mt_rand((int)$row['minId'], (int)$row['maxId']);
SELECT id,name,... FROM table WHERE id=$randId LIMIT 1
Чтобы найти случайные строки из таблицы, не используйте ORDER BY RAND (), потому что он заставляет MySQL выполнять полную сортировку файла и только затем извлекать необходимое количество строк. Чтобы избежать этой полной сортировки файлов, используйте функцию RAND () только в предложении where. Он остановится, как только наберет необходимое количество строк. Видеть http://www.rndblog.com/how-to-select-random-rows-in-mysql/
Я столкнулся с проблемой, когда мои идентификаторы не были последовательными. Что я придумал это.
SELECT * FROM products WHERE RAND()<=(5/(SELECT COUNT(*) FROM products)) LIMIT 1
Возвращено примерно 5 строк, но я ограничиваю их до 1.
Если вы хотите добавить еще одно предложение WHERE, это станет немного интереснее. Допустим, вы хотите найти товары со скидкой.
SELECT * FROM products WHERE RAND()<=(100/(SELECT COUNT(*) FROM pt_products)) AND discount<.2 LIMIT 1
Что вам нужно сделать, так это убедиться, что вы возвращаете достаточный результат, поэтому я установил его на 100. Наличие предложения WHERE Discount <.2 в подзапросе было в 10 раз медленнее, поэтому лучше возвращать больше результатов и лимит.
SELECT DISTINCT * FROM yourTable WHERE 4 = 4 LIMIT 1;
Я вижу здесь множество решений. Один или два кажутся нормальными, но у других решений есть некоторые ограничения. Но следующее решение будет работать для любой ситуации
select a.* from random_data a, (select max(id)*rand() randid from random_data) b
where a.id >= b.randid limit 1;
Здесь id не обязательно должен быть последовательным. Это может быть любой столбец с первичным ключом / уникальный / автоинкремент. См. Следующий Самый быстрый способ выбрать случайную строку из большой таблицы MySQL
Спасибо Зиллур - www.techinfobest.com
Используйте приведенный ниже запрос, чтобы получить случайную строку
SELECT user_firstname ,
COUNT(DISTINCT usr_fk_id) cnt
FROM userdetails
GROUP BY usr_fk_id
ORDER BY cnt ASC
LIMIT 1
В моем случае у моей таблицы есть идентификатор в качестве первичного ключа, автоинкремент без пробелов, поэтому я могу использовать COUNT(*) или MAX(id), чтобы получить количество строк.
Я сделал этот скрипт, чтобы проверить самую быструю операцию:
logTime();
query("SELECT COUNT(id) FROM tbl");
logTime();
query("SELECT MAX(id) FROM tbl");
logTime();
query("SELECT id FROM tbl ORDER BY id DESC LIMIT 1");
logTime();
Результаты следующие:
36.8418693542479 ms0.241041183472 ms0.216960906982 msОтветьте с методом заказа:
SELECT FLOOR(RAND() * (
SELECT id FROM tbl ORDER BY id DESC LIMIT 1
)) n FROM tbl LIMIT 1
...
SELECT * FROM tbl WHERE id = $result;
Я использовал это, и работа была сделана ссылка из здесь
SELECT * FROM myTable WHERE RAND()<(SELECT ((30/COUNT(*))*10) FROM myTable) ORDER BY RAND() LIMIT 30;
Создайте функцию, чтобы сделать это, скорее всего, это лучший ответ и самый быстрый ответ здесь!
Плюсы - Работает даже с пробелами и очень быстро.
<?
$sqlConnect = mysqli_connect('localhost','username','password','database');
function rando($data,$find,$max = '0'){
global $sqlConnect; // Set as mysqli connection variable, fetches variable outside of function set as GLOBAL
if ($data == 's1'){
$query = mysqli_query($sqlConnect, "SELECT * FROM `yourtable` ORDER BY `id` DESC LIMIT {$find},1");
$fetched_data = mysqli_fetch_assoc($query);
if (mysqli_num_rows($fetched_data>0){
return $fetch_$data;
}else{
rando('','',$max); // Start Over the results returned nothing
}
}else{
if ($max != '0'){
$irand = rand(0,$max);
rando('s1',$irand,$max); // Start rando with new random ID to fetch
}else{
$query = mysqli_query($sqlConnect, "SELECT `id` FROM `yourtable` ORDER BY `id` DESC LIMIT 0,1");
$fetched_data = mysqli_fetch_assoc($query);
$max = $fetched_data['id'];
$irand = rand(1,$max);
rando('s1',$irand,$max); // Runs rando against the random ID we have selected if data exist will return
}
}
}
$your_data = rando(); // Returns listing data for a random entry as a ASSOC ARRAY
?>
Имейте в виду, что этот код не тестировался, но представляет собой рабочую концепцию, позволяющую возвращать случайные записи даже с пробелами. Пока пробелы недостаточно велики, чтобы вызвать проблему времени загрузки.
возможный дубликат MySQL быстро выбирает 10 случайных строк из 600 тыс.