我正试图从数据不变的表中抓取一个随机行。我读过人们尝试ORDER BY RAND(),这对大型数据集来说很糟糕,并且不能很好地扩展。
我也看到了解决方法是让SQL在最小/最大范围之间得到一个随机行,如下所示:FLOOR(MAX(needed_id)* RAND)但这只有在行顺序时才有效:1 ,2,3,4,5,6,7,8,9,10。
我需要提取的数据不是连续的,例如:1,2,3,4,10,11,12,13
所以我认为有两种解决方案:
第一个解决方案: 继续运行: FLOOR(MAX(needed_id)* RAND),直到我收到一排正确的类型(1/6机会)
第二个解决方案: 创建一个重复的表(因为我的数据永远不会改变),如下所示:
temp_id | needed_id | type
1 1 1
2 4 1
3 7 2
3 8 2
所以我可以使用这个方法取出一个随机的temp_id:FLOOR(MAX(temp_id)* RAND) - WHERE type = 1
你怎么看?我可能会运行第一个解决方案大约6次,直到我收到正确的行,但在第二个解决方案中它会立即工作但需要另一个表。答案 0 :(得分:4)
您的陈述
但这仅在行是连续的时才有效:
不完全正确:floor()
和max()
示例适用于非连续行,因为您可以执行类似
WHERE id >= FLOOR(RAND()*MAX(id)) LIMIT 1
所以你把最接近的ID带到你得到的随机命中。
对于直接在序列中存在较大差距的匹配后,这确实略有偏好,但这可能不会太糟糕,具体取决于您的数据集。
因此,根据您对这种轻微偏好所带来的问题,您的数据集是如何等等,这仍然是最好的解决方案。
因为有些人不清楚,功能的使用不是问题:
MAX
在索引字段上很快。您不需要计算所有行(在innoDB上慢),您只需要遍历您的BTREE索引,因此您将在log
时间内找到此值。这几乎是即时的
FLOOR
只是一个在线性时间内执行的数学函数。就像RAND
一样。请注意ORDER BY rand()
因rand
而不慢,但因为您需要订购完整的表格!这不是兰特的问题,而是秩序问题。
现在您的查询可以执行以下操作:
WHERE id >= 48 LIMIT 1
在索引字段中哪个是非常快的。请记住,您没有通过任何类型的表扫描获得48
(示例)。
答案 1 :(得分:1)
$ cnt =行数。可以缓存此值(如果您使用InnoDB,则建议使用此值)。
$rnd = mt_rand(0,$cnt);
查询:
SELECT * FROM `table` WHERE `where_cond`='some_value' LIMIT $rnd,1
当然,您可以使用任何where子句选择任何值,所有技巧都在 LIMIT $ rnd,1 部分。
我喜欢这种方法,因为这里没有任何JOIN
此外,即使没有ID,此方法也可用于已排序和未排序的行。
答案 2 :(得分:1)
您应该阅读Jan Kneschke的以下博文:ORDER BY RAND()
他列出了一些可能的解决方案及其表现行为。