最简单方法是用order by random() limit n,适合几千行以内小表;大表需改用tablesample避免全表扫描,且random()不支持索引、不可复用,调试时可用setseed()固定种子。

用 ORDER BY RANDOM() 抽取小表的随机行最简单
对几千行以内的表,ORDER BY RANDOM() 是最直接的办法。PostgreSQL 会为每行计算一个随机数,再按该值排序,配合 LIMIT 就能拿到随机样本。
常见错误是忘了加 LIMIT,导致全表排序——哪怕只要 1 行,也会扫描并排序全部数据,性能断崖式下跌。
-
SELECT * FROM users ORDER BY RANDOM() LIMIT 10;—— 安全,适合小表 - 表超过 10 万行时,响应明显变慢,不建议直接用
- 该写法无法利用任何索引,
RANDOM()是 volatile 函数,优化器完全跳过索引路径
大表抽样必须用 TABLESAMPLE 避免全表扫描
PostgreSQL 9.5+ 支持 TABLESAMPLE,底层基于块级采样(不是行级),不触发全表排序,性能几乎恒定,和表大小无关。
但要注意:它返回的是「近似」行数,且样本分布依赖物理存储顺序,不是统计学意义上的均匀随机。
-
SELECT * FROM logs TABLESAMPLE SYSTEM (1);—— 抽约 1% 的数据页(不是 1% 的行) -
SYSTEM按数据页采样,快但可能倾斜;BERNOULLI按行独立判断,更均匀但稍慢 -
TABLESAMPLE BERNOULLI (0.5)表示每行有 50% 概率被选中,实际行数浮动较大 - 不支持带
WHERE条件的子查询直接采样,需先WITH或物化中间结果
RANDOM() 和 SETSEED() 联用可复现结果
调试或测试时,需要每次运行都得到相同随机样本。这时必须在查询前调用 SETSEED() 固定随机种子。
注意:种子值范围是 -1 到 1,超出会被截断;且 SETSEED() 只影响当前 session,不同连接互不干扰。
-
SELECT SETSEED(0.123); SELECT * FROM products ORDER BY RANDOM() LIMIT 5;—— 同一 session 再次执行结果一致 - 不能在函数或视图里隐式调用
SETSEED(),否则行为不可控 -
RANDOM()本身无参数,不要写成RANDOM(0.123)—— 这会报错function random(double precision) does not exist
真正要统计推断?别信 RANDOM(),用外部工具或扩展
如果任务是「从千万级用户中抽取具有统计代表性的 1% 样本」,ORDER BY RANDOM() 或 TABLESAMPLE 都不够——前者偏差大,后者不保证行级均匀性,且无法分层、加权。
这时候得跳出 SQL 思维:导出主键 ID 到 Python,用 numpy.random.choice(..., replace=False) 生成确定性 ID 列表,再回查;或者启用 pg_sample 扩展做系统化抽样。
最容易被忽略的一点:业务上所谓“随机”,往往隐含了「去重」「按时间/地域均衡」「排除测试账号」等条件——这些必须在抽样前过滤,而不是指望 RANDOM() 自动处理。











