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';
似乎是相当快….