Postgres中的快速随机行选择

Jua*_*uan 84 postgresql random-access

我在postgres中有一个包含数百万行的表.我在网上查了一下,发现了以下内容

SELECT myid FROM mytable ORDER BY RANDOM() LIMIT 1;
Run Code Online (Sandbox Code Playgroud)

它工作,但它真的很慢......是否有另一种方式来进行查询,或者直接选择随机行而不读取所有表格?顺便说一下'myid'是一个整数,但它可以是一个空字段.

谢谢

NPE*_*NPE 92

你可能想要试验OFFSET,如

SELECT myid FROM mytable OFFSET floor(random()*N) LIMIT 1;

N是行数mytable.你可能需要先做一个SELECT COUNT(*)来弄清楚它的价值N.

更新(由Antony Hatchkins提供)

你必须floor在这里使用:

SELECT myid FROM mytable OFFSET floor(random()*N) LIMIT 1;
Run Code Online (Sandbox Code Playgroud)

考虑一个2行的表; random()*N生成0 <= x < 2并例如SELECT myid FROM mytable OFFSET 1.7 LIMIT 1;返回0行,因为隐式舍入到最近的int.

  • 请参阅下面的答案中的错误修正 (3认同)
  • 这有一个错误.它将永远不会返回第一行并将生成错误1/COUNT(*),因为它将尝试返回最后一行之后的行. (2认同)

alf*_*onx 44

PostgreSQL 9.5引入了一种新方法,可以更快地选择样本:TABLESAMPLE

语法是

SELECT * FROM my_table TABLESAMPLE BERNOULLI(percentage);
SELECT * FROM my_table TABLESAMPLE SYSTEM(percentage);
Run Code Online (Sandbox Code Playgroud)

如果您只想选择一行,这不是最佳解决方案,因为您需要知道表的COUNT来计算确切的百分比.

为了避免缓慢的COUNT并对从1行到数十亿行的表使用快速TABLESAMPLE,您可以:

 SELECT * FROM my_table TABLESAMPLE SYSTEM(0.000001) LIMIT 1;
 -- if you got no result:
 SELECT * FROM my_table TABLESAMPLE SYSTEM(0.00001) LIMIT 1;
 -- if you got no result:
 SELECT * FROM my_table TABLESAMPLE SYSTEM(0.0001) LIMIT 1;
 -- if you got no result:
 SELECT * FROM my_table TABLESAMPLE SYSTEM(0.001) LIMIT 1;
 ...
Run Code Online (Sandbox Code Playgroud)

这可能看起来不那么优雅,但可能比任何其他答案都快.

要决定是否要使用BERNULLI oder SYSTEM,请在http://blog.2ndquadrant.com/tablesample-in-postgresql-9-5-2/上阅读有关差异的内容.

  • 这比任何其他答案都快得多,也更容易 - 这个应该在顶部. (2认同)
  • @machineghost“为了避免缓慢的计数...”...如果您的数据很小,您可以在合理的时间内进行计数,那就去做吧!:-) (2认同)
  • @machineghost 使用 `SELECT reltuples FROM pg_class WHERE relname = 'my_table'` 进行计数估计。 (2认同)

小智 34

我用子查询尝试了这个,它工作得很好.偏移,至少在Postgresql v8.4.4中工作正常.

select * from mytable offset random() * (select count(*) from mytable) limit 1 ;
Run Code Online (Sandbox Code Playgroud)


Ant*_*ins 29

你需要使用floor:

SELECT myid FROM mytable OFFSET floor(random()*N) LIMIT 1;
Run Code Online (Sandbox Code Playgroud)


Kub*_*aun 14

检查此链接以获取一些不同的选项. http://www.depesz.com/index.php/2007/09/16/my-thoughts-on-getting-random-row/

更新: (A.Hatchkins)

(非常)长文章的摘要如下.

作者列出了四种方法:

1)ORDER BY random() LIMIT 1; - 慢

2)ORDER BY id where id>=random()*N LIMIT 1- 如果存在间隙,则不均匀

3)随机列 - 需要不时更新

4)自定义随机聚合 - 狡猾的方法,可能很慢:random()需要生成N次

并建议使用改进方法#2

5)ORDER BY id where id=random()*N LIMIT 1 如果结果为空,则随后重新查询.


daa*_*ien 5

获取随机行的最简单和最快的方法是使用tsm_system_rows扩展名:

CREATE EXTENSION IF NOT EXISTS tsm_system_rows;
Run Code Online (Sandbox Code Playgroud)

然后您可以选择所需的确切行数:

SELECT myid  FROM mytable TABLESAMPLE SYSTEM_ROWS(1);
Run Code Online (Sandbox Code Playgroud)

这在 PostgreSQL 9.5 及更高版本中可用。

请参阅:https : //www.postgresql.org/docs/current/static/tsm-system-rows.html

  • 是的,这在文档中有明确的解释(上面的链接):“与内置的 SYSTEM 采样方法一样,SYSTEM_ROWS 执行块级采样,因此样本不是完全随机的,但可能会受到聚类效应的影响,特别是如果样本很小的话请求的行数。» 。如果您有一个小数据集,“ORDER BY random() LIMIT 1;”应该足够快。 (3认同)
  • 公平警告,这不是完全随机的。在较小的表上,我总是按顺序返回第一行。 (2认同)
  • 还值得注意的是,这仅适用于从表中选择随机行然后过滤,而不是运行查询然后随机选择一个或一些记录。 (2认同)