Skip to content
EL
Contents

Views, materialized views and derived data

Should this dashboard use a materialized view?

DecisionCore3 min read15 min hands-on#sql#data-modeling#operations

The idea in 30 seconds

A materialized view trades freshness and refresh work for fast reads. Before approving one, pin down how stale the numbers may be, what each refresh costs now and at ten times the data, whether a refresh blocks readers, and what happens when a refresh fails. The answers decide between a materialized view, a better index, or a summary table updated as data arrives.

Helps to know first: Aggregation and grouping, Joins and relational querying

On this page

The situation

The sales dashboard shows revenue by month and category. It runs the report on every page load and takes almost two seconds. An AI assistant opened a pull request to fix it.

Review taskCache the revenue report

Pull request: “Dashboard is slow. Precompute the report in a materialized view and refresh it every minute from cron.”

-- migration
CREATE MATERIALIZED VIEW monthly_revenue_mv AS
SELECT date_trunc('month', o.ordered_at) AS month, p.category,
       sum(i.quantity * i.unit_price) AS revenue
FROM orders o
JOIN order_items i ON i.order_id = o.id
JOIN products p ON p.id = i.product_id
GROUP BY 1, 2;

And the schedule:

# crontab
* * * * *  psql -c "REFRESH MATERIALIZED VIEW monthly_revenue_mv"

Would you approve it? Write the review you would leave.

Your call

How to decide

Prefer a plain view or an index first, unless the query is expensive even when written well. A view costs nothing to keep correct; check the query plan before adding a cache.

Prefer a materialized view when the result is small, expensive to compute, read far more often than the data changes, and allowed to be minutes or hours old.

Prefer a summary table updated as data arrives when the numbers must be close to live, or a full recompute would take longer than the freshness you need.

Whatever you choose, make freshness visible. Record when each refresh succeeded, alert when it is late, and show users the time the data is from.

Check yourself

  1. What two changes would you make to the pull request’s refresh, at minimum?

    Show answer

    Add a unique index on (month, category) and refresh CONCURRENTLY, so readers are not blocked; and record and alert on refresh success, so a broken refresh is noticed.

  2. Why does refreshing every minute get worse as the business grows, even if the report has only 72 rows?

    Show answer

    A refresh reruns the full query over all orders, so its cost grows with the order tables. Once it takes longer than the interval, refreshes queue up, because only one can run at a time.

  3. When is a summary table a better choice than a materialized view?

    Show answer

    When the numbers must be nearly live, or a full recompute is too slow for the freshness required. A summary table updated as each order arrives does work proportional to the new data.

Finished reading?

Saved in this browser only.

Sources

Where each claim on this page comes from.

  1. 1.
    PostgreSQL documentation: REFRESH MATERIALIZED VIEW
    Primary· Parameters: CONCURRENTLY; Notes

    CONCURRENTLY needs a unique index and a populated view; only one refresh runs at a time against a materialized view.

  2. 2.
    PostgreSQL documentation: 13.3 Explicit Locking
    Primary· §13.3.1 Table-Level Locks

    A refresh without CONCURRENTLY takes ACCESS EXCLUSIVE, which blocks SELECT.

Go further

What to read next, and why it is worth the time.

  1. 1.
    PostgreSQL documentation: 39.3 Materialized Views
    Primary· §39.3

    A worked sales-summary example, including a unique index, and a clear note on when stale data is acceptable.

Applies to PostgreSQL 18. Draft: not yet fact-checked end to end.