我想在 PostgreSQL 中随机选择行,我试过这个:
select * from table where random() < 0.01;
但其他一些人建议这样做:
select * from table order by random() limit 1000;
我有一个非常大的表,有 5 亿行,我希望它快。
哪种方法更好?有什么区别?选择随机行的最佳方法是什么?
原文由 nanounanue 发布,翻译遵循 CC BY-SA 4.0 许可协议
我想在 PostgreSQL 中随机选择行,我试过这个:
select * from table where random() < 0.01;
但其他一些人建议这样做:
select * from table order by random() limit 1000;
我有一个非常大的表,有 5 亿行,我希望它快。
哪种方法更好?有什么区别?选择随机行的最佳方法是什么?
原文由 nanounanue 发布,翻译遵循 CC BY-SA 4.0 许可协议
我认为 postgreSQL 中最好的方法是:
SELECT * FROM tableName ORDER BY random() LIMIT 1
原文由 Saurabh Agrawal 发布,翻译遵循 CC BY-SA 4.0 许可协议
4 回答2.9k 阅读
1 回答2.4k 阅读✓ 已解决
3 回答2.3k 阅读
1 回答2.3k 阅读✓ 已解决
2 回答951 阅读
快捷方式
鉴于您的规格(以及评论中的其他信息),
下面的查询不需要大表的顺序扫描,只需要索引扫描。
首先,获取主查询的估计值:
唯一可能昂贵的部分是
count(*)
(对于大桌子)。鉴于上述规格,您不需要它。 替换完整计数的估计就 可以了,几乎免费:详细解释:
只要
ct
不 小于id_span
,查询将优于其他方法。在
id
空间中生成随机数。您有“很少的空白”,因此在要检索的行数中添加 10 %(足以轻松覆盖空白)。每个
id
都可以偶然被选中多次(尽管在 id 空间很大的情况下不太可能),所以对生成的数字进行分组(或使用DISTINCT
)。将
id
加入到大表中。有了索引,这应该非常快。最后修剪多余的
id
没有被骗子和缝隙吃掉的。每一行都有 完全平等的机会 被选中。精简版
您可以 简化 此查询。上述查询中的 CTE 仅用于教育目的:
使用 rCTE 进行优化
特别是如果您对差距和估计不太确定。
我们可以在基本查询中使用 _较小的盈余_。如果有太多间隙,我们在第一次迭代中找不到足够的行,则 rCTE 继续使用递归项进行迭代。我们仍然需要 ID 空间中相对 较少 的间隙,否则递归可能会在达到限制之前干涸 - 或者我们必须从一个足够大的缓冲区开始,这违背了优化性能的目的。
rCTE 中的
UNION
消除了重复项。一旦我们有足够的行,外部的
LIMIT
就会使 CTE 停止。此查询经过精心起草以使用可用索引,生成实际随机行并且在我们达到限制之前不会停止(除非递归运行枯竭)。如果你要重写它,这里有很多陷阱。
包装成函数
对于具有不同参数的 同一张表 重复使用:
称呼:
通用函数
我们可以使这个泛型适用于具有唯一整数列(通常是 PK)的 任何表:将表作为多态类型和(可选)PK 列的名称传递并使用
EXECUTE
:使用默认值调用(重要!):
或者更具体地说:
与静态版本的性能大致相同。
有关的:
这对 SQL 注入是安全的。看:
可能的替代方案
我您的要求允许 重复调用相同的集合(我们正在谈论重复调用)考虑
MATERIALIZED VIEW
。执行一次上述查询并将结果写入表。用户以闪电般的速度获得准随机选择。每隔一段时间或您选择的事件刷新您的随机选择。Postgres 9.5 引入
TABLESAMPLE SYSTEM (n)
其中
n
是一个百分比。 手册:大胆强调我的。它 _非常快_,但结果 _并不完全随机_。再看说明书:
返回的行数可以变化很大。对于我们的示例,要获得 大约 1000 行:
有关的:
或者 安装附加模块 tsm_system_rows 以准确获取请求的行数(如果有足够的)并允许使用更方便的语法:
有关详细信息,请参阅 埃文的答案。
但这仍然不是完全随机的。