问题描述
我想查询明细数据和明细数据总数。一般来说,这需要两句sql。例如,一个是select col1,col2,col3 from tb limit 50 offset 0
,另一个是select count() from tb
。为了减少查询次数,我改用了“select col1,col3,count() over () totol_count from tb”。
但我发现后者(select col1,count(*) over () totol_count from tb limit 50 offset 0
)有时比前者慢得多。为什么?
两种方法的性能有什么区别?
这是解释分析。我只是把不同的放在上面。
-
select col1 from tb limit 20 offset 0 限制(cost=743828.93..747114.71 rows=200 width=287)(实际时间=2290.254..2390.969 rows=200 loops=1) -> Gather Motion 20:1 (slice3;segments: 20) (cost=743828.93..747114.71 rows=200 width=287) (实际时间=2290.248..2390.933 rows=200 loops=1) -> 限制(cost=743828.93..747110.71 rows=10 width=287)(实际时间=2285.350..2385.507 rows=200 loops=1)
-
从 tb 中选择 count(*) 合计(cost=6123212.33..6123212.34 rows=1 width=8)(实际时间=2339.615..2339.616 rows=1 loops=1) -> Gather Motion 20:1 (slice2;segments: 20) (cost=6123212.10..6123212.32 rows=1 width=8) (actual time=635.672..2339.433 rows=20 loops=1) -> 聚合(成本=6123212.10..6123212.11 行=1 宽度=8)(实际时间=633.879..633.879 行=1 循环=1)
select col,count(*) over() from tb limit 20 offset 0 限制(cost=743828.93..747113.21 rows=200 width=287)(实际时间=57787.942..57788.339 rows=200 loops=1) -> WindowAgg (cost=743828.93..307341631.82 rows=18670608 width=287) (实际时间=57787.936..57788.310 rows=200 loops=1) -> Gather Motion 20:1 (slice3;segments: 20) (cost=743828.93..307014896.18 rows=18670608 width=287) (实际时间=1514.368..32796.802 rows=1873513个循环)
解决方法
只有在看到您的执行计划后,才能确定哪个更适合您。
但是如果您运行 Analyze 足够多,您可以使用:
SELECT reltuples::bigint AS estimate
FROM pg_class
WHERE oid = 'tb'::regclass;
它非常快,但不会为您提供准确的计数,而是近似计数。准确性将取决于运行分析的频率。
select count(*) from tb
慢的原因与MVCC有关
PostgreSQL 中的实现。多个交易可以的事实
看到数据的不同状态意味着不能有
“COUNT(*)” 汇总整个数据的简单方法
桌子;在某种意义上,PostgreSQL 必须遍历所有行。这
通常会导致顺序扫描读取有关每个的信息
表中的行。
Slow counting in PostgreSQL