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

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

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

24 ответов


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

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

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

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

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


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

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

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

классический "SELECT id FROM table ORDER BY RAND () LIMIT 1" на самом деле в порядке.

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

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


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


посмотри этой ссылке Яна Kneschke или это так ответ поскольку они оба обсуждают один и тот же вопрос. Ответ 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

это не решает проблему с отверстиями в таблице.

но вот поворот на предложение lassevks:

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. Имея где скидка<.2>


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

select a.* from random_data a, (select max(id)*rand() randid  from random_data) b
     where a.id >= b.randid limit 1;

здесь, id, не нужно быть последовательным. Это может быть любой первичный ключ/уникальный/автоинкремент столбца. Пожалуйста, смотрите следующее самый быстрый способ выбрать случайную строку из большой таблицы MySQL

спасибо Zillur - 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;