MySQL:ORDER BY RAND的替代品()

时间:2009-12-01 00:27:25

标签: sql mysql random sql-order-by

我已经阅读了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 个答案:

答案 0 :(得分:27)

2016年更新

此解决方案最适合使用索引列

这是一个标有100,000行的简单示例和优化查询工作台。

优化: 300ms

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 /计数(*)。 4s需要是相同的数字。改变你返回的数量不会对速度造成太大影响。极限4和极限1000的基准是相同的。限制10,000使其达到600毫秒

关于加入的注意事项:只是随机化id比整行随机化要快。因为它必须将整行复制到内存中然后随机化它。连接可以是链接到子查询的任何表,以防止表格扫描。

注意where子句:where count限制了随机化结果的数量。它需要一定比例的结果并对它们进行排序而不是整个表格。

note sub query :if if join和where where子句条件需要将它们放在子查询和子查询中。要准确计数并撤回正确的数据。

未加优化: 1200ms

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

PROS

order by rand()快4倍。此解决方案可以与任何带有索引列的表一起使用。

<强> CONS

复杂查询有点复杂。需要在子查询中维护2个代码库

答案 1 :(得分:20)

这是另一种选择,但它仍然基于使用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

答案 2 :(得分:7)

它不是最快的,但比普通ORDER BY RAND()方式更快:

当您使用它来查找仅索引列时,

ORDER BY RAND()并不是那么慢。您可以在一个查询中获取所有ID:

SELECT id
FROM testTable
ORDER BY RAND();

获取一系列随机id,并将结果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()更快。我的搜索3000个随机行的执行时间快了近40倍。

答案 3 :(得分:1)

我今天遇到了这个问题并试图使用'DISTINCT'以及JOINs,但我认为这是重复的,因为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

答案 4 :(得分:1)

创建一个列或连接到具有随机数的选择(例如在php中生成)并按此列排序。

答案 5 :(得分:1)

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

似乎很快......

答案 6 :(得分:0)

我正在使用的解决方案也发布在以下链接中: How can i optimize MySQL's ORDER BY RAND() function?

我假设你的用户表比你的个人资料表更大,如果没有,那么它就是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

在大型表上,此查询将优于RAND()查询的正常ORDER。

希望这有帮助!