All Products
Search
Document Center

AnalyticDB:CREATE MATERIALIZED VIEW

Last Updated:Mar 28, 2026

Creates a materialized view with complete or fast refresh and a configurable refresh schedule.

Syntax

CREATE [OR REPLACE] MATERIALIZED VIEW mv_name
[mv_definition]
[mv_properties]
[REFRESH {COMPLETE|FAST}]
[ON {DEMAND|OVERWRITE}]
[START WITH date] [NEXT date]
[{DISABLE|ENABLE} QUERY REWRITE]
[COMMENT 'view_comment']
AS
query_body

mv_definition:
  ({column_name column_type [column_attributes] [ column_constraints ] [COMMENT 'column_comment']
  | table_constraints}
  [, ... ])
  [table_attribute]
  [partition_options]
  [index_all]
  [storage_policy]
  [block_size]
  [engine]
  [table_properties]

Parameters

OR REPLACE

Optional. Not changeable after creation.

Requires V3.1.4.7 or later.
  • If no existing materialized view uses the same name, a new materialized view is created.

  • If an existing materialized view uses the same name, AnalyticDB for MySQL creates a temporary view to write new data, then replaces the original after the write completes.

mv_definition

Optional. Not changeable after creation.

Defines the schema of the materialized view. If omitted, AnalyticDB for MySQL derives the schema from the query_body results: it uses all query output columns, auto-generates a primary key, creates indexes on all columns, configures hot data storage, and sets the engine to XUANWU.

To define the schema manually—including the distribution key, partition key, primary key, indexes, and tiered storage policies—use the same syntax as CREATE TABLE. For example, use the INDEX keyword to index only specific columns instead of all columns, or set a mixed hot-and-cold storage policy to reduce storage costs.

Primary key rules

For complete refresh, if you do not define a primary key, AnalyticDB for MySQL auto-generates a column named __adb_auto_id__ and uses it as the primary key. To define one explicitly, select any column from the query_body results.

For fast refresh, the primary key must follow these rules regardless of whether it is explicitly defined or auto-generated:

Query typePrimary key requirement
Grouped aggregate (with GROUP BY)Must be the GROUP BY columns. For example, GROUP BY a, b requires PRIMARY KEY(a, b).
Non-grouped aggregate (without GROUP BY)Must be a constant.
Non-aggregateMust exactly match the base table primary key. For example, if the base table has PRIMARY KEY(sale_id, sale_date), the materialized view must also use PRIMARY KEY(sale_id, sale_date).
Tip: Define a primary key, a distribution key, and a partition key to optimize query performance.

mv_properties

Optional. Changeable after creation using ALTER MATERIALIZED VIEW.

Requires V3.1.9.3 or later on Enterprise Edition, Basic Edition, or Data Lakehouse Edition clusters.

Specifies the resource policy for the materialized view in JSON format, including the resource group and configuration hints for refresh tasks.

MV_PROPERTIES='{
  "mv_resource_group": "<resource_group_name>",
  "mv_refresh_hints": {"<hint_name>": "<hint_value>"}
}'

mv_resource_group

The resource group used to create and refresh the materialized view. Defaults to user_default if not specified.

Set this to an interactive resource group or a job resource group powered by the XIHE engine. Job resource groups require seconds to minutes to provision resources; use one when higher refresh latency is acceptable. A materialized view that uses a job resource group is called an elastic materialized view. To improve refresh speed for elastic materialized views, set elastic_job_max_acu in mv_refresh_hints to increase the maximum resource limit.

View available resource groups on the Resource Groups page in the console or by calling the DescribeDBResourceGroup operation.

If the specified resource group does not exist, creation fails.

mv_refresh_hints

Configuration parameters for the materialized view. For supported parameters and usage, see Common Hints.

REFRESH [COMPLETE | FAST]

Optional. Default: COMPLETE. Not changeable after creation.

The refresh policy. For guidance on choosing between policies, see How to choose a refresh policy.

COMPLETE

Runs the full query_body SQL on each refresh, scans all target partitions of the base table, and overwrites existing data with the newly computed results.

Supports three trigger mechanisms: on-demand manual refresh, scheduled automatic refresh (ON DEMAND [START WITH date] [NEXT date]), and automatic refresh when the base table is overwritten (ON OVERWRITE).

FAST

Requires V3.1.9.0 or later. V3.1.9.0 supports single-table materialized views only. V3.2.0.0 or later supports both single-table and multi-table materialized views.

Rewrites the query_body to scan only changed rows in the base table (INSERT, DELETE, UPDATE), then applies those changes to the materialized view. This avoids full base table scans and reduces per-refresh compute overhead.

