MySQL - 从大表中选择随机行
Posted
技术标签:
【中文标题】MySQL - 从大表中选择随机行【英文标题】:MySQL - selecting random row from large table 【发布时间】:2016-09-07 21:25:58 【问题描述】:如果这个话题已经完成到死,我深表歉意,但我正在努力从一个大型 mysql 表中选择一个随机行。这是一个名为photos
的表,它的主键是PhotoID
。目前它的 ID 范围从 ~1500(由于在测试中创建然后删除的行)到 ~12000,有一些差距,我预计它会变得更大。
虽然我一直在使用它相对较小:
SELECT PhotoID FROM photos
...放入一个php数组$All_IDs
,然后在PHP中:
$RandomID = $All_IDs[mt_rand(0,count($All_IDs)-1)]
然后:
SELECT /* other columns */ FROM photos WHERE PhotoID = $RandomID
这很好用,当我重复它时,我会得到很多随机照片。但是,我认为加载整个 PhotoID
列以选择一个随机 ID,然后再进行另一个查询以获取该记录的效率不会很高,特别是如果我要选择几个。同样,我宁愿不选择整个表(所有列)到一个数组中只是为了挑选一个。在其他一些 *** 答案的帮助下,我得出了以下结论:
SELECT MIN(PhotoID) INTO @MinID FROM photos;
SELECT MAX(PhotoID) INTO @MaxID FROM photos;
SELECT PhotoID,/* other columns */ FROM photos WHERE PhotoID >= (@MinID + RAND() * (@MaxID - @MinID)) ORDER BY PhotoID LIMIT 0,1
我认为这会起作用,但我发现重复此查询几次只会给我一小段 ID,在 1500 - 1700 范围内,而如上所述,ID 当前接近 12,000。我不明白这是为什么?
【问题讨论】:
【参考方案1】:我怀疑您看到的值范围很小,因为RAND()
(在WHERE
子句中)正在针对表中的每一 行进行评估。更有可能的是,该行上的 PhotoID 将大于右侧表达式返回的较低值。因此,查询返回的集合对较低的 PhotoID 值具有更高的权重。使用 ORDER BY,您将获得最低的价格。
要获得更随机的分布,您只需 一次 次评估 RAND()。另外,当我可以在单个语句中完成工作并且没有用户定义的变量时,我不希望执行多个查询(三个单独的 SELECT 语句)。
要实现看起来您正在尝试实现的算法,我会这样处理它:
SELECT t.photoid
, ...
FROM photos t
JOIN ( SELECT m.min_id + RAND() * (max_id - min_id) AS _rand
FROM ( SELECT MIN(p.photoid) AS min_id
, MAX(p.photoid) AS max_id
FROM photos p
) m
) r
ON r._rand <= t.photoid
ORDER BY t.photoid
LIMIT 1
在 MySQL 中,内联视图(MySQL 用语中的派生表)将在外部查询之前首先实现。由于m
返回单行,r
中的 RAND() 函数将只计算一次。然后表达式中的单个值将用于外部查询。
【讨论】:
那太理想了,谢谢...我避免使用ORDER BY RAND()
,因为我知道每行都会调用RAND()
,但我认为如果它是@987654328 的一部分,它只会被调用一次@ 子句。我也从没想过这样使用JOIN
。
注意:这种方法不一定是从集合中返回随机行的最佳方法。我试图解释原始查询所观察到的行为的原因,以及一个实现原始查询预期设计的示例。 (在这种情况下使用 JOIN 是可行的,因为内联视图 r
将返回单行。)如果出于某种原因您需要使用多个语句,就像原来的一样,请将 RAND() 操作移到单独的语句中,然后将 single 静态值传递给实际查询。这就是这个答案中的查询正在做什么。)【参考方案2】:
试试这个查询:
select * from photos order by rand() limit 1;
【讨论】:
请注意,MySQL 将为表中的 每一 行评估 RAND() 函数。然后结果集将需要“使用文件排序”操作来识别具有最低 RAND() 值的行。这种方法往往不适用于大型集合。以上是关于MySQL - 从大表中选择随机行的主要内容,如果未能解决你的问题,请参考以下文章