postgresql Postgres中的快速随机行选择

o3imoua4  于 2022-12-18  发布在  PostgreSQL
关注(0)|答案(8)|浏览(122)

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

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

它可以工作,但速度非常慢...有没有其他方法来进行查询,或者直接选择一个随机行而不阅读整个表?顺便说一下,'myid'是一个整数,但它可以是一个空字段。

x4shl7ld

x4shl7ld1#

您可能希望尝试使用OFFSET,例如

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

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

更新(安东尼哈奇金斯)

必须在此处使用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行,因为隐式舍入到最接近的整数。

4nkexdtk

4nkexdtk2#

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

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

如果您只想选择一行,这不是最佳解决方案,因为您需要知道表的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;
 ...

这可能看起来不那么优雅,但可能比其他任何答案都要快。
要决定是否使用BERNULLI或SYSTEM,请阅读http://blog.2ndquadrant.com/tablesample-in-postgresql-9-5-2/上的差异

ezykj2lf

ezykj2lf3#

我用一个子查询试过了,效果很好。偏移,至少在Postgresql v8. 4. 4中效果很好。

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

5lhxktic4#

您需要使用floor

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

8ulbf1ek5#

查看此链接了解一些不同的选项。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 aggregate--巧妙的方法,可能很慢:random()需要生成N次
并建议通过使用以下方法改进方法#2
5)ORDER BY id where id=random()*N LIMIT 1,如果结果为空,则执行后续重新查询。

webghufk

webghufk6#

获取随机行最简单、最快的方法是使用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

eivnm1vs

eivnm1vs7#

我已经想出了一个不需要TABLESAMPLE的快速解决方案,比OFFSET random()*N LIMIT 1快得多,甚至不需要计算表数。
其思想是使用随机但可预测的数据创建表达式索引,例如md5(primary key)
下面是使用1 M行样本数据进行的测试:

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)
wgxvkvu9

wgxvkvu98#

我向每一行添加了一个随机生成的数字,并在我的编程语言中生成一个添加到每一行的随机数。调用时,我将一个随机数传递给查询(在本例中为0. 27)

SELECT * FROM
(
  (SELECT id, random FROM t where <condition> and random >= 0.27 ORDER BY random LIMIT 1)
  UNION ALL
  (SELECT id, random FROM t where <condition> and random < 0.27 ORDER BY random DESC LIMIT 1)
) as results
ORDER BY abs(0.27-random) LIMIT 1;

(查询取自here
如果在条件行和随机行(包含随机数)上有一个索引,那么在这个850万行的表上,我会在6毫秒内得到一个结果,这比使用order by random()之类的方法快了几个数量级。
为了提高随机性,您还可以为您命中的每个结果生成一个新的随机数。(如果不这样做,某些数字将比其他数字出现得更频繁。)
与TABLESAMPLE不同,它还支持条件。

相关问题