MySQL: альтернативы ORDER BY RAND () - PullRequest
       53

MySQL: альтернативы ORDER BY RAND ()

57 голосов
/ 01 декабря 2009

Я читал о нескольких альтернативах функции MySQL ORDER BY RAND(), но большинство альтернатив применимы только в тех случаях, когда требуется один случайный результат.

Кто-нибудь знает, как оптимизировать запрос, который возвращает несколько случайных результатов, например:

   SELECT u.id, 
          p.photo 
     FROM users u, profiles p 
    WHERE p.memberid = u.id 
      AND p.photo != '' 
      AND (u.ownership=1 OR u.stamp=1) 
 ORDER BY RAND() 
    LIMIT 18 

Ответы [ 7 ]

27 голосов
/ 15 марта 2016

ОБНОВЛЕНИЕ 2016

Это решение лучше всего работает с индексным столбцом .

Вот простой пример и оптимизированный стенд запросов, помеченный 100 000 строк.

ОПТИМИЗИРОВАННЫЙ: 300 мс

SELECT 
    g.*
FROM
    table g
        JOIN
    (SELECT 
        id
    FROM
        table
    WHERE
        RAND() < (SELECT 
                ((4 / COUNT(*)) * 10)
            FROM
                table)
    ORDER BY RAND()
    LIMIT 4) AS z ON z.id= g.id

примечание о сумме лимита : лимит 4 и 4 / количество (*). Четверки должны быть одинаковыми. Изменение того, сколько вы возвращаете, не сильно влияет на скорость. Контрольные точки на пределе 4 и 1000 одинаковы. Предел 10 000 взял его до 600 мс

примечание о соединении : рандомизация только идентификатора быстрее, чем рандомизация целой строки. Так как он должен скопировать всю строку в память, затем рандомизировать его. Объединение может быть любой таблицей, связанной с подзапросом It, чтобы предотвратить сканирование таблиц.

note where claus : Счетчик where ограничивает количество рандомизированных результатов. Он берет процент от результатов и сортирует их, а не всю таблицу.

примечание к подзапросу : при выполнении условий объединения и дополнительных выражений where необходимо поместить их как в подзапрос, так и в подзапрос. Чтобы иметь точный счет и вернуть правильные данные.

Неоптимизировано: 1200 мс

SELECT 
    g.*
FROM
    table g
ORDER BY RAND()
LIMIT 4

PROS

в 4 раза быстрее, чем order by rand(). Это решение может работать с любой таблицей с индексированным столбцом.

CONS

Это немного сложно со сложными запросами. Необходимо поддерживать 2 базы кода в подзапросах

20 голосов
/ 01 декабря 2009

Вот альтернатива, но она все еще основана на использовании RAND ():

  SELECT u.id, 
         p.photo,
         ROUND(RAND() * x.m_id) 'rand_ind'
    FROM users u, 
         profiles p,
         (SELECT MAX(t.id) 'm_id'
            FROM USERS t) x
   WHERE p.memberid = u.id 
     AND p.photo != '' 
     AND (u.ownership=1 OR u.stamp=1) 
ORDER BY rand_ind
   LIMIT 18

Это немного сложнее, но дает лучшее распределение значений random_ind:

  SELECT u.id, 
         p.photo,
         FLOOR(1 + RAND() * x.m_id) 'rand_ind'
    FROM users u, 
         profiles p,
         (SELECT MAX(t.id) - 1 'm_id'
            FROM USERS t) x
   WHERE p.memberid = u.id 
     AND p.photo != '' 
     AND (u.ownership=1 OR u.stamp=1) 
ORDER BY rand_ind
   LIMIT 18
7 голосов
/ 24 ноября 2016

Это не самый быстрый, но более быстрый, чем обычный ORDER BY RAND() способ:

ORDER BY RAND() не так медленно, когда вы используете его, чтобы найти только проиндексированный столбец. Вы можете взять все свои идентификаторы в одном запросе, как это:

SELECT id
FROM testTable
ORDER BY RAND();

для получения последовательности случайных идентификаторов и JOIN результат для другого запроса с другими параметрами SELECT или WHERE:

SELECT t.*
FROM testTable t
JOIN
    (SELECT id
    FROM `testTable`
    ORDER BY RAND()) AS z ON z.id= t.id   
