postgresql COUNT(DISTINCT ...)非常慢

fer*_*020 141 postgresql performance count distinct

我有一个非常简单的SQL查询:

SELECT COUNT(DISTINCT x) FROM table;
Run Code Online (Sandbox Code Playgroud)

我的桌子有大约150万行.这个查询运行得很慢; 它需要大约7.5s,相比之下

 SELECT COUNT(x) FROM table;
Run Code Online (Sandbox Code Playgroud)

大约需要435毫秒.有没有办法改变我的查询以提高性能?我已经尝试过分组并进行常规计数,以及在x上放置索引; 两者都有相同的7.5s执行时间.

Ank*_*kur 278

你可以用这个:

SELECT COUNT(*) FROM (SELECT DISTINCT column_name FROM table_name) AS temp;
Run Code Online (Sandbox Code Playgroud)

这比以下快得多:

COUNT(DISTINCT column_name)
Run Code Online (Sandbox Code Playgroud)

  • 神圣的蝙蝠侠!这加快了我的postgres数量,从190s到4.5不等! (32认同)
  • 我在[www.postgresql.org](http://www.postgresql.org)上发现了这个帖子,讨论了同样的事情:[链接](http://www.postgresql.org/message-id/CAONnt+72Mtg6kyAFDTHXFWyPPY -QRbAtuREak+64Lm1KN1c-wg@mail.gmail.com).其中一个回复(作者Jeff Janes)说COUNT(DISTINCT())对表进行排序以完成其工作而不是使用哈希. (19认同)
  • 我很想解释为什么这样做.棒极了! (18认同)
  • @Ankur我可以问你一个问题吗?由于`COUNT(DISTINCT())`执行排序,因此在`column_name`上有一个索引肯定是有帮助的,特别是在相对少量的`work_mem`(其中散列将产生相关的大量批次)中.既然如此,使用COUNT并不总是坏的(DISTINCT()_,不是吗? (5认同)
  • @musmahn `Count(column)` 只计算非空值。`count(*)` 计算行数。所以第一个/更长的,也将计算空行(一次)。更改为 `count(column_name)` 以使它们的行为相同。 (3认同)
  • @ankur 这对我来说没有多大用处..没有得到任何显着的改进。 (2认同)
  • 我查了一下。PostgreSQL 13 中没有区别。 (2认同)

wil*_*ser 11

-- My default settings (this is basically a single-session machine, so work_mem is pretty high)
SET effective_cache_size='2048MB';
SET work_mem='16MB';

\echo original
EXPLAIN ANALYZE
SELECT
        COUNT (distinct val) as aantal
FROM one
        ;

\echo group by+count(*)
EXPLAIN ANALYZE
SELECT
        distinct val
       -- , COUNT(*)
FROM one
GROUP BY val;

\echo with CTE
EXPLAIN ANALYZE
WITH agg AS (
    SELECT distinct val
    FROM one
    GROUP BY val
    )
SELECT COUNT (*) as aantal
FROM agg
        ;
Run Code Online (Sandbox Code Playgroud)

结果:

original                                                      QUERY PLAN                                                      
----------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=36448.06..36448.07 rows=1 width=4) (actual time=1766.472..1766.472 rows=1 loops=1)
   ->  Seq Scan on one  (cost=0.00..32698.45 rows=1499845 width=4) (actual time=31.371..185.914 rows=1499845 loops=1)
 Total runtime: 1766.642 ms
(3 rows)

group by+count(*)
                                                         QUERY PLAN                                                         
----------------------------------------------------------------------------------------------------------------------------
 HashAggregate  (cost=36464.31..36477.31 rows=1300 width=4) (actual time=412.470..412.598 rows=1300 loops=1)
   ->  HashAggregate  (cost=36448.06..36461.06 rows=1300 width=4) (actual time=412.066..412.203 rows=1300 loops=1)
         ->  Seq Scan on one  (cost=0.00..32698.45 rows=1499845 width=4) (actual time=26.134..166.846 rows=1499845 loops=1)
 Total runtime: 412.686 ms
(4 rows)

with CTE
                                                             QUERY PLAN                                                             
------------------------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=36506.56..36506.57 rows=1 width=0) (actual time=408.239..408.239 rows=1 loops=1)
   CTE agg
     ->  HashAggregate  (cost=36464.31..36477.31 rows=1300 width=4) (actual time=407.704..407.847 rows=1300 loops=1)
           ->  HashAggregate  (cost=36448.06..36461.06 rows=1300 width=4) (actual time=407.320..407.467 rows=1300 loops=1)
                 ->  Seq Scan on one  (cost=0.00..32698.45 rows=1499845 width=4) (actual time=24.321..165.256 rows=1499845 loops=1)
       ->  CTE Scan on agg  (cost=0.00..26.00 rows=1300 width=0) (actual time=407.707..408.154 rows=1300 loops=1)
     Total runtime: 408.300 ms
    (7 rows)
Run Code Online (Sandbox Code Playgroud)

与CTE相同的计划也可能由其他方法(窗口函数)生成

  • 你考虑过缓存的效果吗?如果随后进行三次"解释分析",第一个可能是缓慢从磁盘中取出东西,而后者可能是从内存中快速取出. (2认同)