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;
N
是mytable
的行数。 您可能需要先执行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)