我在Postgres中有一个包含数百万行的表。我在互联网上查了一下,找到以下内容:
SELECT myid FROM mytable ORDER BY RANDOM() LIMIT 1;
它可以工作,但速度非常慢...是否有另一种方法来进行查询,或者直接选择一个随机行而不必读取整个表格?顺便说一下,'myid'是一个整数,但可能是一个空字段。
我在Postgres中有一个包含数百万行的表。我在互联网上查了一下,找到以下内容:
SELECT myid FROM mytable ORDER BY RANDOM() LIMIT 1;
它可以工作,但速度非常慢...是否有另一种方法来进行查询,或者直接选择一个随机行而不必读取整个表格?顺便说一下,'myid'是一个整数,但可能是一个空字段。
您可能想尝试使用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;
考虑一个有2行的表格;random()*N
生成0 <= x < 2
,例如SELECT myid FROM mytable OFFSET 1.7 LIMIT 1;
返回0行,因为隐式四舍五入取整。EXPLAIN SELECT ...
并且对于查询不同的N值得到相同的成本,那么我猜最好选择最大的N值。 - JuanPostgreSQL 9.5 引入了一种更快的样本选择方法:TABLESAMPLE。
语法如下:
SELECT * FROM my_table TABLESAMPLE BERNOULLI(percentage);
SELECT * FROM my_table TABLESAMPLE SYSTEM(percentage);
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;
...
SELECT * FROM my_table TABLESAMPLE SYSTEM(SELECT 1/COUNT(*) FROM my_table) LIMIT 1;
? - machineghostSELECT reltuples FROM pg_class WHERE relname = 'my_table'
进行计数估算。 - Hynek -Pichi- Vychodil我尝试使用子查询,效果很好。在Postgresql v8.4.4中,偏移量(offset)运作良好。
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;
由于隐式四舍五入到最近的整数,返回0行。 - Antony Hatchkinsorder by random()
更快,大约为 3*O(N) < O(NlogN)
- 由于索引,实际数据可能会略有不同。 - Antony Hatchkinsfloor()
?它有什么优势呢? - ADTC点击以下链接以查看不同选项。 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) 自定义随机聚合 -- 狡猾的方法,可能很慢:需要生成 N 次 random()
并建议通过使用以下方式来改进方法 #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;
应该足够快。 - daamien我想到了一种非常快速的解决方案,不需要使用TABLESAMPLE
,比OFFSET random()*N LIMIT 1
快得多。它甚至不需要表格计数。
这个想法是创建一个带有随机但可预测数据的表达式索引,例如md5(primary key)
。
这里是一个包含100万行样本数据的测试:
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
这个查询有时会返回0行数据(概率约为1/总行数),因此需要进行检查和重新运行。此外,各行的概率并不完全相同 - 有些行比其他行更可能出现。
作为对比:
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)
我在每一行中添加了一个随机生成的数字,并在我的编程语言中生成一个随机数,将其添加到每一行中。 在调用时,我将一个随机数传递给查询(在本例中为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;
(查询来自这里)
如果您在条件和随机行(包含随机数)上有一个索引,我可以在我的850万行表格上在6毫秒内获得结果。这比使用类似于order by random()的任何东西要快几个数量级。
为了改善随机性,您还可以为每个命中的结果生成一个新的随机数。(如果没有这个,某些数字会比其他数字更频繁地出现。)
与TABLESAMPLE不同,这也支持条件。