Standard materialized views improve query performance by storing precomputed results, but they go stale as soon as the underlying data changes. Keeping them fresh requires a full refresh — re-reading all data in the view — which becomes expensive at scale.
Real-time materialized views solve this by moving work to write time. Each INSERT, UPDATE, or DELETE on a base table triggers an incremental update to the view, processing only the rows that changed. Reads return current data with no additional refresh cost.
Supported versions
| Cluster version | Minimum revision version | polar_ivm extension required |
|---|---|---|
| PolarDB for PostgreSQL (Compatible with Oracle) 2.0 | 2.0.14.8.11.0 | Yes — always required |
| PolarDB for PostgreSQL (Compatible with Oracle) 1.0, revision >= 2.0.11.9.27.0 (released October 2022) | 2.0.11.9.27.0 | No — use directly |
| PolarDB for PostgreSQL (Compatible with Oracle) 1.0, revision < 2.0.11.9.27.0 | Upgrade required | Yes — required after upgrading |
To check your current revision version, run SHOW polardb_version; or view it in the PolarDB console. To upgrade, see Version management.
To install the polar_ivm extension:
CREATE EXTENSION polar_ivm WITH SCHEMA pg_catalog;Key concepts
| Term | Definition |
|---|---|
| Base table | A standard table referenced in the materialized view definition |
| Delta | The set of rows added or removed from the base table since the last view update |
| Refresh | The operation that brings materialized view data in sync with the current base table state |
| Apply delta | The operation that inserts or deletes calculated incremental data into the real-time materialized view |
How it works
Creating a real-time materialized view:
PolarDB rewrites the view query and calculates the hidden columns needed to maintain incremental state.
A trigger is created on the base table to fire on each DML statement.
Where applicable, unique indexes are created on the view to accelerate delta application.
Refreshing a real-time materialized view:
A DML statement on the base table fires the trigger.
The trigger captures the incremental data (delta) from the base table.
PolarDB calculates the updated view rows based on the view definition and the delta.
The calculated delta is applied to the view, keeping it in sync with the base table.
Dropping a real-time materialized view:
The delta refresh trigger is removed from the base table.
The materialized view is dropped.
Limitations
Real-time materialized views support a subset of SQL features:
Base table type: The base table must be a standard table. Partitioned tables, inherited tables, and columnar tables are not supported.
Join types: Only
INNER JOINis supported.Functions: Only
immutablefunctions are supported.View definition complexity: Supported constructs include simple queries, projections,
DISTINCT, and aggregate functions listed below. The following are not supported: subqueries,[NOT] EXISTS,[NOT] IN,LIMIT,HAVING,DISTINCT ON, CTEs (WITH),ORDER BY, window functions,GROUPING SETS,CUBE,ROLLUP,UNION,INTERSECT, andEXCEPT.`GROUP BY` and projection: When a
GROUP BYclause is used, all grouped columns must appear in the projection (SELECT list).Aggregate functions: Only
MIN,MAX,SUM,AVG, andCOUNTare supported.
Create a real-time materialized view
CREATE MATERIALIZED VIEW table_name[ (column_name [, ...] ) ]
[ BUILD DEFERRED | IMMEDIATE ]
REFRESH FAST
ON COMMIT
AS query| Parameter | Description |
|---|---|
table_name | Name of the materialized view. Can be schema-qualified. |
column_name | Column names for the view. If omitted, column names are inherited from the result set of the query in the AS clause. |
BUILD DEFERRED | Creates the view without populating it. When you query the view, no error is reported. However, no data is returned until you run REFRESH MATERIALIZED VIEW once to populate it. After that first refresh, all subsequent base table changes are applied automatically. |
BUILD IMMEDIATE | Populates the view immediately after creation. This is the default. No manual refresh is needed. |
query | The SQL query whose results are stored in the view. Accepts SELECT, TABLE, or VALUES statements. Runs in a secure, restricted context. |
Refresh a real-time materialized view
Views created with BUILD IMMEDIATE refresh automatically — no action required.
For views created with BUILD DEFERRED, run the following statement once to populate the view:
REFRESH MATERIALIZED VIEW table_nameAfter the first refresh, PolarDB applies all subsequent base table changes to the view in real time.
Drop a real-time materialized view
DROP MATERIALIZED VIEW [ IF EXISTS ] table_name [, ...] [ CASCADE | RESTRICT ]| Parameter | Description |
|---|---|
IF EXISTS | Returns a notice instead of an error if the view does not exist. |
table_name | Name of the materialized view to drop. Can be schema-qualified. |
CASCADE | Automatically drops all objects that depend on this view (such as other materialized views or regular views), and all objects that depend on those objects. |
RESTRICT | Refuses to drop the view if any object depends on it. This is the default. |
Performance considerations
Real-time materialized views improve read performance by maintaining precomputed results. The cost is write overhead: every DML statement on a base table triggers an incremental view update. In write-heavy workloads, this overhead can be significant.
Use real-time materialized views when reads outnumber writes. Before deploying to production, benchmark write performance in a test environment with a workload representative of your production traffic.
To reduce write overhead:
Limit view count per base table. Each real-time materialized view on a base table adds a separate trigger and update path. Every additional view multiplies the per-statement cost, so keep the number of views per table small.
Use batch writes. Batch write data to the base table — for example, using the
COPYorINSERT INTO SELECTstatement to batch import data — to reduce the frequency of incremental view updates.Add primary keys and include them in the view projection. Create primary keys for all base tables, and include those keys in the projected columns of the view definition to accelerate delta application.
Examples
The following example shows the complete lifecycle of a real-time materialized view: installation, creation, DML operations, and cleanup.
1. Install the `polar_ivm` extension.
CREATE EXTENSION IF NOT EXISTS polar_ivm WITH SCHEMA pg_catalog;2. Create a base table and insert initial data.
CREATE TABLE t (a INT, b VARCHAR);
INSERT INTO t VALUES
(1, 'a'),
(2, 'b'),
(3, 'c'),
(4, 'd'),
(5, 'e');3. Create a real-time materialized view.
The view groups rows by b and computes MAX(a) and MIN(a) per group.
CREATE MATERIALIZED VIEW mv
REFRESH FAST
ON COMMIT
AS
SELECT max(a), min(a), b FROM t GROUP BY b;4. Query the view and verify initial data.
SELECT * FROM mv ORDER BY b;Expected output:
max | min | b
-----+-----+---
1 | 1 | a
2 | 2 | b
3 | 3 | c
4 | 4 | d
5 | 5 | e
(5 rows)5. Insert a new row and verify the view updates automatically.
INSERT INTO t VALUES (6, 'f');
SELECT * FROM mv ORDER BY b;Expected output:
max | min | b
-----+-----+---
1 | 1 | a
2 | 2 | b
3 | 3 | c
4 | 4 | d
5 | 5 | e
6 | 6 | f
(6 rows)6. Delete a row and verify the view reflects the change.
DELETE FROM t WHERE a = 2;
SELECT * FROM mv ORDER BY b;Expected output:
max | min | b
-----+-----+---
1 | 1 | a
3 | 3 | c
4 | 4 | d
5 | 5 | e
6 | 6 | f
(5 rows)7. Update rows and verify the view reflects the change.
UPDATE t SET a = a + 1;
SELECT * FROM mv ORDER BY b;Expected output:
max | min | b
-----+-----+---
2 | 2 | a
4 | 4 | c
5 | 5 | d
6 | 6 | e
7 | 7 | f
(5 rows)8. Drop the view when no longer needed.
DROP MATERIALIZED VIEW mv;