MaxCompute can automatically rewrite SQL queries to read from materialized views instead of source tables, accelerating query performance without requiring changes to your SQL code.
The optimizer selects the most effective rewrite rule for each query. If a rewrite would add operations without improving performance, MaxCompute skips the rewrite and runs the query against the source table instead.
Usage notes
Data completeness
For a query to be rewritten, the materialized view must contain all the data the query needs — output columns, columns used in filter conditions, aggregate functions, and JOIN conditions. If the view is missing required columns or uses unsupported aggregate functions, the rewrite fails.
Invalid materialized views
If a materialized view is invalid, query rewrite is not supported. The query runs against the source table without acceleration.
Enable automatic query rewrite
Add the following SET statement before your query:
SET odps.sql.materialized.view.enable.auto.rewriting=true;Cross-project rewrite
By default, a MaxCompute project uses only its own materialized views for query rewrite. To use materialized views from other projects, specify an allowlist before your query:
SET odps.sql.materialized.view.source.project.white.list = <project_name1>,<project_name2>,<project_name3>;Rewrite for LEFT/RIGHT JOIN and UNION ALL
To rewrite queries for materialized views that contain LEFT JOIN, RIGHT JOIN, or UNION ALL, add the following before your query:
SET odps.sql.materialized.view.enable.substitute.rewriting=true;Supported operators
The following table compares the operator types MaxCompute supports for query rewrite against other systems.
| Operator type | Classification | MaxCompute | BigQuery | Amazon Redshift | Hive |
|---|---|---|---|---|---|
| FILTER | Full expression match | Support | Support | Support | Support |
| FILTER | Partial expression match | Support | Supported | Supported | Support |
| AGGREGATE | Single AGGREGATE | Support | Support | Supported | Supported |
| AGGREGATE | Multiple AGGREGATEs | Not supported | Not supported | Not supported | Not supported |
| JOIN | JOIN type | INNER JOIN | Not supported | INNER JOIN | INNER JOIN |
| JOIN | Single JOIN | Support | Not supported | Support | Support |
| JOIN | Multiple JOINs | Supported | Not supported | Support | Support |
| AGGREGATE+JOIN | — | Support | Not supported | Support | Support |
Examples
Example 1: Filter conditions
Create a materialized view:
CREATE MATERIALIZED VIEW mv AS SELECT a,b,c FROM src WHERE a>5;The following table shows how queries on src are rewritten to use mv.
| Original query | Rewritten query |
|---|---|
SELECT a,b FROM src WHERE a>5; | SELECT a,b FROM mv; |
SELECT a, b FROM src WHERE a=10; | SELECT a,b FROM mv WHERE a=10; |
SELECT a, b FROM src WHERE a=10 AND b=3; | SELECT a,b FROM mv WHERE a=10 AND b=3; |
SELECT a, b FROM src WHERE a>3; | (SELECT a,b FROM src WHERE a>3 AND a<=5) UNION (SELECT a,b FROM mv); |
SELECT a, b FROM src WHERE a=10 AND d=4; | Rewrite fails — column d is not in the materialized view. |
SELECT d, e FROM src WHERE a=10; | Rewrite fails — columns d and e are not in the materialized view. |
SELECT a, b FROM src WHERE a=1; | Rewrite fails — the materialized view contains no data where a=1. |
Example 2: Aggregate functions
When the materialized view and the query use the same aggregation key, all aggregate functions can be rewritten. When the aggregation keys differ, only SUM, MIN, and MAX are supported.
Create a materialized view:
CREATE MATERIALIZED VIEW mv AS
SELECT a, b, sum(c) AS sum, count(d) AS cnt FROM src GROUP BY a, b;The following table shows how queries are rewritten.
| Original query | Rewritten query |
|---|---|
SELECT a, sum(c) FROM src GROUP BY a; | SELECT a, sum(sum) FROM mv GROUP BY a; |
SELECT a, count(d) FROM src GROUP BY a, b; | SELECT a, cnt FROM mv; |
SELECT a, count(b) FROM (SELECT a, b FROM src GROUP BY a, b) GROUP BY a; | SELECT a,count(b) FROM mv GROUP BY a; |
SELECT a,count(b) FROM mv GROUP BY a; | Rewrite fails — the view has already aggregated columns a and b, and cannot aggregate b again. |
SELECT a, count(c) FROM src GROUP BY a; | Rewrite fails — re-aggregation is not supported for the COUNT function. |
Aggregate functions with DISTINCT
When an aggregate function contains DISTINCT, the rewrite requires the materialized view and the query to have the same aggregation key.
Create a materialized view:
CREATE MATERIALIZED VIEW mv AS
SELECT a, b, sum(DISTINCT c) AS sum, count(DISTINCT d) AS cnt FROM src GROUP BY a, b;| Original query | Rewritten query |
|---|---|
SELECT a, count(DISTINCT d) FROM src GROUP BY a, b; | SELECT a, cnt FROM mv; |
SELECT a, count(c) FROM src GROUP BY a, b; | Rewrite fails — re-aggregation is not supported for the COUNT function. |
SELECT a, count(DISTINCT c) FROM src GROUP BY a; | Rewrite fails — column a requires another aggregation. |
Example 3: JOIN clause
Rewrite JOIN inputs
Create materialized views:
CREATE MATERIALIZED VIEW mv1 AS SELECT a, b FROM j1 WHERE b > 10;
CREATE MATERIALIZED VIEW mv2 AS SELECT a, b FROM j2 WHERE b > 10;| Original query | Rewritten query |
|---|---|
SELECT j1.a,j1.b,j2.a FROM (SELECT a,b FROM j1 WHERE b > 10) j1 JOIN j2 ON j1.a=j2.a; | SELECT mv1.a, mv1.b, j2.a FROM mv1 JOIN j2 ON mv1.a=j2.a; |
SELECT j1.a,j1.b,j2.a FROM (SELECT a,b FROM j1 WHERE b > 10) j1 JOIN (SELECT a,b FROM j2 WHERE b > 10) j2 ON j1.a=j2.a; | SELECT mv1.a,mv1.b,mv2.a FROM mv1 JOIN mv2 ON mv1.a=mv2.a; |
JOIN with filter conditions
Create materialized views:
-- Non-partitioned materialized views
CREATE MATERIALIZED VIEW mv1 AS SELECT j1.a, j1.b FROM j1 JOIN j2 ON j1.a=j2.a;
CREATE MATERIALIZED VIEW mv2 AS SELECT j1.a, j1.b FROM j1 JOIN j2 ON j1.a=j2.a WHERE j1.a > 10;
-- Partitioned materialized view
CREATE MATERIALIZED VIEW mv LIFECYCLE 7 PARTITIONED BY (ds) AS SELECT t1.id, t1.ds AS ds FROM t1 JOIN t2 ON t1.id = t2.id;| Original query | Rewritten query |
|---|---|
SELECT j1.a,j1.b FROM j1 JOIN j2 ON j1.a=j2.a WHERE j1.a=4; | SELECT a, b FROM mv1 WHERE a=4; |
SELECT j1.a,j1.b FROM j1 JOIN j2 ON j1.a=j2.a WHERE j1.a > 20; | SELECT a,b FROM mv2 WHERE a>20; |
SELECT j1.a,j1.b FROM j1 JOIN j2 ON j1.a=j2.a WHERE j1.a > 5; | (SELECT j1.a,j1.b FROM j1 JOIN j2 ON j1.a=j2.a WHERE j1.a > 5 AND j1.a <= 10) UNION SELECT * FROM mv2; |
SELECT key FROM t1 JOIN t2 ON t1.id= t2.id WHERE t1.ds='20210306'; | SELECT key FROM mv WHERE ds='20210306'; |
SELECT key FROM t1 JOIN t2 ON t1.id= t2.id WHERE t1.ds>='20210306'; | SELECT key FROM mv WHERE ds>='20210306'; |
SELECT j1.a,j1.b FROM j1 JOIN j2 ON j1.a=j2.a WHERE j2.a=4; | Rewrite fails — the materialized view does not have the j2.a column. |
JOIN with an additional table
Create a materialized view:
CREATE MATERIALIZED VIEW mv AS SELECT j1.a, j1.b FROM j1 JOIN j2 ON j1.a=j2.a;| Original query | Rewritten query |
|---|---|
SELECT j1.a, j1.b FROM j1 JOIN j2 JOIN j3 ON j1.a=j2.a AND j1.a=j3.a; | SELECT mv.a, mv.b FROM mv JOIN j3 ON mv.a=j3.a; |
SELECT j1.a, j1.b FROM j1 JOIN j2 JOIN j3 ON j1.a=j2.a AND j2.a=j3.a; | SELECT mv.a,mv.b FROM mv JOIN j3 ON mv.a=j3.a; |
The three JOIN sub-scenarios above can be combined. If a query meets the rewrite conditions, it is rewritten.
Example 4: LEFT JOIN clause
Enable substitute rewrite before running the query:
SET odps.sql.materialized.view.enable.substitute.rewriting=true;Create a materialized view:
CREATE MATERIALIZED VIEW mv LIFECYCLE 7(
user_id,
job,
total_amount
) AS SELECT t1.user_id, t1.job, sum(t2.order_amount) AS total_amount
FROM user_info AS t1 LEFT JOIN sale_order AS t2 ON t1.user_id=t2.user_id GROUP BY t1.user_id;| Original query | Rewritten query |
|---|---|
SELECT t1.user_id, sum(t2.order_amout) AS total_amount FROM user_info AS t1 LEFT JOIN sale_order AS t2 ON t1.user_id=t2.user_id GROUP BY t1.user_id; | SELECT user_id, total_amount FROM mv; |
Example 5: UNION ALL clause
Enable substitute rewrite before running the query:
SET odps.sql.materialized.view.enable.substitute.rewriting=true;Create a materialized view:
CREATE MATERIALIZED VIEW mv LIFECYCLE 7(
user_id,
tran_amount,
tran_date
) AS SELECT user_id, tran_amount, tran_date FROM alipay_tran UNION ALL
SELECT user_id, tran_amount, tran_date FROM unionpay_tran;| Original query | Rewritten query |
|---|---|
SELECT user_id, tran_amount FROM alipay_tran UNION ALL SELECT user_id, tran_amount FROM unionpay_tran; | SELECT user_id, total_amount FROM mv; |
Example 6: End-to-end scenario
This example shows the full flow: creating a materialized view, enabling automatic query rewrite, and verifying that the rewrite took effect.
Scenario: The visit_records table logs page visits. You frequently query page visit counts grouped by page ID.
The table schema is:
+------------------------------------------------------------------------------------+
| Field | Type | Label | Comment |
+------------------------------------------------------------------------------------+
| page_id | string | | |
| user_id | string | | |
| visit_time | string | | |
+------------------------------------------------------------------------------------+Create a materialized view that pre-aggregates visit counts by page:
CREATE MATERIALIZED VIEW count_mv AS SELECT page_id, count(*) FROM visit_records GROUP BY page_id;Enable automatic query rewrite and run the query:
SET odps.sql.materialized.view.enable.auto.rewriting=true; SELECT page_id, count(*) FROM visit_records GROUP BY page_id;MaxCompute automatically rewrites this query to read from
count_mv.Verify the rewrite using
EXPLAIN:EXPLAIN SELECT page_id, count(*) FROM visit_records GROUP BY page_id;The output shows:
job0 is root job In Job job0: root Tasks: M1 In Task M1: Data source: doc_test_dev.count_mv TS: doc_test_dev.count_mv FS: output: Screen schema: page_id (string) _c1 (bigint) OKThe
Data sourcefield showsdoc_test_dev.count_mv, confirming the query reads from the materialized view and the rewrite is successful.