Materialized Views
What are Materialized Views?
Materialized views pre-compute and store query results. Unlike regular views, they persist data on disk, trading storage for query speed.
Creating Materialized Views
-- PostgreSQL: materialized view for daily sales
CREATE MATERIALIZED VIEW daily_sales_summary AS
SELECT
d.date,
p.category,
COUNT(*) as sale_count,
SUM(s.amount) as total_sales
FROM fact_sales s
JOIN dim_date d ON s.date_id = d.date_id
JOIN dim_product p ON s.product_id = p.product_id
GROUP BY d.date, p.category;
-- Add index for faster queries
CREATE INDEX idx_daily_sales_date ON daily_sales_summary(date);
-- Query is much faster than the original
SELECT * FROM daily_sales_summary
WHERE date >= '2026-01-01';
Refresh Strategies
Complete Refresh
Rebuilds the entire view:
REFRESH MATERIALIZED VIEW daily_sales_summary;
Simple but locks the view during refresh. Best for small datasets or low-frequency refreshes.
Concurrent Refresh
Creates a new version and swaps atomically. No lock but requires a unique index:
CREATE UNIQUE INDEX idx_daily_sales_pk ON daily_sales_summary(date, category);
REFRESH MATERIALIZED VIEW CONCURRENTLY daily_sales_summary;
Incremental Refresh
Only processes changed data. Not natively supported in PostgreSQL but available in specialized databases.
Use Cases
| Use Case | Refresh Frequency | Benefit | |----------|------------------|---------| | Dashboards | Hourly | Sub-second queries | | Aggregations | Daily | Avoid full table scans | | Pre-joined data | On-demand | Eliminate expensive joins | | Reporting | Nightly | Consistent snapshot |
Performance Considerations
-- Compare query performance
EXPLAIN ANALYZE
SELECT date, SUM(total_sales)
FROM daily_sales_summary
WHERE date >= '2026-01-01'
GROUP BY date;
-- vs the base query (much slower)
EXPLAIN ANALYZE
SELECT d.date, SUM(s.amount)
FROM fact_sales s
JOIN dim_date d ON s.date_id = d.date_id
WHERE d.date >= '2026-01-01'
GROUP BY d.date;
Conclusion
Materialized views are essential for dashboard and reporting performance. Use concurrent refresh to avoid locks. Add indexes on frequently filtered columns. Choose refresh strategy based on freshness requirements. Monitor storage overhead for large views.