MySQL быстро выбирает 10 случайных строк из 600К строк - PullRequest
429 голосов
/ 02 декабря 2010

Как лучше всего написать запрос, который выбирает 10 строк случайным образом из общего числа 600 КБ?

Ответы [ 24 ]

363 голосов
/ 02 декабря 2010

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

http://jan.kneschke.de/projects/mysql/order-by-rand/

Для наиболее общего случая вот как вы это делаете:

SELECT name
  FROM random AS r1 JOIN
       (SELECT CEIL(RAND() *
                     (SELECT MAX(id)
                        FROM random)) AS id)
        AS r2
 WHERE r1.id >= r2.id
 ORDER BY r1.id ASC
 LIMIT 1

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

317 голосов
/ 13 октября 2012
SELECT column FROM table
ORDER BY RAND()
LIMIT 10

Не эффективное решение, но работает

18 голосов
/ 13 января 2016

Очень простой и однострочный запрос.

SELECT * FROM Table_Name ORDER BY RAND() LIMIT 0,10;
17 голосов
/ 02 августа 2011

Я получаю быстрых запросов (около 0,5 секунд) с медленным процессором , выбирая 10 случайных строк в регистрах 400 КБ базы данных MySQL без кэширования размером 2 ГБ.Смотрите здесь мой код: Быстрый выбор случайных строк в MySQL

<?php
$time= microtime_float();

$sql='SELECT COUNT(*) FROM pages';
$rquery= BD_Ejecutar($sql);
list($num_records)=mysql_fetch_row($rquery);
mysql_free_result($rquery);

$sql="SELECT id FROM pages WHERE RAND()*$num_records<20
   ORDER BY RAND() LIMIT 0,10";
$rquery= BD_Ejecutar($sql);
while(list($id)=mysql_fetch_row($rquery)){
    if($id_in) $id_in.=",$id";
    else $id_in="$id";
}
mysql_free_result($rquery);

$sql="SELECT id,url FROM pages WHERE id IN($id_in)";
$rquery= BD_Ejecutar($sql);
while(list($id,$url)=mysql_fetch_row($rquery)){
    logger("$id, $url",1);
}
mysql_free_result($rquery);

$time= microtime_float()-$time;

logger("num_records=$num_records",1);
logger("$id_in",1);
logger("Time elapsed: <b>$time segundos</b>",1);
?>
15 голосов
/ 11 января 2017

Простой запрос с превосходной производительностью (работает с пробелами):

SELECT * FROM tbl WHERE id IN 
    (SELECT id FROM (SELECT id FROM tbl ORDER BY RAND() LIMIT 10) t)

Используются два вложенных подзапроса, поскольку MySQL еще не поддерживает LIMIT в первом.

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

Для взвешенной версии: https://stackoverflow.com/a/41577458/893432

13 голосов
/ 26 июня 2015

из книги:

Выбор случайной строки с использованием смещения

Еще одна техника, которая позволяет избежать проблем, обнаруженных в предыдущем Альтернативой является подсчет строк в наборе данных и возвращение случайного число между 0 и количеством. Затем используйте это число в качестве смещения при запросе набора данных

<?php
$rand = "SELECT ROUND(RAND() * (SELECT COUNT(*) FROM Bugs))";
$offset = $pdo->query($rand)->fetch(PDO::FETCH_ASSOC);
$sql = "SELECT * FROM Bugs LIMIT 1 OFFSET :offset";
$stmt = $pdo->prepare($sql);
$stmt->execute( $offset );
$rand_bug = $stmt->fetch();

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

7 голосов
/ 20 июля 2013

Как выбрать случайные строки из таблицы:

Отсюда: Выбор случайных строк в MySQL

Быстрое улучшение по сравнению с "сканированием таблицы" - использование индекса для выбора случайных идентификаторов.

SELECT *
FROM random, (
        SELECT id AS sid
        FROM random
        ORDER BY RAND( )
        LIMIT 10
    ) tmp
WHERE random.id = tmp.sid;
6 голосов
/ 02 декабря 2010

Ну, если у вас нет пробелов в ваших ключах, и они все числовые, вы можете вычислить случайные числа и выбрать эти строки. но это, вероятно, не так.

Таким образом, одним из решений будет следующее:

SELECT * FROM table WHERE key >= FLOOR(RAND()*MAX(id)) LIMIT 1

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

