我已经阅读了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
答案 0 :(得分:27)
此解决方案最适合使用索引列。
这是一个标有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。
希望这有帮助!