Query rewrite lets AnalyticDB for MySQL automatically redirect queries to materialized views—without any changes to your SQL. When a query matches a materialized view, the engine reads precomputed results instead of scanning base tables, which can significantly reduce query latency.
Prerequisites
Before you begin, make sure you have:
An AnalyticDB for MySQL cluster running V3.1.4.0 or later
To check the minor version of a Data Lakehouse Edition cluster, run
SELECT adb_version();. To upgrade, contact technical support. For Data Warehouse Edition clusters, see Update the minor version of a cluster.The following permissions on the relevant databases or tables:
Permission Required for CREATE Creating materialized views INSERT Refreshing materialized views SELECT Querying the base tables and columns referenced by a materialized view Refresh via 127.0.0.1or'%'Configuring auto-refresh on materialized views you own
How it works
AnalyticDB for MySQL compares each incoming query against all materialized views that have query rewrite enabled. If a match is found, the engine rewrites the query to read from the materialized view instead of the base tables. Two matching strategies are used, applied in the following order:
Exact match rewrite — when the query structure is identical to the materialized view definition. This is the simpler strategy with fewer restrictions.
Advanced query rewrite — when the structures differ. The engine applies rewrite rules (FILTER, JOIN, AGGREGATION, AGGREGATION ROLLUP, SUBQUERIES, QUERY PARTIAL, UNION) to determine whether the materialized view contains enough data to answer the query or part of it. Different subqueries in the same statement can match different materialized views.
All query rewrites operate at the STALE_TOLERATED level: the engine rewrites a query even if the materialized view contains stale data that hasn't been synced from the base tables. This maximizes rewrite coverage but means results may not reflect the latest inserts or updates. Refresh your materialized views before running latency-sensitive queries. For details, see Configure full refresh for materialized views.
AnalyticDB for MySQL支持如下改写级别:
TRUSTED:仅使用已刷新到最新数据的物化视图来自动改写查询,即物化视图存储的数据与基表数据相同。STALE_TOLERATED:会对所有开启改写的物化视图进行查询改写,即使物化视图中的数据不是最新的,改写结果可能无法反映物化视图基表的最新更改。
Enable query rewrite
Enable query rewrite in one of two ways:
At creation time, include the
ENABLE QUERY REWRITEclause in yourCREATE MATERIALIZED VIEWstatement. See Create a materialized view — Parameters.After creation, run:
ALTER MATERIALIZED VIEW <mv_name> ENABLE QUERY REWRITE;
Disable query rewrite
Disable query rewrite in one of two ways:
For a specific materialized view:
ALTER MATERIALIZED VIEW <mv_name> DISABLE QUERY REWRITE;For a specific query, add a hint before the
SELECTstatement:/*+MV_QUERY_REWRITE_ENABLED=false*/ SELECT ...
Verify that query rewrite is active
After enabling query rewrite, use EXPLAIN to confirm the engine is reading from the materialized view.
Example
Create a materialized view with query rewrite enabled.
CREATE MATERIALIZED VIEW adb_mv REFRESH START WITH now() + interval 1 day ENABLE QUERY REWRITE AS SELECT course_id, course_name, max(course_grade) AS max_grade FROM tb_courses;After enabling query rewrite, run
EXPLAINon the query.EXPLAIN SELECT course_id, course_name, max(course_grade) AS max_grade FROM tb_courses;Check the execution plan. When query rewrite is active, the
TableScanrow shows the materialized view name (adb_mv), not the base table (tb_courses).+---------------+ | Plan Summary | +---------------+ 1- Output[ Query plan ] {Est rowCount: 1.0} 2 -> Exchange[GATHER] {Est rowCount: 1.0} 3 - TableScan {table: adb_mv, Est rowCount: 1.0}If the plan still shows
TableScan {table: tb_courses, ...}, query rewrite did not activate. Check the Troubleshooting section.
Rewrite range
The following examples demonstrate each rewrite rule supported by the advanced query rewrite method. All examples use the same four tables:
CREATE TABLE part (
partkey INTEGER NOT NULL,
name VARCHAR(55) NOT NULL,
type VARCHAR(25) NOT NULL
);
CREATE TABLE lineitem (
orderkey BIGINT,
partkey BIGINT NOT NULL,
suppkey BIGINT NOT NULL,
extendedprice DOUBLE NOT NULL,
discount DOUBLE NOT NULL,
returnflag CHAR(1) NOT NULL,
linestatus CHAR(1) NOT NULL,
shipdate DATE NOT NULL,
shipmode VARCHAR(25) NOT NULL,
commitdate DATE NOT NULL,
receiptdate DATE NOT NULL
);
CREATE TABLE orders (
orderkey BIGINT PRIMARY KEY,
custkey BIGINT NOT NULL,
orderstatus VARCHAR(1) NOT NULL,
totalprice DOUBLE NOT NULL,
orderdate DATE NOT NULL
);
CREATE TABLE partsupp (
partkey INTEGER NOT NULL PRIMARY KEY,
suppkey INTEGER NOT NULL,
availqty INTEGER NOT NULL,
supplycost DECIMAL(15,2) NOT NULL
);Exact match rewrite
When the query structure is identical to the materialized view definition, the engine rewrites the query directly.
Query
SELECT
l.returnflag,
l.linestatus,
SUM(l.extendedprice * (1 - l.discount)),
COUNT(*) AS count_order
FROM lineitem AS l
GROUP BY l.returnflag, l.linestatus;Materialized view
CREATE MATERIALIZED VIEW mv0
REFRESH NEXT now() + interval 1 day
ENABLE QUERY REWRITE
AS
SELECT
l.returnflag,
l.linestatus,
SUM(l.extendedprice * (1 - l.discount)) AS sum_disc_price,
COUNT(*) AS count_order
FROM lineitem AS l
GROUP BY l.returnflag, l.linestatus;Rewritten query
SELECT returnflag, linestatus, sum_disc_price, count_order
FROM mv0;Advanced query rewrite
FILTER
When the query predicate is narrower than the materialized view's predicate, the engine adds a WHERE clause to the materialized view scan to apply the missing filter.
Query
SELECT
l.shipmode,
l.extendedprice * (1 - l.discount) AS disc_price
FROM orders AS o, lineitem AS l
WHERE o.orderkey = l.orderkey
AND l.shipmode IN ('REG AIR', 'TRUCK')
AND l.commitdate < l.receiptdate
AND l.shipdate < l.commitdate;Materialized view
CREATE MATERIALIZED VIEW mv1
REFRESH NEXT now() + interval 1 day
ENABLE QUERY REWRITE
AS
SELECT
l.shipmode,
l.extendedprice,
l.discount
FROM orders AS o, lineitem AS l
WHERE o.orderkey = l.orderkey
AND l.commitdate < l.receiptdate
AND l.shipdate < l.commitdate;Rewritten query
SELECT
shipmode,
extendedprice * (1 - discount) AS disc_price,
discount
FROM mv1
WHERE shipmode IN ('REG AIR', 'TRUCK');JOIN
When the query and the materialized view have different join relationships, the engine derives the required join from the materialized view. For example, a materialized view with an outer join can satisfy a query that needs an inner join by filtering out null rows.
Supported join types: inner join, outer join, left join, and right join.
Query
SELECT
p.type,
p.partkey,
ps.suppkey
FROM part AS p, partsupp AS ps
WHERE p.partkey = ps.partkey
AND p.type NOT LIKE 'MEDIUM POLISHED%';Materialized view
CREATE MATERIALIZED VIEW mv2
REFRESH NEXT now() + INTERVAL 1 day
ENABLE QUERY REWRITE
AS
SELECT
p.type,
p.partkey,
ps.suppkey
FROM partsupp AS ps
INNER JOIN part AS p ON p.partkey = ps.partkey
WHERE p.type NOT LIKE 'MEDIUM POLISHED%';Rewritten query
SELECT type, partkey, suppkey
FROM mv2;AGGREGATION
When the query or the materialized view uses different GROUP BY clauses or aggregate functions, the engine constructs the same aggregate function from the materialized view using the AGGREGATION rule.
Query
SELECT
l.returnflag,
l.linestatus,
SUM(l.extendedprice * (1 - l.discount)) AS sum_disc_price
FROM lineitem AS l
GROUP BY l.returnflag, l.linestatus;Materialized view
CREATE MATERIALIZED VIEW mv3
REFRESH NEXT now() + INTERVAL 1 day
ENABLE QUERY REWRITE
AS
SELECT
l.returnflag,
l.linestatus,
SUM(l.extendedprice * (1 - l.discount)) AS sum_disc_price,
COUNT(*) AS count_order
FROM lineitem AS l
GROUP BY l.returnflag, l.linestatus;Rewritten query
SELECT returnflag, linestatus, sum_disc_price, count_order
FROM mv3;AGGREGATION ROLLUP
When the query groups by a subset of the materialized view's GROUP BY fields, the engine rolls up the precomputed aggregates.
Query
SELECT
l.returnflag,
SUM(l.extendedprice * (1 - l.discount)) AS sum_disc_price,
COUNT(*) AS count_order
FROM lineitem AS l
WHERE l.returnflag = 'R'
GROUP BY l.returnflag;Materialized view
CREATE MATERIALIZED VIEW mv4
REFRESH NEXT now() + INTERVAL 1 day
ENABLE QUERY REWRITE
AS
SELECT
l.returnflag,
l.linestatus,
SUM(l.extendedprice * (1 - l.discount)) AS sum_disc_price,
COUNT(*) AS count_order
FROM lineitem AS l
GROUP BY l.returnflag, l.linestatus;Rewritten query
SELECT
returnflag,
linestatus,
sum_disc_price,
count_order
FROM mv4
WHERE returnflag = 'R'
GROUP BY returnflag;SUBQUERIES
When the query uses a subquery in place of a base table, the engine checks whether the materialized view covers the full table and pushes the subquery's filter into the rewritten scan.
Query
SELECT
p.type,
p.partkey,
ps.suppkey
FROM part AS p,
(SELECT * FROM partsupp WHERE suppkey > 10) ps
WHERE p.partkey = ps.partkey;Materialized view
CREATE MATERIALIZED VIEW mv5
REFRESH NEXT now() + INTERVAL 1 day
ENABLE QUERY REWRITE
AS
SELECT
p.type,
p.partkey,
ps.suppkey
FROM part AS p, partsupp AS ps
WHERE p.partkey = ps.partkey;Rewritten query
SELECT type, partkey, suppkey
FROM mv5
WHERE suppkey > 10;QUERY PARTIAL
When the query references a table that the materialized view doesn't cover, the engine joins the materialized view with the missing table.
Query
SELECT
p.type,
p.partkey,
ps.suppkey
FROM part AS p, partsupp AS ps
WHERE p.partkey = ps.partkey
AND p.type NOT LIKE 'MEDIUM POLISHED%';Materialized view
CREATE MATERIALIZED VIEW mv6
REFRESH NEXT now() + INTERVAL 1 day
ENABLE QUERY REWRITE
AS
SELECT
p.type,
p.partkey
FROM part AS p
WHERE p.type NOT LIKE 'MEDIUM POLISHED%';Rewritten query
SELECT
mv6.type,
mv6.partkey,
ps.suppkey
FROM mv6, partsupp AS ps
WHERE mv6.partkey = ps.partkey;UNION
When the materialized view covers only part of the query's date or value range, the engine retrieves the covered portion from the materialized view and fetches the remaining rows directly from the base table, then combines the results with UNION ALL.
Query
SELECT
l.linestatus,
COUNT(*) AS count_order
FROM lineitem AS l
WHERE l.shipdate >= DATE '1998-01-01'
GROUP BY l.linestatus;Materialized view (covers shipdate >= 2000-01-01 only)
CREATE MATERIALIZED VIEW mv7
REFRESH NEXT now() + interval 1 day
ENABLE QUERY REWRITE
AS
SELECT
l.linestatus,
COUNT(*) AS count_order
FROM lineitem AS l
WHERE l.shipdate >= DATE '2000-01-01'
GROUP BY l.linestatus;Rewritten query
SELECT linestatus, count_order
FROM (
SELECT linestatus, count_order
FROM mv7
UNION ALL
SELECT
l.linestatus,
COUNT(*) AS count_order
FROM lineitem AS l
WHERE l.shipdate >= DATE '1998-01-01' AND l.shipdate < DATE '1998-01-01'
GROUP BY l.linestatus
)
GROUP BY linestatus;Limitations
Exact match rewrite
The exact match rewrite method does not activate if the materialized view contains:
Nondeterministic functions:
NOW,CURRENT_TIMESTAMP,RANDOMUser-defined functions (UDFs)
Advanced query rewrite
The advanced query rewrite method does not activate if the materialized view contains any of the following:
ORDER BY,LIMIT, orOFFSETclauseUNIONorUNION ALLclauseGROUPING SETS,CUBE, orROLLUPin aGROUP BYclauseWindow functions
FULL OUTER JOINSystem tables
Correlated subqueries
Nondeterministic functions:
NOW,CURRENT_TIMESTAMP,RANDOMUDFs
HAVINGclauseSELF JOIN
Statement types where query rewrite never activates
Query rewrite does not apply to queries embedded in the following statement types, regardless of the materialized view definition:
CREATE TABLE AS SELECTINSERT INTO SELECTINSERT OVERWRITE SELECTREPLACE INTO SELECTDELETEorUPDATE
Single-table queries without filters or aggregates
Query rewrite does not activate for single-table queries that have no filter conditions and no aggregate functions.
Troubleshooting
Query rewrite isn't activating after I create a materialized view.
Start with the most common causes:
Query rewrite is not enabled on the materialized view. Run
ALTER MATERIALIZED VIEW <mv_name> ENABLE QUERY REWRITE;and try again.The materialized view hits a limit. Review the Limitations section and check whether your materialized view definition includes any unsupported clause or function.
Missing SELECT permission on the materialized view. Grant the required permission to the account running the query:
GRANT SELECT ON <database>.<mv_name> TO '<account>';For details, see the Required permissions section of the Query data from a materialized view topic.
物化视图是否过期或正在刷新中。您可以通过
REFRESH MATERIALIZED VIEW <mv_name>语句手动刷新一次物化视图,更新详情,请参见全量刷新物化视图。物化视图是否包含查询涉及的所有列,若未包含所有列,自动改写不会生效。建议确认查询所涉及的所有列后,再重新创建视图。创建视图的语句,请参见创建物化视图。