однако это НЕ действительно случайно, потому что ваши ключи, скорее всего, не будут распределяться равномерно.

Это действительно большая проблема, и ее нелегко решить, выполнив все требования, MySQL rand () - лучшее, что вы можете получить, если вам действительно нужно 10 случайных строк.

Однако есть другое решение, которое быстро, но также имеет компромисс, когда дело доходит до случайности, но может подойти вам лучше. Прочтите об этом здесь: Как я могу оптимизировать функцию ORDER BY RAND () в MySQL?

Вопрос в том, насколько случайным вам это должно быть.

Можете ли вы объяснить немного больше, чтобы я мог дать вам хорошее решение.

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

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

3 голосов
/ 24 сентября 2014

Мне нужен был запрос для возврата большого количества случайных строк из довольно большой таблицы.Это то, что я придумал.Сначала получите максимальный идентификатор записи:

SELECT MAX(id) FROM table_name;

Затем подставьте это значение в:

SELECT * FROM table_name WHERE id > FLOOR(RAND() * max) LIMIT n;

Где max - максимальный идентификатор записи в таблице, а n - количество строк, которое вы хотитев вашем наборе результатов.Предполагается, что в идентификаторах записей нет пробелов, хотя я сомневаюсь, что это повлияет на результат, если они были (хотя я и не пробовал).Я также создал эту хранимую процедуру, чтобы быть более общей;передайте имя таблицы и количество возвращаемых строк.Я использую MySQL 5.5.38 в Windows 2008, 32 ГБ, двойной EHz50 с частотой 3 ГГц и в таблице с 17 361 264 строками, она достаточно стабильна при ~ 0,03 с / 11 с и возвращает 1 000 000 строк.(время из MySQL Workbench 6.1; вы также можете использовать CEIL вместо FLOOR во 2-м операторе выбора в зависимости от ваших предпочтений)

DELIMITER $$

USE [schema name] $$

DROP PROCEDURE IF EXISTS `random_rows` $$

CREATE PROCEDURE `random_rows`(IN tab_name VARCHAR(64), IN num_rows INT)
BEGIN

SET @t = CONCAT('SET @max=(SELECT MAX(id) FROM ',tab_name,')');
PREPARE stmt FROM @t;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

SET @t = CONCAT(
    'SELECT * FROM ',
    tab_name,
    ' WHERE id>FLOOR(RAND()*@max) LIMIT ',
    num_rows);

PREPARE stmt FROM @t;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END
$$

затем

CALL [schema name].random_rows([table name], n);
3 голосов
/ 28 августа 2012

Я использовал это http://jan.kneschke.de/projects/mysql/order-by-rand/, опубликованное Riedsio (я использовал случай хранимой процедуры, которая возвращает одно или несколько случайных значений):

   DROP TEMPORARY TABLE IF EXISTS rands;
      CREATE TEMPORARY TABLE rands ( rand_id INT );

    loop_me: LOOP
        IF cnt < 1 THEN
          LEAVE loop_me;
        END IF;

        INSERT INTO rands
           SELECT r1.id
             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;

        SET cnt = cnt - 1;
      END LOOP loop_me;

В статье он решает проблему пропусков в идентификаторах, вызывающих не очень случайные результаты путем ведения таблицы (с использованием триггеров и т. Д. См. Статью); Я решаю проблему, добавив в таблицу еще один столбец, заполненный непрерывными числами, начиная с 1 ( edit: этот столбец добавляется во временную таблицу, созданную подзапросом в время выполнения, не влияет на вашу постоянную таблицу):

   DROP TEMPORARY TABLE IF EXISTS rands;
      CREATE TEMPORARY TABLE rands ( rand_id INT );

    loop_me: LOOP
        IF cnt < 1 THEN
          LEAVE loop_me;
        END IF;

        SET @no_gaps_id := 0;

        INSERT INTO rands
           SELECT r1.id
             FROM (SELECT id, @no_gaps_id := @no_gaps_id + 1 AS no_gaps_id FROM random) AS r1 JOIN
                  (SELECT (RAND() *
                                (SELECT COUNT(*)
                                   FROM random)) AS id)
                   AS r2
            WHERE r1.no_gaps_id >= r2.id
            ORDER BY r1.no_gaps_id ASC
            LIMIT 1;

        SET cnt = cnt - 1;
      END LOOP loop_me;

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...