COUNT(DISTINCT)在postgreSQL中是如何工作的

COUNT(DISTINCT)在postgreSQL中是如何工作的,sql,postgresql,group-by,distinct,Sql,Postgresql,Group By,Distinct,我有下表: CREATE TABLE foo( c1 integer, c2 text ) 填充为 INSERT INTO foo SELECT id, md5(random()::text)) FROM generate_series(1, 1000000) id 我试图探究这个问题: EXPLAIN ANALYZE SELECT c2, COUNT(DISTINCT CASE WHEN c1 < 150 THEN c1 ELSE null END) FROM foo GROUP B

我有下表:

CREATE TABLE foo( c1 integer, c2 text )
填充为

INSERT INTO foo
SELECT id, md5(random()::text))
FROM generate_series(1, 1000000) id
我试图探究这个问题:

EXPLAIN ANALYZE
SELECT c2, COUNT(DISTINCT CASE WHEN c1 < 150 THEN c1 ELSE null END)
FROM foo
GROUP BY c2
GroupAggregate  (cost=145337.34..170339.34 rows=200 width=37) (actual time=8583.758..10793.980 rows=999766 loops=1)
  ->  Group  (cost=145337.34..155337.34 rows=1000000 width=37) (actual time=8583.747..10362.651 rows=1000000 loops=1)
        ->  Sort  (cost=145337.34..147837.34 rows=1000000 width=37) (actual time=8583.738..10112.619 rows=1000000 loops=1)
              Sort Key: foo.c2, foo.c1
              Sort Method: external merge  Disk: 48816kB
              ->  Seq Scan on foo  (cost=0.00..18334.00 rows=1000000 width=37) (actual time=0.084..168.937 rows=1000000 loops=1)
规划者没有提供关于如何执行独特操作的任何信息。所以,我试着让计划更详细:

EXPLAIN ANALYZE
SELECT st.c2, COUNT(c1)
FROM (
    SELECT c2, CASE WHEN c1 < 150 THEN c1 ELSE null END c1
    FROM foo
    GROUP BY c2, c1
) st
GROUP BY
st.c2
GroupAggregate的相对成本几乎相同,只是在当前的5个_设置“seq_page_cost”中有所不同,实际执行时间也几乎相同


问题:PostgreSQL server如何执行计数不同操作。它是否可以使用索引(如果有的话)来提高它的性能?从我所看到的情况来看,这与我提供的第二个查询非常接近,但并不完全是……

您的查询访问c2到group by,并访问c1来计算150以下的不同值。没有WHERE子句,所以您阅读了整个表

有两种情况:

要么你有一个覆盖索引c2,c1,那么这个表就不需要被访问;然后读取索引就足够了。 否则,就必须读取表本身,通过索引使其变得更复杂是没有意义的。 覆盖索引的优点是已经排序。因此,这是两种方法中速度更快的一种


因此,所有这些都与COUNTDISTINCT无关,您不需要知道DBMS如何在内部处理COUNTDISTINCT。

Postgres在COUNTDISTINCT上的工作很糟糕。通常,这样做效果更好:

SELECT c2, COUNT(c1_new)
FROM (SELECT c2, (CASE WHEN c1 < 150 THEN c1 END) as c1_new
      FROM foo
      GROUP BY c2, (CASE WHEN c1 < 150 THEN c1 END)
     ) f
GROUP BY c2;
Postgres会选择更好的算法,因为外部查询中没有countdistinct


你可能对这个主题的博客文章感兴趣。这恰好是Oracle和SQL Server做得更好的一个领域。而且,我觉得很讽刺的是,Hive也有同样的问题,但原因有所不同。

虽然有用,但我认为这并不能回答问题。您最好的参考资料是源代码,尽管我承认要找到一个简洁的解释来解释如何处理任何聚合函数不同的表达式并不容易。src/backend/executor/nodeAgg.c中的process\u ordered\u aggregate\u single可能是一个有用的起点。顺便说一句,正如我读到的关于在聚合中使用DISTINCT的内容一样,可以使用索引。这是真的吗?因此,如果我们在列上有相对较少的工作和索引,COUNTDISTINCT col__name可能非常有用_name@St.Antario . . . Postgres最近才引入了索引扫描。我不确定它们是否适用于countdistinct。我不是指仅索引扫描,我的意思是避免使用seq扫描和对要显式分离的值进行排序,而是使用索引提供的排序。
SELECT c2, COUNT(c1_new)
FROM (SELECT c2, (CASE WHEN c1 < 150 THEN c1 END) as c1_new
      FROM foo
      GROUP BY c2, (CASE WHEN c1 < 150 THEN c1 END)
     ) f
GROUP BY c2;