COUNT(DISTINCT)如何在postgreSQL中工作

时间:2015-10-12 09:56:21

标签: 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 BY c2

只得到了这个:

GroupAggregate  (cost=145337.34..165315.50 rows=997816 width=37) (actual time=8684.038..12173.533 rows=999766 loops=1)
  ->  Sort  (cost=145337.34..147837.34 rows=1000000 width=37) (actual time=8683.918..10402.606 rows=1000000 loops=1)
        Sort Key: c2
        Sort Method: external merge  Disk: 48816kB
        ->  Seq Scan on foo  (cost=0.00..18334.00 rows=1000000 width=37) (actual time=0.102..168.323 rows=1000000 loops=1)

规划人员未提供有关如何执行DISTINCT操作的任何信息。所以,我试着让这个计划更加冗长:

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

GroupAggregate的相对成本几乎相同(仅在5 current_setting('seq_page_cost')中有所不同,实际执行时间也几乎相同。

问题: PostgreSQL服务器如何执行COUNT (DISTINCT)操作。它可以使用索引(如果有的话)来提高它的性能吗?从我所看到的,它接近我提供的第二个查询,但不完全是......

2 个答案:

答案 0 :(得分:1)

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

有两种情况:

  • 要么有覆盖索引(c2,c1),那么就不必访问该表;然后阅读索引就足够了。
  • 或者你没有,那么必须阅读表本身,通过索引使其变得更复杂是没有意义的。

覆盖索引具有已经分类的优点。因此,这是两者中较快的选择。

所以这一切都与COUNT(DISTINCT)无关,你不需要知道DBMS如何在内部处理COUNT(DISTINCT)。

答案 1 :(得分:1)

Postgres可以用count(distinct)做一个糟糕的工作。通常情况下,这会更好:

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将选择更好的算法,因为外部查询中没有count(distinct)

您可能对this关于该主题的博文感兴趣。这恰好是Oracle和SQL Server做得更好的一个领域。 (而且,我觉得Hive有同样的问题,但原因有些不同,具有讽刺意味。)