我在postgres中有一张表格,其中包含数百万行。我已经检查了互联网,发现以下内容
SELECT myid FROM mytable ORDER BY RANDOM() LIMIT 1;
它可以工作,但速度确实很慢...还有另一种方法可以进行该查询,还是直接选择随机行而不读取所有表?顺便说一句,“ myid”是一个整数,但它可以是一个空字段。
Answers:
您可能想尝试使用OFFSET
,例如
SELECT myid FROM mytable OFFSET floor(random()*N) LIMIT 1;
该N
是行数mytable
。您可能需要先执行a SELECT COUNT(*)
来确定的值N
。
更新(由安东尼·哈奇金斯撰写)
您必须floor
在这里使用:
SELECT myid FROM mytable OFFSET floor(random()*N) LIMIT 1;
考虑一个两行的表;random()*N
生成0 <= x < 2
并SELECT myid FROM mytable OFFSET 1.7 LIMIT 1;
返回0行,因为隐式舍入到最接近的int。
SELECT COUNT(*)
?是有意义的,我的意思是,不是使用表中的所有值,而是仅使用其中的一部分?
EXPLAIN SELECT ...
具有不同N的值给查询提供相同的开销,那么我想最好选择N的最大值
PostgreSQL 9.5引入了一种新方法,可以更快地选择样本:TABLESAMPLE
语法是
SELECT * FROM my_table TABLESAMPLE BERNOULLI(percentage);
SELECT * FROM my_table TABLESAMPLE SYSTEM(percentage);
如果只希望选择一行,这不是最佳解决方案,因为您需要知道表的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;
...
这看起来可能不太优雅,但可能比其他任何答案都快。
要确定是否要使用BERNULLI oder SYSTEM,请访问http://blog.2ndquadrant.com/tablesample-in-postgresql-9-5-2/了解有关差异的信息
SELECT * FROM my_table TABLESAMPLE SYSTEM(SELECT 1/COUNT(*) FROM my_table) LIMIT 1;
?
SELECT reltuples FROM pg_class WHERE relname = 'my_table'
用于计数估计。
我尝试了一个子查询,它工作正常。偏移量,至少在Postgresql v8.4.4中可以正常工作。
select * from mytable offset random() * (select count(*) from mytable) limit 1 ;
您需要使用floor
:
SELECT myid FROM mytable OFFSET floor(random()*N) LIMIT 1;
random()*N
生成0 <= x <2,并且例如SELECT myid FROM mytable OFFSET 1.7 LIMIT 1;
由于隐式舍入到最接近的int而返回0行。
order by random()
,大约是3*O(N) < O(NlogN)
-实际数字由于索引而略有不同。
WHERE myid NOT IN (1st-myid)
且WHERE myid NOT IN (1st-myid, 2nd-myid)
将不起作用,因为该决定是由OFFSET决定的。嗯...我想我可以在第二个和第三个SELECT中将N减少1和2。
floor()
吗?它提供什么优势?
请查看此链接以了解其他选项。 http://www.depesz.com/index.php/2007/09/16/my-thoughts-on-getting-random-row/
更新: (哈奇金斯)
(很长)文章的摘要如下。
作者列出了四种方法:
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
如果结果为空,则进行后续查询。
提取随机行最简单,最快的方法是使用tsm_system_rows
扩展名:
CREATE EXTENSION IF NOT EXISTS tsm_system_rows;
然后,您可以选择所需的确切行数:
SELECT myid FROM mytable TABLESAMPLE SYSTEM_ROWS(1);
这在PostgreSQL 9.5和更高版本中可用。
参见:https : //www.postgresql.org/docs/current/static/tsm-system-rows.html
ORDER BY random() LIMIT 1;
速度应该足够快。
我想出了一个非常快速的解决方案,而没有TABLESAMPLE
。比快得多OFFSET random()*N LIMIT 1
。它甚至不需要表计数。
这个想法是用例如随机但可预测的数据创建一个表达索引md5(primary key)
。
这是一个具有1M行样本数据的测试:
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)