Before creating a materialized view with fast refresh, enable binary logging for the cluster and all base tables. Otherwise, creation fails. See Enable binary logging.

Fast refresh requires scheduled automatic refresh—define the next refresh time using ON DEMAND {NEXT date}. It also has specific limits on the query_body; creation fails if the query does not meet these limits. See Limitations.

ON [DEMAND | OVERWRITE]

Optional. Default: DEMAND. Not changeable after creation.

The refresh trigger mechanism. For guidance on choosing between mechanisms, see How to choose a refresh trigger mechanism.

DEMAND

Refresh on demand: trigger a refresh manually or use the NEXT clause to schedule automatic refresh. Fast refresh supports only ON DEMAND.

OVERWRITE

Triggered automatically when the base table is overwritten by an INSERT OVERWRITE statement. Do not specify START WITH or NEXT when using ON OVERWRITE.

[START WITH date] [NEXT date]

Optional. Not changeable after creation. Applies only when the trigger mechanism is ON DEMAND.

START WITH

The time of the first refresh. If omitted, the first refresh runs at creation time.

NEXT

The time of the next refresh. If not specified, no scheduled refresh occurs after the first.

Refresh typeRequirement
Fast refreshNEXT is required. The interval must be between 5 seconds and 5 minutes.
Complete refreshNEXT is optional. If specified, the interval must be at least 60 seconds.

date

Any time function is supported, but the value must be accurate to the second. Milliseconds are truncated.

[DISABLE | ENABLE] QUERY REWRITE

Optional. Default: DISABLE. Changeable after creation using ALTER MATERIALIZED VIEW.

Requires V3.1.4 or later.

Enables or disables query rewrite for the materialized view. For details, see Query rewrite for materialized views.

When enabled, the optimizer rewrites full or partial queries based on SQL patterns and routes them to the materialized view instead of the base tables, improving query performance.

query_body

Required. Not changeable after creation.

The query that defines the materialized view.

For complete refresh, base tables can be internal tables, external tables, existing materialized views, or views in AnalyticDB for MySQL. There are no restrictions on query syntax. For supported query syntax, see SELECT.

For fast refresh, base tables must be internal tables in AnalyticDB for MySQL, and the query must follow the rules below.

SELECT output column rules (fast refresh)

Grouped aggregate queries (with GROUP BY)

All GROUP BY columns must appear in the SELECT list.

CorrectIncorrect
sale_date is in both SELECT and GROUP BY.sale_date is in GROUP BY but missing from SELECT — creation fails.
SELECT sale_id, sale_date, max(quantity) AS max, sum(price) AS sum FROM sales GROUP BY sale_id, sale_dateSELECT sale_id, max(quantity) AS max, sum(price) AS sum FROM sales GROUP BY sale_id, sale_date

Non-grouped aggregate queries (without GROUP BY)

The SELECT list can contain only constant columns and aggregate columns, or only aggregate columns.

CorrectIncorrect
Only aggregate columns: SELECT max(quantity) AS max, sum(price) AS sum FROM salesNon-aggregate column in SELECT — creation fails: SELECT sale_id, max(quantity) AS max, sum(price) AS sum FROM salessale_id is not a constant or aggregate.
Constant and aggregate columns: SELECT 1 AS pk, max(quantity) AS max, sum(price) AS sum FROM sales — The constant pk serves as the primary key.

Non-aggregate queries

All primary key columns of the base table must appear in the SELECT list.

CorrectIncorrect
Single primary key sale_id included: SELECT sale_id, quantity FROM salesPrimary key sale_id missing from SELECT — creation fails: SELECT sale_date, quantity FROM sales
Composite primary key PRIMARY KEY(sale_id, sale_date) — both columns included: SELECT sale_id, sale_date, quantity FROM sales1Composite primary key PRIMARY KEY(sale_id, sale_date)sale_date missing from SELECT — creation fails: SELECT sale_id, quantity FROM sales1

Expression columns

Every expression column in the SELECT list must have an alias. Use a meaningful alias, such as SUM(price) AS total_price.

Limitations

The following limitations apply to fast refresh only.

Unsupported expressions and clauses

  • Non-deterministic expressions (NOW(), RAND()) are not supported.

  • ORDER BY is not supported.

  • HAVING is not supported.

  • Window functions are not supported.

  • Set operations (UNION, EXCEPT, INTERSECT) are not supported.

JOIN constraints

