Data and databases

5 min read · Published · Updated

Materialized Views: When Precomputing the Answer Is Better Than Recalculating It Every Time

A fast report is useful only when its data is understood. Precomputation, late corrections and what the product should do when refresh stops.

The sales report is opened at every management meeting, and each time the database joins purchases, refunds, categories and business units from scratch. A minute later, another manager opens the same summary and the work starts again. If the result barely changed between those reads, much of the computation serves repeated presentation rather than a new question.

A materialized view stores a query's result. The application reads prepared data instead of rebuilding the report on demand. That can be valuable, but somebody now has to maintain the result and make clear which information it represents.

A regular view stores a query; a materialized view also stores its result

A conventional view gives a complex query a usable name and interface. It does not by itself promise precomputed rows. A materialized view persists the result: instead of scanning every sale and refund for the month, the application might read totals prepared by day and business unit. Many implementations also allow indexes on that stored result.

This need not become a separate analytics platform. A modest view may live in the same database. Its refresh, however, uses that database's CPU, storage, memory and sometimes locks. Moving work from read time to refresh time does not make it free.

First inspect the source query and its indexes. If a report is rarely opened and refreshing is expensive, scheduled precomputation may increase total load. Compare the cost of all the reads it replaces with storage, refresh and maintenance, not just the response time of one dashboard.

The data shape determines which questions remain answerable

Suppose the summary stores only daily sales per business unit. It can quickly show how each branch performed. It cannot reconstruct a filter by salesperson when that dimension was discarded during aggregation. If every new question needs another grouping, the system may accumulate many similar views, each with its own refresh schedule and operating cost.

Even familiar metrics need care. The company's average purchase value is not generally the unweighted average of branch averages: branches have different numbers of purchases. Preserve totals and counts when later aggregation needs them, rather than keeping only a precomputed average.

Refunds, cancellations and changing reference data raise further questions. Which date owns a refund? Which product category should a historical sale use after the product is recategorised? Materialization preserves the chosen calculation; it does not decide the definition for the business. The report, source query and validation process need the same definition.

“Refresh every five minutes” is not a five-minute freshness guarantee

A schedule specifies when a job should start. It does not guarantee when it will complete. Refreshes may wait for capacity, run longer than expected or fail. The source used to build the view may itself receive updates late.

Distinguish the time of the last successful refresh, the calculation's duration and the point through which input data is actually represented. “Updated just now” can be misleading when a calculation has only just finished reading a source that was already behind.

A management meeting may be well served by a summary of the previous completed day. An immediate decision about allocating credit or selling the last available resource should not generally rely on an outdated aggregate as its only safeguard. Validate the consequential action against authoritative state even if the overview remains fast and asynchronous.

Refresh behaviour is implementation-specific

Do not promise incremental maintenance simply because a database object is called a materialized view. In PostgreSQL 18, the standard REFRESH MATERIALIZED VIEW runs its defining query again. CONCURRENTLY permits ordinary reads during refresh, but requires an already populated view and a suitable unique index covering all rows. It does not turn full computation into a free append of newly arrived records.

Other systems offer different arrangements. SQL Server's indexed views are maintained when the underlying tables change, adding work to those writes. The freshness decision is therefore linked to another question: when is the maintenance performed, and which workload pays for it?

A custom incremental pipeline must handle corrections as well as new records. A refund arriving after a day has closed will not repair yesterday's total if the pipeline only considers rows dated today. It needs rules for affected periods, duplicate handling and reconciliation against a complete calculation.

When refresh stops, the interface needs to notice

A fast report can look entirely healthy after several refresh failures. The user sees no latency, but the information behind their decision is stale. Monitor data age against an agreed limit and define what happens when that limit is exceeded: show a warning, disable a dependent action or stop serving the result.

Automatically sending all readers to the expensive source query can be dangerous too. If the view exists to reduce database load, that fallback restores the load precisely during a failure. An explicit temporary-unavailability message may be safer.

Test recovery using a separate copy: rebuild the history, compare control totals, include a late correction and only then switch readers. Check permissions on the derived result as well. An aggregate must not accidentally expose information across tenants merely because it was computed from their shared underlying tables.

The decision has a practical boundary. Frequent reads, a reasonably stable shape and a defined tolerance for delay can make precomputation worthwhile. For mostly unique requests or work requiring current information without lag, compare maintenance costs with other options, from query improvements to a separate analytics store. Speeding up one screen should not quietly create another reporting system that nobody owns.