WHERE t.isVisible = 1
LIMIT 100; 

в вашем случае это будет:

SELECT u.id, p.photo 
FROM users u, profiles p 
JOIN
    (SELECT id
    FROM users
    ORDER BY RAND()) AS z ON z.id = u.id   
WHERE p.memberid = u.id 
  AND p.photo != '' 
  AND (u.ownership=1 OR u.stamp=1) 
LIMIT 18 

Это очень тупой метод, и он может быть неправильным с очень большими таблицами, но все же он быстрее обычного RAND(). Я получил в 20 раз быстрее время выполнения поиска 3000 случайных строк почти в 400000.

1 голос
/ 03 ноября 2016

Order by rand() очень медленно на больших столах,

Я нашел следующий обходной путь в php-скрипте:

Select min(id) as min, max(id) as max from table;

Тогда делай случайным образом в php

$rand = rand($min, $max);

Тогда

'Select * from table where id>'.$rand.' limit 1';

Кажется, довольно быстро ....

1 голос
/ 15 февраля 2015

Создайте столбец или присоединитесь к выбору со случайными числами (сгенерированными, например, в php) и упорядочите по этому столбцу.

1 голос
/ 16 января 2013

Я столкнулся с этим сегодня и пытался использовать DISTINCT вместе с JOIN, но получал дубликаты, я полагаю, потому что RAND делал каждую строку JOINed отличной. Я немного запутался и нашел решение, которое работает так:

SELECT DISTINCT t.id, 
                t.photo 
       FROM (SELECT  u.id, 
                     p.photo,
                     RAND() as rand
                FROM users u, profiles p 
                 WHERE p.memberid = u.id 
                  AND p.photo != '' 
                  AND (u.ownership=1 OR u.stamp=1)
                ORDER BY rand) t
       LIMIT 18
0 голосов
/ 17 сентября 2014

Решение, которое я использую, также размещено в ссылке ниже: Как я могу оптимизировать функцию ORDER BY RAND () в MySQL?

Я предполагаю, что ваша таблица пользователей будет больше, чем таблица ваших профилей, если нет, то это будет от 1 до 1 кардинальности.

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

Сначала сделайте выбор:

SELECT *
FROM users
WHERE users.ownership = 1 OR users.stamp = 1

Затем из этого пула выберите случайные строки по рассчитанной вероятности. Если в вашей таблице M строк, и вы хотите выбрать N случайных строк, вероятность случайного выбора должна быть N / M. Следовательно:

SELECT *
FROM
(
    SELECT *
    FROM users
    WHERE users.ownership = 1 OR users.stamp = 1
) as U
WHERE 
    rand() <= $limitCount / (SELECT count(*) FROM users WHERE users.ownership = 1 OR users.stamp = 1)

Где N - $ limitCount, а M - подзапрос, который вычисляет количество строк таблицы. Тем не менее, поскольку мы работаем над вероятностью, возможно получить меньше строк, чем $ limitCount. Поэтому мы должны умножить N на коэффициент, чтобы увеличить размер случайного пула.

т.е:

SELECT*
FROM
(
    SELECT *
    FROM users
    WHERE users.ownership = 1 OR users.stamp = 1
) as U
WHERE 
    rand() <= $limitCount * $factor / (SELECT count(*) FROM users WHERE users.ownership = 1 OR users.stamp = 1)

Обычно я устанавливаю $ factor = 2. Вы можете установить коэффициент на более низкое значение, чтобы дополнительно уменьшить размер случайного пула (например, 1,5).

К этому моменту мы уже ограничили бы таблицу размера M примерно размером 2N. Отсюда мы можем сделать JOIN, а затем LIMIT.

SELECT * 
FROM
(
       SELECT *
        FROM
        (
            SELECT *
            FROM users
            WHERE users.ownership = 1 OR users.stamp = 1
        ) as U
        WHERE 
            rand() <= $limitCount * $factor / (SELECT count(*) FROM users WHERE users.ownership = 1 OR users.stamp = 1)
) as randUser
JOIN profiles
ON randUser.id = profiles.memberid AND profiles.photo != ''
LIMIT $limitCount

В большой таблице этот запрос будет превосходить обычный запрос ORDER по запросу RAND ().

Надеюсь, это поможет!

...