быстрый выбор случайной строки из большой таблицы в mysql

StackOverflow https://stackoverflow.com/questions/211329

  •  03-07-2019
  •  | 
  •  

Вопрос

Каков быстрый способ выбрать случайную строку из большой таблицы mysql?

Я работаю на php, но меня интересует любое решение, даже если оно на другом языке.

Это было полезно?

Решение

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

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

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

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

Не делайте этого и не делайте заказ по GUID, у него та же проблема.

Другие советы

Я знал, что должен быть способ сделать это в одном запросе быстрым способом.И вот оно здесь:

Быстрый способ без привлечения внешнего кода, престижность

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;

MediaWiki использует интересный трюк (для специальной функции Википедии: Random).:в таблице со статьями есть дополнительный столбец со случайным числом (генерируется при создании статьи).Чтобы получить случайную статью, сгенерируйте случайное число и получите статью со следующим большим или меньшим (не помню, каким именно) значением в столбце случайных чисел.С индексом это может быть очень быстро.(А MediaWiki написан на PHP и разработан для MySQL.)

Такой подход может вызвать проблему, если результирующие числа будут плохо распределены;IIRC, это было исправлено в MediaWiki, поэтому, если вы решите сделать это таким образом, вам следует взглянуть на код, чтобы увидеть, как это делается в данный момент (вероятно, они периодически восстанавливают столбец случайных чисел).

Вот решение, которое работает довольно быстро, и оно получает лучшее случайное распределение, не зависящее от того, являются ли значения 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;

Может быть, вы могли бы сделать что-то вроде:

SELECT * FROM table 
  WHERE id=
    (FLOOR(RAND() * 
           (SELECT COUNT(*) FROM table)
          )
    );

Это предполагает, что все ваши идентификационные номера расположены последовательно, без пробелов.

Добавьте столбец, содержащий вычисленное случайное значение, к каждой строке и используйте его в предложении ordering, ограничившись одним результатом при выборе.Это работает быстрее, чем сканирование таблицы, которая ORDER BY RANDOM() причины.

Обновить: Вам все еще нужно вычислить некоторое случайное значение перед выдачей SELECT заявление при извлечении, конечно, например

SELECT * FROM `foo` WHERE `foo_rand` >= {some random value} LIMIT 1

Простым, но медленным способом было бы (хорошо для небольших таблиц)

SELECT * from TABLE order by RAND() LIMIT 1

В псевдокоде:

sql "select id from table"
store result in list
n = random(size of list)
sql "select * from table where id=" + list[n]

Это предполагает, что id является уникальным (первичным) ключом.

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

Чтобы найти случайные строки из таблицы, не используйте ORDER BY RAND(), потому что это заставляет MySQL выполнять полную сортировку файлов и только затем извлекать требуемое предельное количество строк.Чтобы избежать такой полной сортировки файлов, используйте функцию RAND() только в предложении where.Он остановится, как только достигнет необходимого количества строк.Видишь http://www.rndblog.com/how-to-select-random-rows-in-mysql/

если вы не удаляете строку в этой таблице, наиболее эффективным способом является:

(если вы знаете идентификатор mininum, просто пропустите его)

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

Для выбора нескольких случайных строк из заданной таблицы (скажем, "слов") наша команда придумала эту красоту:

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

Классическое "ВЫБРАТЬ идентификатор ИЗ таблицы ORDER С ПОМОЩЬЮ RAND() LIMIT 1" на самом деле в порядке.

Смотрите следующий отрывок из руководства по MySQL:

Если вы используете LIMIT row_count с ORDER BY , MySQL завершает сортировку, как только находит первые строки row_count отсортированного результата, вместо сортировки всего результата.

При оформлении заказа вы сделаете полное сканирование таблицы.Лучше всего, если вы выполните select count(*), а позже получите случайную строку = rownum между 0 и последним реестром

Взгляните на эта ссылка автор : Ян Кнешке или это ТАКОЙ ответ поскольку они оба обсуждают один и тот же вопрос.В ответе 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,

-Дипин

Я немного новичок в 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().Насколько медленно это будет происходить на больших таблицах, скажем, с миллионом строк?

Я столкнулся с проблемой, когда мои идентификаторы не были последовательными.То, что я придумал с этим.

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<предложение .2 в подзапросе было в 10 раз медленнее, поэтому лучше возвращать больше результатов и ограничивать их.

Я вижу здесь множество решений.Один или два кажутся нормальными, но другие решения имеют некоторые ограничения.Но следующее решение будет работать для любой ситуации

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 ms
  • Макс.: 0.241041183472 ms
  • Порядок: 0.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
?>

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

Быстрый и грязный метод:

SET @COUNTER=SELECT COUNT(*) FROM your_table;

SELECT PrimaryKey
FROM your_table
LIMIT 1 OFFSET (RAND() * @COUNTER);

Сложность первого запроса равна O(1) для таблиц MyISAM.

Второй запрос сопровождается полным сканированием таблицы.Сложность = O(n)

Грязный и быстрый способ:

Заведите отдельную таблицу только для этой цели.Вы также должны вставлять одни и те же строки в эту таблицу при каждой вставке в исходную таблицу.Предположение:Никаких удалений.

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);

Если удаления разрешены,

SET @delta = CAST(@RandPK/10, INT);

SET @PrimaryKey = (SELECT PrimaryKey
                   FROM Aux
                   WHERE MyPK BETWEEN @RandPK - @delta AND @RandPK + @delta
                   LIMIT 1);

Общая сложность равна O (1).

SELECT DISTINCT * FROM yourTable WHERE 4 = 4 LIMIT 1;

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top