When most developers think about PostgreSQL optimization, they reach for the same familiar tools: add an index here, rewrite a query there, maybe run ANALYZE or VACUUM. While these conventional approaches are effective, they often miss creative solutions that can deliver dramatic performance improvements with minimal effort. Let’s explore three unconventional PostgreSQL optimization techniques that can transform your database performance.
1. Eliminate Full Table Scans with Constraint Exclusion
Imagine you have a users table with a check constraint that limits plan values to ‘free’ and ‘pro’. When an analyst accidentally queries for ‘Pro’ (with a capital P), PostgreSQL will scan the entire table even though the constraint guarantees no rows can match this condition.
The Problem: Honest Mistakes, Expensive Consequences
Consider this scenario:
CREATE TABLE users (
id INT PRIMARY KEY,
username TEXT NOT NULL,
plan TEXT NOT NULL,
CONSTRAINT plan_check CHECK (plan IN ('free', 'pro'))
);
When someone queries for a non-existent value:
SELECT * FROM users WHERE plan = 'Pro';
PostgreSQL performs a full table scan, examining every row despite the check constraint that makes this condition impossible to satisfy. On a table with millions of rows, this “honest mistake” can consume significant resources.
The Solution: Enable constraint_exclusion
PostgreSQL can eliminate impossible table scans, but this feature isn’t enabled by default for regular tables:
SET constraint_exclusion = 'on';
With this setting enabled, PostgreSQL analyzes check constraints during query planning and can completely skip table scans when constraints make conditions impossible to satisfy. The query plan changes from a costly sequential scan to a simple “Result” node that immediately returns zero rows.
When to Use This Technique
The constraint_exclusion parameter is set to “partition” by default because enabling it for all tables adds planning overhead. However, this technique shines in specific environments:
- BI and reporting systems where users craft ad-hoc queries
- Data warehouses with complex constraint hierarchies
- Development environments where query mistakes are common
The planning overhead is often worthwhile when weighed against the cost of accidental full table scans on large datasets.
2. Optimize for Lower Cardinality with Function-Based Indexes
Traditional indexing wisdom suggests creating indexes on the columns you query. But what if you’re over-indexing and wasting storage on precision you don’t need?
The Storage Cost of Precision
Consider a sales table where analysts frequently generate daily reports:
CREATE TABLE sale (
id INT PRIMARY KEY,
sold_at TIMESTAMPTZ NOT NULL,
charged INT NOT NULL
);
The obvious solution is to index the sold_at column:
CREATE INDEX sale_sold_at_ix ON sale(sold_at);
But this index stores full timestamp precision (microseconds) when analysts only need daily granularity. On a table with 10 million rows, this index might consume 214 MB of storage – nearly half the table size!
Rethinking the Problem
Instead of indexing the full timestamp, create a function-based index on just the date portion:
CREATE INDEX sale_sold_at_date_ix ON sale(
(date_trunc('day', sold_at AT TIME ZONE 'UTC'))::date
);
This approach offers several advantages:
- Smaller index size: 66 MB vs 214 MB (3x reduction)
- Better deduplication: Fewer distinct values allow PostgreSQL to optimize storage
- Faster queries: Smaller indexes require fewer I/O operations
- Timezone consistency: Explicit timezone handling prevents subtle bugs
The Discipline Problem
Function-based indexes are fragile – even slight expression changes can prevent index usage. The solution is PostgreSQL 18’s virtual generated columns:
ALTER TABLE sale ADD sold_at_date DATE
GENERATED ALWAYS AS (date_trunc('day', sold_at AT TIME ZONE 'UTC'));
Virtual generated columns look like regular columns but are computed on-the-fly. They guarantee that queries use the exact same expression as the index, eliminating the discipline problem while providing all the performance benefits.
Current Limitations
PostgreSQL 18 doesn’t yet support indexes directly on virtual generated columns, so you still need the function-based index approach. However, this limitation is expected to be resolved in PostgreSQL 19.
3. Enforce Uniqueness with Hash Indexes
Unique constraints typically use B-tree indexes, but for large values like URLs, this can be incredibly wasteful. Hash indexes offer a compelling alternative that most developers overlook.
The Problem: Oversized Unique Indexes
Consider a URL tracking table:
CREATE TABLE urls (
id INT PRIMARY KEY,
url TEXT NOT NULL,
data JSON
);
CREATE UNIQUE INDEX urls_url_unique_ix ON urls(url);
With modern web applications storing extensive state in URLs, these can become very large. A B-tree index on URL columns might consume 154 MB for a 160 MB table – nearly doubling storage requirements.
The Hash Index Solution
Hash indexes store hash values instead of the actual data, dramatically reducing storage requirements. However, PostgreSQL doesn’t support unique hash indexes directly. The workaround uses exclusion constraints:
ALTER TABLE urls ADD CONSTRAINT urls_url_unique_hash
EXCLUDE USING HASH (url WITH =);
This exclusion constraint prevents duplicate URLs using a hash index, effectively creating a “unique hash index.” The storage savings are dramatic: 32 MB vs 154 MB (5x reduction).
Performance Benefits
Beyond storage savings, hash indexes can outperform B-tree indexes for equality lookups on large values. The hash index approach often delivers faster query times while using significantly less storage.
Limitations to Consider
This technique has some important constraints:
- No foreign key support: Foreign keys require unique constraints, not exclusion constraints
- Limited INSERT … ON CONFLICT support: Must use
ON CONFLICT ON CONSTRAINTsyntax - No DO UPDATE support: Exclusion constraints don’t support
ON CONFLICT DO UPDATE
For these limitations, consider using MERGE statements instead:
MERGE INTO urls t
USING (VALUES (1000004, 'https://example.com')) AS s(id, url)
ON t.url = s.url
WHEN MATCHED THEN UPDATE SET id = s.id
WHEN NOT MATCHED THEN INSERT (id, url) VALUES (s.id, s.url);
Implementation Strategy
These techniques work best when applied strategically:
Start with Measurement
Before implementing any optimization, establish baseline metrics:
- Query execution times
- Index sizes
- Storage utilization
- Planning overhead
Consider Your Workload
Different techniques suit different scenarios:
- Constraint exclusion: Best for ad-hoc query environments
- Function-based indexes: Ideal for predictable aggregation patterns
- Hash indexes: Perfect for large, unique values without foreign key requirements
Monitor the Impact
Track both positive and negative effects:
- Improved query performance
- Reduced storage costs
- Increased planning time
- Maintenance complexity
The Broader Implications
These unconventional techniques highlight important principles:
Question Assumptions
Just because B-tree indexes are the default doesn’t mean they’re always optimal. Different data patterns benefit from different approaches.
Consider Total Cost of Ownership
Storage costs, maintenance overhead, and operational complexity all factor into optimization decisions. Sometimes a smaller, specialized solution outperforms a general-purpose one.
Embrace PostgreSQL’s Flexibility
PostgreSQL offers numerous advanced features beyond basic indexing. Exclusion constraints, generated columns, and specialized index types provide powerful optimization opportunities.
Looking Forward
As PostgreSQL continues evolving, these techniques will become more refined:
- PostgreSQL 19 may support indexes on virtual generated columns
- Hash index improvements continue with each release
- Query planner enhancements make constraint exclusion more efficient
The key is staying curious about PostgreSQL’s capabilities and thinking creatively about performance challenges. Sometimes the best optimization isn’t adding another B-tree index – it’s rethinking the problem entirely.
Conclusion
Conventional PostgreSQL optimization techniques are valuable, but they’re not the complete picture. By leveraging constraint exclusion, function-based indexes, and hash-based uniqueness constraints, you can achieve dramatic performance improvements that traditional approaches miss.
These techniques require careful consideration of trade-offs and limitations, but when applied appropriately, they can transform database performance while reducing resource consumption. The next time you face a PostgreSQL performance challenge, consider whether an unconventional approach might be exactly what you need.
Remember: the best optimization is often the one that solves the actual problem rather than applying a generic solution. PostgreSQL provides the tools – it’s up to us to use them creatively.
In case you have found a mistake in the text, please send a message to the author by selecting the mistake and pressing Ctrl-Enter.