select * from table order by rand() limit 40
如我们所知,这是按预期工作,但它需要太长的时间。
所以我要让它跑得更快。
我得到了一些解决方案,比如:
SELECT t1.* FROM talbe AS t1 JOIN
(SELECT ROUND(RAND() * ((SELECT MAX(id) FROM table)-(SELECT MIN(id) FROM table))+(SELECT MIN(id) FROM table)) AS id) AS t2
WHERE t1.id >= t2.id AND t1.type=1
ORDER BY t1.id
LIMIT 40
但它并不完全起作用。
有时它不能得到40行记录。
最佳答案
你知道你的第二个版本在做什么吗?
它最多可以选择40条id
大于或等于MIN(id)
和MAX(id)
之间的随机数的记录。从某种意义上说,这并不是选择40个随机记录:而是选择一个随机记录,然后选择39个id
。可以说是随机选择了一组记录。显然,如果随机选择的记录太接近最大值id
,接下来将不会有39个。
此外,每个集群的概率分布都是id
-因此该列中的任何不一致性(例如记录被删除的间隙)都会导致结果不一致。在极端情况下,假设一个人有80个id
1-40和1000001-1000040的记录。前四十个被选中的概率是后四十个的一百万倍(因为任何大于40的随机值,例如41、2753或999999将只返回1000000以后的记录)。
因此,如果考虑到所有这一点,你很乐意继续使用第二个版本,尽管它存在缺陷,而不是在id
和MIN(id)
之间随机选择MAX(id)
的起始记录,可以从最大值中选择id
和第四十MIN(id)
之间的id
。
SELECT t1.*
FROM `table` JOIN (
SELECT min.id + ROUND(RAND() * (max.id - min.id)) AS id
FROM (SELECT id FROM `table` ORDER BY id DESC LIMIT 39, 1) max,
(SELECT id FROM `table` ORDER BY id ASC LIMIT 0, 1) min
) AS pivot ON pivot.id <= table.id
WHERE table.type = 1
ORDER BY table.id
LIMIT 40
然而,如果该方法的上述缺陷是不可取的,那么@Quassnoi的文章“Selecting random rows”(其中@Kay Nelsonlinked above)基于将
RAND()
与被选择的下一个记录的概率进行比较,提供了更好的解决方案。我建议添加一个LIMIT
子句,以确保MySQL在找到所需数量的随机记录后停止表扫描:SELECT table.*, @lim := @lim - 1
FROM `table`, (SELECT @cnt := COUNT(*) + 1, @lim := 40 FROM `table`) init
WHERE RAND() < @lim / (@cnt := @cnt - 1)
LIMIT 40