Only INNER JOIN is supported. The join columns must meet all of the following conditions:

  • The columns are original columns of the tables (not derived expressions).

  • The columns have the same data type.

  • The columns have an index.

You can join up to 5 tables. To join more tables, contact technical support.

Aggregate function constraints

Only the following aggregate functions are supported: COUNT, SUM, MAX, MIN, AVG, APPROX_DISTINCT, and COUNT(DISTINCT).

Additional constraints:

  • AVG does not support the DECIMAL data type.

  • COUNT(DISTINCT) supports only the INTEGER data type.

Required permissions

To create a materialized view, you need all of the following permissions:

  • CREATE permission on the database where the materialized view will be created.

  • SELECT permission on the relevant columns or the entire base tables.

For auto-refresh materialized views, you also need:

  • Permission to connect to AnalyticDB for MySQL from any IP address ('%').

  • INSERT permission on the view itself, or on all tables in its database, to allow the refresh to write data.

Examples

Example preparation

All examples in this section use the following base tables. Run these statements first to create them.

/*+ RC_DDL_ENGINE_REWRITE_XUANWUV2=false */ -- Specify XUANWU as the table engine.
CREATE TABLE customer (
    customer_id INT PRIMARY KEY,
    customer_name VARCHAR(255),
    is_vip Boolean
);

/*+ RC_DDL_ENGINE_REWRITE_XUANWUV2=false */ -- Specify XUANWU as the table engine.
CREATE TABLE sales (
    sale_id INT PRIMARY KEY,
    product_id INT,
    customer_id INT,
    price DECIMAL(10, 2),
    quantity INT,
    sale_date TIMESTAMP
);

Complete refresh

Every 5 minutes

CREATE MATERIALIZED VIEW myview1
REFRESH   -- Equivalent to REFRESH COMPLETE.
 NEXT now() + INTERVAL 5 minute  -- Runs at creation time, then every 5 minutes.
AS
SELECT count(*) AS cnt FROM customer;

Daily at 02:00

CREATE MATERIALIZED VIEW myview2
REFRESH COMPLETE
 START WITH DATE_FORMAT(now() + INTERVAL 1 day, '%Y-%m-%d 02:00:00')  -- First run: tomorrow at 02:00.
 NEXT DATE_FORMAT(now() + INTERVAL 1 day, '%Y-%m-%d 02:00:00')        -- Then: every day at 02:00.
AS
SELECT count(*) AS cnt FROM customer;

Every Monday at 02:00

CREATE MATERIALIZED VIEW myview3
REFRESH COMPLETE ON DEMAND
 START WITH DATE_FORMAT(now() + INTERVAL 7 - weekday(now()) day, '%Y-%m-%d 02:00:00')  -- First run: next Monday at 02:00.
 NEXT DATE_FORMAT(now() + INTERVAL 7 - weekday(now()) day, '%Y-%m-%d 02:00:00')        -- Then: every Monday at 02:00.
AS
SELECT count(*) AS cnt FROM customer;

On the first day of each month at 02:00

CREATE MATERIALIZED VIEW myview4
REFRESH   -- Equivalent to REFRESH COMPLETE.
 NEXT DATE_FORMAT(last_day(now()) + INTERVAL 1 day, '%Y-%m-%d 02:00:00')  -- Runs at 02:00 on the 1st of the next month, then monthly.
AS
SELECT count(*) AS cnt FROM customer;

Once only

CREATE MATERIALIZED VIEW myview5
REFRESH   -- Equivalent to REFRESH COMPLETE.
 START WITH now() + INTERVAL 1 day  -- Runs once, 24 hours after creation. No further scheduled refreshes.
AS
SELECT count(*) AS cnt FROM customer;

Manual refresh only

CREATE MATERIALIZED VIEW myview6 (
  PRIMARY KEY (customer_id)
) DISTRIBUTED BY HASH (customer_id)
AS
SELECT customer_id FROM customer;

To refresh manually:

REFRESH MATERIALIZED VIEW myview6;

On INSERT OVERWRITE (triggered refresh)

Refreshes automatically when the base table is overwritten by an INSERT OVERWRITE statement. No schedule is needed.

CREATE MATERIALIZED VIEW myview7
REFRESH COMPLETE ON OVERWRITE
AS
SELECT count(*) AS cnt FROM customer;

Fast refresh — single-table

Before creating a materialized view with fast refresh, enable binary logging for the cluster and base tables:

SET ADB_CONFIG BINLOG_ENABLE=true;
ALTER TABLE customer binlog=true;
ALTER TABLE sales binlog=true;

Non-aggregate query, 10-second interval

