MySQL:ORDER BY RAND()的替代方法

我已经阅读了关于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 

更新2016年

此解决scheme使用索引列的效果最佳。

这里是一个简单的示例,并标有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计数限制正在随机化的结果数量。 它需要一定比例的结果并对它们进行sorting,而不是整个表格。

注意子查询 :如果要做连接和额外的where子句条件,你需要把它们都放在子查询和子查询中。 要有一个准确的数量,并拉回正确的数据。

UNOPTIMIZED: 1200ms

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

PROS

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

缺点

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

这里有一个替代scheme,但它仍然基于使用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 

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

ORDER BY RAND()不是那么慢,当你用它来find只有索引列。 您可以像这样在一个查询中获取所有ID:

 SELECT id FROM testTable ORDER BY RAND(); 

得到一个随机id的序列,然后将结果join另一个SELECT或WHERE参数的查询中:

 SELECT t.* FROM testTable JOIN (SELECT id FROM `testTable` ORDER BY RAND()) AS z ON z.id= t.id WHERE 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倍,search了近400000条3000条随机行。

创build一个列或连接到一个随机数字(例如生成在PHP中),并按此列顺序select。

我今天遇到了这个问题,试图和JOIN一起使用“DISTINCT”,但是我得到了重复,因为RAND使每个JOIN行都不同。 我糊涂了一下,发现了一个解决scheme,就像这样:

 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 

我正在使用的解决scheme也张贴在下面的链接: 我怎样才能优化MySQL的ORDER BY RAND()函数?

我假设你的用户表将会比你的个人资料表大,如果不是,那么它是1比1的基数。

如果是这样的话,我会先在用户表上做一个随机select,然后join个人资料表。

首先做select:

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

然后从这个池中,通过计算的概率挑出随机的行。 如果你的表有M行,你想挑出N个随机行,随机select的概率应该是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是计算表行数的子查询。 但是,由于我们正在处理概率,所以返回的行数可能会less于$ limitCount。 所以我们应该乘以一个因子来增加随机池的大小。

即:

 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。

希望这可以帮助!

Order by rand()在大型表上非常缓慢,

我在php脚本中find了以下解决方法:

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

然后在php中做随机

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

然后

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

似乎是相当快….