Query rewrite for materialized views
Without query rewrite, SELECT statements that reference base tables bypass any materialized views you have created—even when a matching view exists. Query rewrite removes this constraint by automatically redirecting qualifying SELECT statements to use materialized views, with no SQL changes required. This accelerates JOIN operations, aggregate functions, subqueries, common table expressions (CTEs), and high-concurrency workloads.
How it works
When you run a SELECT statement, AnalyticDB for PostgreSQL compares its syntax tree against the definitions of existing materialized views. If a match is found, the query is rewritten to read from the materialized view rather than the base table.
Two match strategies apply:
Complete match — The SELECT statement is syntactically identical to the materialized view definition (ignoring spaces, line breaks, comments, and aliases). The materialized view is used directly.
Query supplement — The SELECT statement partially overlaps with the materialized view definition. Query rewrite fills in the missing clauses and returns results from the materialized view.
To verify that query rewrite is active, run EXPLAIN on your query. When the rewrite succeeds, the materialized view name appears in the query plan instead of the base table name. See Examples for annotated output.
Supported versions
AnalyticDB for PostgreSQL V6.0 instances running V6.3.6.0 or later.
View your instance's minor version on the Basic Information page in the AnalyticDB for PostgreSQL console. If your instance does not meet this requirement, update the minor version.
Enable or disable query rewrite
The default state differs by materialized view type:
| View type | Default state | Command to change |
|---|---|---|
| Real-time materialized view | Enabled | SET enable_incremental_matview_query_rewrite TO OFF; |
| Standard materialized view | Disabled | SET enable_matview_query_rewrite TO ON; |
These commands apply session-wide. You cannot enable or disable this feature for specific instances. To change the default for a specific instance, submit a ticket.
Limitations
The following are current limitations and may be relaxed in future versions.
Query rewrite does not apply to:
SELECT FOR UPDATEstatementsQueries that contain recursive CTEs
Queries that contain random functions such as
RANDOM()andNOW()Partial matches where the query supplement requirements described below are not met
Query supplement rules
Query supplement applies to these clauses: SELECT columns, JOIN tables, GROUP BY columns, WHERE clause, HAVING clause, ORDER BY columns, and LIMIT clause. All other parts of the SELECT statement must be identical to the materialized view definition.
SELECT columns
| Scenario | Supported |
|---|---|
| Query selects columns in a different order than the materialized view | Yes |
| Query omits columns that exist in the materialized view | Yes |
| Query selects columns not in the materialized view, but those columns can be derived from materialized view columns | Yes |
| Query selects columns not in the materialized view and not derivable | No |
GROUP BY columns
When the materialized view has no GROUP BY and no aggregate functions:
The query can add GROUP BY columns, aggregate functions, or both.
When the materialized view has GROUP BY but no aggregate functions:
| Scenario | Supported |
|---|---|
| Query uses a subset of the materialized view's GROUP BY columns | Yes |
| Query adds GROUP BY columns not in the materialized view | No |
Query uses count(distinct) | Yes |
When the materialized view has aggregate functions but no GROUP BY:
Adding GROUP BY columns in the query is not supported.
When the materialized view has both GROUP BY and aggregate functions:
| Scenario | Supported |
|---|---|
| Query uses a subset of the materialized view's GROUP BY columns | Yes |
| Query adds GROUP BY columns not in the materialized view | No |
When the query has fewer GROUP BY columns than the materialized view, query rewrite performs re-aggregation. Re-aggregation is supported for COUNT, SUM, MAX, MIN, and AVG. Other aggregate functions are not supported. If the query includes a HAVING clause, GROUP BY supplement is not supported.
JOIN tables
When JOIN tables are identical between the query and the materialized view:
| Scenario | Supported |
|---|---|
| INNER JOIN tables in a different order | Yes |
| LEFT OUTER JOIN converted to RIGHT OUTER JOIN, or vice versa | Yes |
| FULL OUTER JOIN left and right tables swapped | Yes |
When the query has additional JOIN tables not in the materialized view:
Extra INNER JOIN tables can be supplemented. INNER JOIN and implicit (comma) joins are interchangeable. Additional tables cannot be supplemented for LEFT OUTER JOIN, RIGHT OUTER JOIN, or FULL OUTER JOIN.
Examples of supported patterns when supplementing an INNER JOIN:
-- Materialized view definition
SELECT * FROM a, b;
-- Supported query (adds table c)
SELECT * FROM a, b, c;-- Materialized view definition
SELECT * FROM a INNER JOIN b ON a.i = b.i;
-- Supported query (adds INNER JOIN c)
SELECT * FROM a INNER JOIN b ON a.i = b.i INNER JOIN c ON a.i = c.i;WHERE clause
AND conditions: Order is ignored. The query can add AND conditions that reference columns present in the materialized view.
Example:
-- Materialized view definition
SELECT * FROM t WHERE a > 100;
-- Supported query (adds b > 200 as an extra AND condition)
SELECT * FROM t WHERE b > 200 AND a > 100;OR conditions: Order is ignored. The query can use a narrower range, provided all referenced columns exist in the materialized view.
Example:
-- Materialized view definition
SELECT * FROM t WHERE a > 100 OR a < 200;
-- Supported query (narrower: only one branch)
SELECT * FROM t WHERE a < 200;Range subsumption: When the materialized view defines a range, the query can use an equality condition or a narrower range within that range. All referenced columns must exist in the materialized view.
-- Materialized view definition
SELECT * FROM t WHERE a < 200 AND a >= 100;
-- Supported query (equality within the range)
SELECT * FROM t WHERE a = 102;-- Materialized view definition
SELECT * FROM t WHERE a < 200 AND a >= 0;
-- Supported query (narrower range)
SELECT * FROM t WHERE a <= 100 AND a > 50;HAVING clause
When GROUP BY supplement is not needed, HAVING clause supplement follows the same rules as WHERE clause supplement: missing AND conditions can be added, OR conditions can be narrowed, and ranges can be narrowed.
When GROUP BY supplement is needed, the query can add a HAVING clause even if the materialized view has none.
ORDER BY columns
The query's ORDER BY columns must be a subset of the materialized view's ORDER BY columns. This applies regardless of whether the materialized view includes ORDER BY.
LIMIT clause
| Scenario | Supported |
|---|---|
| Materialized view has no LIMIT; query adds a LIMIT | Yes |
| Materialized view has a LIMIT; query must be an exact match | Yes (exact match only) |
Expression supplement
When an expression in the query doesn't directly match the materialized view, query rewrite searches sub-expressions from the top down to find the closest match.
-- Materialized view definition
SELECT a+b, c FROM t;
-- Supported queries (use sub-expression a+b)
SELECT a+b, (a+b)+c, mod(a+b, c) FROM t;
SELECT sum((a+b)*c) FROM t;For aggregate function expressions:
SUM()andCOUNT()in the materialized view can be combined to satisfy anAVG()in the query.COUNT(*)andCOUNT(1)are interchangeable.
-- Materialized view definition
SELECT sum(a), count(a), count(*) FROM t;
-- Supported query
SELECT avg(a), count(1) FROM t;CTEs and subqueries
A common table expression (CTE) in a WITH clause is treated as a subquery.
Single subquery:
| Scenario | Behavior |
|---|---|
| Query's primary query and subquery both match the materialized view | Complete match replacement |
| Query's subquery matches but primary query differs | Query supplement applied to primary query |
| Query has a subquery, materialized view does not — and the materialized view matches the primary query | Subquery supplemented to materialized view query (correlated subqueries not supported) |
| Query has a subquery, materialized view does not — and the materialized view matches the subquery | Materialized view replaces the subquery |
When one part of the query is rewritten, query rewrite continues attempting to rewrite the remaining parts.
Multiple subqueries: Query rewrite applies the same rules to each subquery independently and continues rewriting remaining parts after each successful rewrite.
Recursive CTEs are not supported.
UNION, EXCEPT, and INTERSECT
When both the query and the materialized view use UNION, EXCEPT, or INTERSECT:
UNION and INTERSECT: The two operand queries can be swapped, and clauses can be supplemented.
EXCEPT: Clauses can be supplemented, but the two operand queries cannot be swapped.
When only the query uses UNION, EXCEPT, or INTERSECT:
Query rewrite can supplement the set operator to join results from multiple materialized views.
Selecting among multiple materialized views
When a query matches multiple materialized views, the following priority applies:
A complete match is preferred over a query supplement match.
Among supplement matches, the materialized view that covers the most tables in the query is preferred.
If table coverage is equal, the materialized view that references the least data is preferred.
Examples
Example 1: Complete match
Create a base table:
CREATE TABLE t1 (a int, b int) DISTRIBUTED BY (a);Insert data:
INSERT INTO t1 VALUES (generate_series(1, 10), generate_series(1, 2));Create a real-time materialized view:
CREATE INCREMENTAL MATERIALIZED VIEW mv AS SELECT count(a), b FROM t1 GROUP BY b DISTRIBUTED BY (b);Run
EXPLAINon a query identical to the materialized view definition:EXPLAIN SELECT count(a), b FROM t1 GROUP BY b;Expected output:
QUERY PLAN ----------------------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) (cost=0.00..2.02 rows=2 width=12) -> Seq Scan on mv (cost=0.00..2.02 rows=1 width=12) Optimizer: Postgres query optimizer (3 rows)The plan shows
Seq Scan on mvinstead ofSeq Scan on t1, confirming a complete match. Query rewrite reads directly from the materialized view.
Example 2: Query supplement (JOIN and WHERE added, GROUP BY narrowed)
Create two base tables:
CREATE TABLE t1 (a int, b int) DISTRIBUTED BY (a); CREATE TABLE t2 (i int, j int) DISTRIBUTED BY (i);Insert data:
INSERT INTO t1 VALUES (generate_series(1, 10), generate_series(1, 2)); INSERT INTO t2 VALUES (generate_series(1, 10), generate_series(1, 2));Create a real-time materialized view:
CREATE INCREMENTAL MATERIALIZED VIEW mv AS SELECT count(a), a, b FROM t1 GROUP BY a, b DISTRIBUTED BY (a);Run
EXPLAINon a query that adds a JOIN and WHERE clause, and uses fewer GROUP BY columns:EXPLAIN SELECT count(a) FROM t1 JOIN t2 ON t1.a = t2.i WHERE b > 3 GROUP BY a;Expected output:
QUERY PLAN ---------------------------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) (cost=0.00..437.00 rows=1 width=8) -> Result (cost=0.00..437.00 rows=1 width=8) -> GroupAggregate (cost=0.00..437.00 rows=1 width=8) Group Key: mv.a -> Sort (cost=0.00..437.00 rows=1 width=12) Sort Key: mv.a -> Hash Join (cost=0.00..437.00 rows=1 width=12) Hash Cond: (mv.a = t2.i) -> Index Scan using mv_index on mv (cost=0.00..6.00 rows=1 width=12) Index Cond: (b > 3) -> Hash (cost=431.00..431.00 rows=4 width=4) -> Seq Scan on t2 (cost=0.00..431.00 rows=4 width=4) Optimizer: Pivotal Optimizer (GPORCA) version 3.86.0 (13 rows)The plan references
mvinstead oft1, confirming that query rewrite used the materialized view. Query rewrite supplemented the JOIN tot2, applied theWHERE b > 3filter via an index scan on the materialized view, and performed re-aggregation to reduce the GROUP BY from(a, b)to(a).