CREATE MATERIALIZED VIEW fast_mv1
REFRESH FAST NEXT now() + INTERVAL 10 second
AS
SELECT sale_id, sale_date, price
FROM sales
WHERE price > 10;

Grouped aggregate query, 5-second interval

The GROUP BY columns (customer_id, sale_date) become the primary key of the materialized view.

CREATE MATERIALIZED VIEW fast_mv2
REFRESH FAST NEXT now() + INTERVAL 5 second
AS
SELECT
   customer_id, sale_date,                 -- GROUP BY columns; used as the primary key.
   COUNT(sale_id) AS cnt_sale_id,          -- Aggregate operation; alias required.
   SUM(price * quantity) AS total_revenue, -- Aggregate operation; alias required.
   customer_id / 100 AS new_customer_id    -- Expression column; alias required.
FROM sales
WHERE ifnull(price, 1) > 0                 -- Expressions are supported in WHERE conditions.
GROUP BY customer_id, sale_date;

Non-grouped aggregate query, 1-minute interval

The system generates a constant as the primary key because a non-grouped aggregate always produces a single row.

CREATE MATERIALIZED VIEW fast_mv3
REFRESH FAST NEXT now() + INTERVAL 1 minute
AS
SELECT count(*) AS cnt
FROM sales;

Fast refresh — multi-table

Non-aggregate JOIN, 5-second interval

CREATE MATERIALIZED VIEW fast_mv4
REFRESH FAST NEXT now() + INTERVAL 5 second
AS
SELECT
    c.customer_id,
    c.customer_name,
    s.sale_id,
    (s.price * s.quantity) AS revenue
FROM
    sales s
JOIN
    customer c ON s.customer_id = c.customer_id;

Grouped aggregate JOIN, 10-second interval

CREATE MATERIALIZED VIEW fast_mv5
REFRESH FAST NEXT now() + INTERVAL 10 second
AS
SELECT
    s.sale_id,
    c.customer_name,
    COUNT(*) AS cnt,
    SUM(s.price * s.quantity) AS revenue
FROM
    sales s
JOIN
    (SELECT customer_id, customer_name FROM customer) c ON c.customer_id = s.customer_id
GROUP BY
    s.sale_id, c.customer_name;

Partitioned materialized view

Creates a materialized view with a distribution key (sale_id) and a partition key (sale_date). All columns from SELECT * are included even if not listed in mv_definition.

CREATE MATERIALIZED VIEW myview8 (
  quantity INT,
  price DECIMAL(10, 2),
  sale_date TIMESTAMP
)
DISTRIBUTED BY HASH(sale_id)
PARTITION BY VALUE(date_format(sale_date, "%Y%m%d")) LIFECYCLE 30
AS
SELECT * FROM sales;

Custom schema definition

Index only specific columns

CREATE MATERIALIZED VIEW myview9 (
  INDEX (sale_date),
  PRIMARY KEY (sale_id)
) DISTRIBUTED BY HASH (sale_id)
REFRESH
 NEXT now() + INTERVAL 1 DAY
AS
SELECT * FROM sales;

Primary key, distribution key, clustered index, column-based index, and comment

CREATE MATERIALIZED VIEW myview10 (
  quantity INT,
  price DECIMAL(10, 2),
  KEY INDEX_ID(customer_id) COMMENT 'customer',
  CLUSTERED KEY INDEX(sale_id),
  PRIMARY KEY(sale_id, sale_date)
)
DISTRIBUTED BY HASH(sale_id)
COMMENT 'MATERIALIZED VIEW c'
AS
SELECT * FROM sales;

Elastic materialized view

An elastic materialized view uses a job resource group powered by the XIHE engine. Resources are provisioned on demand, which reduces idle compute costs but introduces higher refresh latency.

Use the serverless resource group, refresh daily

CREATE MATERIALIZED VIEW myview11
MV_PROPERTIES='{
  "mv_resource_group": "serverless"
}'
REFRESH COMPLETE ON DEMAND
 START WITH now()
 NEXT now() + INTERVAL 1 DAY
AS
SELECT * FROM sales;

Use the serverless resource group with a maximum of 12 ACUs

Set elastic_job_max_acu to increase the resource limit and improve refresh speed.

CREATE MATERIALIZED VIEW myview12
MV_PROPERTIES='{
  "mv_resource_group": "serverless",
  "mv_refresh_hints": {"elastic_job_max_acu": "12"}
}'
REFRESH COMPLETE ON DEMAND
 START WITH now()
 NEXT now() + INTERVAL 1 DAY
AS
SELECT * FROM sales;

What's next