COUNT(DISTINCT col) in PostgreSQL silently disables parallel query execution because the aggregate has no combine function — workers can't merge partial distinct counts without shipping all values to one place. This forces a single-core serial sort that often spills to disk. The fix is to push deduplication into a GROUP BY subquery, which PostgreSQL can parallelize via partial hash aggregates. On a 10M-row table this rewrite yields ~3.4x speedup (1211ms vs 360ms) and eliminates disk spill. The same parallelism block affects any aggregate with an inner ORDER BY (string_agg, array_agg, percentile_cont). The per-group variant (GROUP BY country, COUNT(DISTINCT user_id)) has an analogous rewrite using stacked GROUP BYs, though the planner may not always choose the parallel path.

9m read timeFrom postgr.es
Post cover image
Table of contents
The schemaTwo counts, two different plansWhy the planner can't split itOne DISTINCT poisons the whole statementThe rewrite: push the DISTINCT into a GROUP BYORDER BY aggregates hit the same wallThe harder case: per-group distinct countsWhen to actually care
266 Impressions