Postgres中的快速随机行select

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

SELECT myid FROM mytable ORDER BY RANDOM() LIMIT 1; 

它的工作原理,但它真的很慢…有没有另一种方式来进行查询,或直接的方式来select一个随机的行而不读取所有的表? 顺便说一下,“myid”是一个整数,但它可以是一个空的字段。

谢谢

您可能想要尝试使用OFFSET

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

Nmytable的行数。 您可能需要先执行SELECT COUNT(*)来计算N的值。

更新 (由安东尼Hatchkins)

你必须在这里使用floor

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

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

我试过这与子查询,它工作正常。 偏移,至less在Postgresql v8.4.4中工作正常。

 select * from mytable offset random() * (select count(*) from mytable) limit 1 ; 

PostgreSQL 9.5引入了一个新的方法来更快地select样本: TABLESAMPLE

语法是

 SELECT * FROM my_table TABLESAMPLE BERNOULLI(percentage); SELECT * FROM my_table TABLESAMPLE SYSTEM(percentage); 

如果您只需要select一行,这不是最佳解决scheme,因为您需要知道表格的COUNT以计算确切的百分比。

为了避免缓慢的COUNT和快速的TABLESAMPLE从1行到数十亿行的表格,你可以这样做:

  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; ... 

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

要决定是否要使用BERNULLI或SYSTEM,请参阅http://blog.2ndquadrant.com/tablesample-in-postgresql-9-5-2/

你需要使用floor

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

检查这个链接了一些不同的选项。 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次

并build议通过使用改进方法#2

5) ORDER BY id where id=random()*N LIMIT 1 ,如果结果为空,则进行后续的重新查询。

我想出了一个没有TABLESAMPLE快速解决scheme。 比OFFSET random()*N LIMIT 1快得多。 它甚至不需要表计数。

这个想法是用随机但可预测的数据创build一个expression式索引,例如md5(primary key)

这里是一个1M行的示例数据的testing:

 create table randtest (id serial primary key, data int not null); insert into randtest (data) select (random()*1000000)::int from generate_series(1,1000000); create index randtest_md5_id_idx on randtest (md5(id::text)); explain analyze select * from randtest where md5(id::text)>md5(random()::text) order by md5(id::text) limit 1; 

结果:

  Limit (cost=0.42..0.68 rows=1 width=8) (actual time=6.219..6.220 rows=1 loops=1) -> Index Scan using randtest_md5_id_idx on randtest (cost=0.42..84040.42 rows=333333 width=8) (actual time=6.217..6.217 rows=1 loops=1) Filter: (md5((id)::text) > md5((random())::text)) Rows Removed by Filter: 1831 Total runtime: 6.245 ms 

这个查询有时(约1 / Number_of_rows概率)返回0行,所以需要检查和重新运行。 概率也不完全相同 – 有些行比其他行更可能。

为了比较:

 explain analyze SELECT id FROM randtest OFFSET random()*1000000 LIMIT 1; 

结果差异很大,但可能相当糟糕:

  Limit (cost=1442.50..1442.51 rows=1 width=4) (actual time=179.183..179.184 rows=1 loops=1) -> Seq Scan on randtest (cost=0.00..14425.00 rows=1000000 width=4) (actual time=0.016..134.835 rows=915702 loops=1) Total runtime: 179.211 ms (3 rows)