MySQL:ORDER BY RAND的替代品()

Ton*_*ony 58 mysql sql 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 
Run Code Online (Sandbox Code Playgroud)

Neo*_*ptt 27

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
Run Code Online (Sandbox Code Playgroud)

关于限制ammount的注意事项:限制4和4 /计数(*).4s需要是相同的数字.改变你返回的数量不会对速度造成太大影响.极限4和极限1000的基准是相同的.限制10,000使其达到600毫秒

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

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

note子查询:如果要执行连接和额外的where子句条件,则需要将它们放在子查询和子查询中.准确计数并撤回正确的数据.

未优化:1200ms

SELECT 
    g.*
FROM
    table g
ORDER BY RAND()
LIMIT 4
Run Code Online (Sandbox Code Playgroud)

PROS

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

缺点

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

  • 非常好.我一定会用到这个. (2认同)
  • 如果您使用这些ID并将它们放入缓存层10秒钟,然后让应用程序从缓存层中的ID中随机选择,则拉出一系列随机ID可能会更有用. (2认同)

OMG*_*ies 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
Run Code Online (Sandbox Code Playgroud)

这稍微复杂一点,但更好地分配了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
Run Code Online (Sandbox Code Playgroud)

  • 如何将`RAND()`乘以常数值可以提供更好的分布? (6认同)
  • 我只是尝试在InnoDB表上选择10个随机记录,这些记录略高于50万条记录,而且我没有看到任何显着的性能提升,而不仅仅是使用rand(). (4认同)
  • 这些表单不提供`ORDER BY RAND()`的任何*优化*.我只是在一百万行表上运行测试,以比较性能.平均5次运行的结果(丢弃第一次运行),直接"ORDER BY RAND()"实际上快了11.0%.(平均2.70秒对3.04秒). (4认同)
  • @OMG小马:是的,但你建议:-)所以我的问题是:为什么`ORDER BY RAND()`比`ORDER BY RAND()*const`更糟? (2认同)
  • 仍然需要为每一行创建一个RAND()值,将整个数据复制到临时表并对其进行排序. (2认同)

Adl*_*ran 8

它不是最快的,但速度快于普通ORDER BY RAND()方式:

ORDER BY RAND()当你用它来查找索引列时,并不是那么慢.您可以在一个查询中获取所有ID,如下所示:

SELECT id
FROM testTable
ORDER BY RAND();
Run Code Online (Sandbox Code Playgroud)

获取一系列随机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; 
Run Code Online (Sandbox Code Playgroud)

在你的情况下,它将是:

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 
Run Code Online (Sandbox Code Playgroud)

这是非常生硬的方法,对于非常大的表来说可能不合适,但它仍然比普通表更快RAND().我的执行时间比搜索3000个随机行快了近40倍.