Postgres SELECT DISTINCT Does Not Scale
Source Entity
Hacker News

A technical analysis reveals that Postgres's SELECT DISTINCT clause fails to scale efficiently because it performs a full table scan regardless of indexing. This performance bottleneck can significantly impact database-backed workloads, particularly in high-frequency queues.
The Hidden Performance Bottleneck in Postgres
PostgreSQL is frequently lauded as the gold standard for relational database management systems, often recommended as a universal solution for developers regardless of their specific workload requirements. However, the recent discourse surrounding its scalability has highlighted a critical nuance regarding the SELECT DISTINCT clause. While it is a fundamental SQL command for retrieving unique values, its underlying implementation in Postgres creates a significant performance ceiling that developers must navigate.
The Mechanics of SELECT DISTINCT
At the heart of the issue is how the Postgres engine processes unique value retrieval. Contrary to the intuitive expectation that indexing a column would allow the database to jump directly to unique entries, SELECT DISTINCT forces the engine to perform a full scan of every row that satisfies the query’s predicates. Even when an optimal index exists, the database engine ignores the potential for a direct lookup, resulting in a linear increase in execution time as the dataset grows.
Impact on High-Frequency Workloads
This behavior is particularly detrimental to systems that rely on high-frequency, low-latency interactions, such as Postgres-backed queueing systems. In these environments, where the speed of row retrieval is paramount, SELECT DISTINCT often emerges as the most expensive operation within a query plan. Because the engine cannot leverage indexes to prune the search space, the query becomes a bottleneck, degrading the overall throughput of the application.
The False Promise of Indexing
Many database administrators and developers operate under the assumption that adding a B-tree index will solve performance issues for any query involving a specific column. The reality for SELECT DISTINCT is starkly different: the index does not assist in identifying unique values in the way one might expect for a WHERE clause. This architectural limitation means that as tables scale into the millions or billions of rows, what started as an innocuous query can quickly evolve into a system-wide performance degradation.
Broader Architectural Implications
This discovery serves as a cautionary tale for those building large-scale distributed systems on top of Postgres. It emphasizes the need for a deeper understanding of how SQL features translate into physical execution plans. When developers treat database features as 'black boxes' without investigating their performance characteristics, they risk architecting systems that are fundamentally incapable of scaling under load.
Future Trends and Mitigation
Moving forward, developers should look toward alternatives for deduplication, such as maintaining materialized views, using triggers to update a separate lookup table, or employing specialized data structures for unique tracking. By acknowledging that SELECT DISTINCT is not a scalable tool for large datasets, the engineering community can move toward more robust patterns that bypass these inherent Postgres limitations, ensuring that applications remain performant as their data volumes expand.