3

我正在尝试从数据不变的表中获取随机行。我读过有人尝试 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 次,直到我收到正确的行,但在第二个解决方案中它会立即工作,但需要另一个表。

4

3 回答 3

4

你的陈述

但这仅在行是连续的时才有效:

并不完全正确:floor()andmax()示例确实适用于非顺序行,因为您会做类似的事情

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不是通过进行任何类型的表扫描得到的(一个示例)。

于 2011-06-19T11:31:49.143 回答
1

您应该阅读 Jan Kneschke 的以下博客文章:ORDER BY RAND()

他列出了一些可能的解决方案及其性能行为。

于 2011-06-19T11:20:53.277 回答
1

$cnt = 行数。这个值可以被缓存(如果你使用 InnoDB,这是非常推荐的)。

$rnd = mt_rand(0,$cnt);

询问:

SELECT * FROM `table` WHERE `where_cond`='some_value' LIMIT $rnd,1

当然,您可以使用任何 where 子句选择任何值,所有技巧都在LIMIT $rnd, 1部分中。
我喜欢这种方法,因为这里没有任何JOINs。
此外,即使没有 ID,此方法也可用于已排序和未排序的行。

于 2011-06-19T11:09:09.940 回答