$ lexprog.com

// notes from an old coder -- php, databases, and the occasional rant

[September 19, 2024] PostgreSQL

PostgreSQL Materialized Views: Refresh Strategies

PostgreSQL Materialized Views: Refresh Strategies

────────────────────────────────────────────────────────

PostgreSQL Materialized Views: Refresh Strategies

Tip: Create Materialized View

CREATE MATERIALIZED VIEW daily_stats AS
SELECT date, count(*) as orders, sum(total) as revenue
FROM orders GROUP BY date;

Pre-computed results for fast reads.

Gotcha: Views Don't Auto-Refresh

You must manually refresh:

REFRESH MATERIALIZED VIEW daily_stats;

Tip: Concurrent Refresh

REFRESH MATERIALIZED VIEW CONCURRENTLY daily_stats;

Doesn't lock the view during refresh. Needs a UNIQUE index.

Gotcha: CONCURRENTLY Needs Unique Index

CREATE UNIQUE INDEX ON daily_stats (date);

Without it, CONCURRENTLY fails.

Tip: Schedule Refresh via pg_cron

SELECT cron.schedule('refresh_stats', '0 2 * * *',
    'REFRESH MATERIALIZED VIEW CONCURRENTLY daily_stats');

Auto-refresh at 2 AM daily.

Gotcha: Stale Data Window

Between refreshes, the view shows old data. Design your refresh frequency based on freshness requirements.

Tip: EXPLAIN (ANALYZE, BUFFERS) Is Your Best Friend

For query debugging, always use EXPLAIN (ANALYZE, BUFFERS) instead of plain EXPLAIN. The BUFFERS option shows hit/miss rates for every node, revealing whether your indexes are actually in memory.

Tip: Partial Indexes Are Underutilized

CREATE INDEX ON orders (status) WHERE status = 'pending' creates a tiny index that covers only the rows your query needs. It's faster to scan and cheaper to maintain than a full-column index.

Gotcha: NULL Sorting Is Non-Obvious

By default, NULLs sort AFTER non-null values in ascending order. ORDER BY col DESC puts NULLs FIRST. Use NULLS LAST or NULLS FIRST to be explicit.

Senior Insight

Materialized views are my go-to solution for reporting queries that take seconds to run. They store the query result on disk and refresh on demand. The key insight I learned the hard way: a REFRESH MATERIALIZED VIEW CONCURRENTLY requires a unique index on the view, but the refresh takes longer and doesn't block reads during refresh. For dashboards that update hourly, concurrent refresh is essential to avoid locking out users.

Source: pganalyze Blog (https://pganalyze.com/blog), PostgreSQL Docs (https://www.postgresql.org/docs/current/), Crunchy Data Blog (https://www.crunchydata.com/blog)

────────────────────────────────────────────────────────
<-- back to posts