【发布时间】:2010-09-19 22:37:23
【问题描述】:
如何在 SQL 中获取有效的简单随机样本?有问题的数据库正在运行 MySQL;我的表至少有 200,000 行,我想要一个大约 10,000 的简单随机样本。
“显而易见”的答案是:
SELECT * FROM table ORDER BY RAND() LIMIT 10000
对于大表来说,这太慢了:它为每一行调用RAND()(已经将其置于 O(n) 处),并对它们进行排序,使其充其量为 O(n lg n)。有没有比 O(n) 更快的方法?
注意:正如 Andrew Mao 在 cmets 中指出的,如果你在 SQL Server 上使用这种方法,你应该使用 T-SQL 函数 NEWID(),因为 RAND() @987654321 @。
编辑:5 年后
我在使用更大的表时再次遇到了这个问题,最终使用了@ignorant 解决方案的一个版本,并进行了两个调整:
- 将行采样到我想要的样本大小的 2-5 倍,便宜
ORDER BY RAND() - 在每次插入/更新时将
RAND()的结果保存到索引列。 (如果您的数据集不是很频繁更新,您可能需要找到另一种方法来保持此列的新鲜度。)
为了对包含 1000 个项目的表进行抽样,我计算行数并将结果抽样到平均 10,000 行,并使用 frozen_rand 列:
SELECT COUNT(*) FROM table; -- Use this to determine rand_low and rand_high
SELECT *
FROM table
WHERE frozen_rand BETWEEN %(rand_low)s AND %(rand_high)s
ORDER BY RAND() LIMIT 1000
(我的实际实现涉及更多工作,以确保我不会欠采样,并手动包装 rand_high,但基本思想是“将 N 随机减少到几千。”)
虽然这会做出一些牺牲,但它允许我使用索引扫描对数据库进行采样,直到它再次小到足以ORDER BY RAND()。
【问题讨论】:
-
这甚至在 SQL Server 中都行不通,因为
RAND()每次后续调用都返回相同的值。 -
好点——我要补充一点,SQL Server 用户应该使用 ORDER BY NEWID() 代替。
-
它仍然非常低效,因为它必须对所有数据进行排序。一定百分比的随机抽样技术更好,但我什至在这里阅读了一堆帖子后,我还没有找到一个足够随机的可接受的解决方案。
-
如果您阅读了这个问题,我特意问的是因为 ORDER BY RAND() 是 O(n lg n)。
-
如果您不太沉迷于 RAND() 的统计随机性,muposat 的回答非常棒。
标签: mysql sql postgresql random