All Products
Search
Document Center

AnalyticDB:Create a materialized view

Last Updated:Jul 10, 2026

Materialized views pre-compute and store query results, so AnalyticDB for MySQL can serve those results directly instead of re-running expensive multi-table joins and aggregations on every query. This topic explains how to create a materialized view, choose a refresh type, and resolve common errors.

Prerequisites

Before you begin, make sure that:

  • The cluster's kernel version is 3.1.3.4 or later. To check or update the version, go to the Configuration Information section on the Cluster Information page in the AnalyticDB for MySQL console

  • Your database account has all required permissions:

    • CREATE permission on tables in the target database

    • SELECT permission on all columns (or specific columns) of every base table the materialized view references

    • For automatically refreshed materialized views, additionally:

      • Permission to connect from '%' (any IP address)

      • INSERT permission on the materialized view or all tables in its database

Choose a refresh type

AnalyticDB for MySQL supports two refresh types. The refresh type determines which SQL features the query body can use and how the materialized view stays in sync with its base tables.

Complete refresh

Fast (incremental) refresh

How it works

Replaces all data in the materialized view

Applies only the changes since the last refresh

Base tables

Internal tables, external tables, existing materialized views, and views

Internal tables only

SQL support

Full SELECT syntax

Subset of SELECT (see Incremental MV query constraints)

Refresh triggers

Scheduled auto-refresh, auto-refresh on base table overwrite, or manual refresh

Scheduled auto-refresh only (interval: 5 seconds to 5 minutes)

Minimum version

3.1.3.4

3.1.9.0 (single-table); 3.2.1.0 (multi-table)

Prepare base tables

The examples in this topic use the following two tables. Create them before running the materialized view examples.

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

/*+ RC_DDL_ENGINE_REWRITE_XUANWUV2=false */ -- Set the table engine to XUANWU.
CREATE TABLE sales (
    sale_id INT PRIMARY KEY,
    product_id INT,
    customer_id INT,
    price DECIMAL(10, 2),
    quantity INT,
    sale_date TIMESTAMP
);
The examples do not specify a resource group. Without a resource group, AnalyticDB for MySQL uses the reserved computing resources of the default interactive resource group to create and refresh materialized views. To use a job resource group instead, see Use elastic resources.

Create a complete-refresh materialized view

Complete-refresh materialized views support the full SELECT syntax and can reference internal tables, external tables, existing materialized views, and views.

The following example creates a materialized view named join_mv that joins customer and sales and is configured for manual refresh:

CREATE MATERIALIZED VIEW join_mv
REFRESH COMPLETE ON DEMAND
AS
SELECT
  sale_id,
  SUM(price * quantity) AS price
FROM customer
INNER JOIN (SELECT sale_id, customer_id, price, quantity FROM sales) sales
  ON customer.customer_id = sales.customer_id
GROUP BY customer.customer_id;

To refresh the view manually, run:

REFRESH MATERIALIZED VIEW join_mv;

Create a fast (incremental) materialized view

Fast materialized views apply only the changes since the last refresh, which is more efficient for large datasets that change incrementally. The trade-off is a narrower set of supported SQL features — what you write in the SELECT query body determines whether incremental refresh applies (see Incremental MV query constraints).

Version requirement: 3.1.9.0 or later (single-table), 3.2.1.0 or later (multi-table).

Enable binary logging

Incremental refresh relies on binary logging to track changes in base tables. Enable it at the cluster level and for each base table:

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

If enabling binary logging fails, see Can not create FAST materialized view.

Create a single-table fast materialized view

The following example creates a fast materialized view named sales_mv_incre on the sales table with an auto-refresh interval of 3 minutes:

CREATE MATERIALIZED VIEW sales_mv_incre
REFRESH FAST NEXT now() + INTERVAL 3 minute
AS
SELECT
  sale_id,
  SUM(price * quantity) AS price
FROM sales
GROUP BY sale_id;

Create a multi-table fast materialized view

On clusters running V3.2.1.0 or later, fast materialized views can join multiple tables. The following example joins customer and sales:

CREATE MATERIALIZED VIEW join_mv_incre
REFRESH FAST NEXT now() + INTERVAL 3 minute
AS
SELECT
  customer.customer_id,
  SUM(sales.price) AS price
FROM customer
INNER JOIN (SELECT customer_id, price FROM sales) sales
  ON customer.customer_id = sales.customer_id
GROUP BY customer.customer_id;

Note

If a base table is subject to a TRUNCATE or INSERT OVERWRITE operation, or an internal anomaly that may affect data correctness occurs, an incremental materialized view automatically falls back to a one-time complete refresh. After the complete refresh succeeds, subsequent refreshes automatically resume in incremental mode.

For the complete CREATE MATERIALIZED VIEW syntax, see CREATE MATERIALIZED VIEW.

Monitor creation progress

A CREATE MATERIALIZED VIEW statement can take time because it includes an initial data load. Run the following query to list materialized views currently being created:

SHOW PROCESSLIST WHERE info LIKE '%CREATE MATERIALIZED VIEW%';

Each row represents a materialized view in progress. The User field shows the database account, State shows the current status, and Info contains the full CREATE statement. For field definitions, see SHOW PROCESSLIST.

View sample results

Sample output:

+-------+-------------------+-------+--------------------+-------+-------------------+----+---------+-------------------------------+
|Id     |ProcessId          |User   |Host                |DB     |Command            |Time|State    |Info                           |
+-------+-------------------+-------+--------------------+-------+-------------------+----+---------+-------------------------------+
|31801  |20250127144727...  |wenjun |21.17.xx.xx:49534   |demo1  |INSERT_FROM_SELECT |2   |RUNNING  |CREATE MATERIALIZED VIEW join_mv|
|       |                   |       |                    |       |                   |    |         |REFRESH COMPLETE ON DEMAND     |
|       |                   |       |                    |       |                   |    |         |AS ...                         |
+-------+-------------------+-------+--------------------+-------+-------------------+----+---------+-------------------------------+

When SHOW PROCESSLIST returns no rows, the materialized view has been created, including its schema and initial data.

Use elastic resources to create or refresh materialized views

By default, AnalyticDB for MySQL uses the reserved computing resources of the default interactive resource group (user_default) to create and refresh materialized views. To avoid contending with interactive workloads, use a job resource group instead.

When to use elastic resources:

  • You want to isolate materialized view creation and refresh from interactive queries.

  • You want to avoid purchasing dedicated resources in advance.

Trade-off: Job resource groups provision computing resources on demand, which adds seconds to minutes of startup overhead before each refresh compared to interactive resource groups.

Cluster requirements:

  • Enterprise Edition, Basic Edition, or Data Lakehouse Edition

  • V3.1.9.3 or later

Specify a job resource group using MV_PROPERTIES. The following example creates a materialized view using the job resource group my_job_rg at high priority, with daily refresh:

CREATE MATERIALIZED VIEW job_mv
MV_PROPERTIES='{
  "mv_resource_group": "my_job_rg",
  "mv_refresh_hints": {"query_priority": "HIGH"}
}'
REFRESH COMPLETE ON DEMAND
START WITH now()
NEXT now() + INTERVAL 1 DAY
AS
SELECT * FROM customer;

To cap the maximum resources used during refresh, add "elastic_job_max_acu": "<value>" to mv_refresh_hints. For the full list of mv_properties options, see mv_properties.

Refresh trigger mechanisms

A materialized view reflects its data as of the last refresh, not the current state of the base tables. Choose a refresh trigger based on how fresh the data needs to be:

  • Scheduled auto-refresh: Refreshes at a fixed interval (for example, every 3 minutes, every day). Supported by both complete and fast refresh.

  • Auto-refresh on base table overwrite: Triggers a refresh when a base table is overwritten. Supported by complete refresh only.

  • Manual refresh: Run REFRESH MATERIALIZED VIEW <view_name>; when needed. Supported by complete refresh only.

For details on refresh policies and manual refresh syntax, see Refresh materialized views.

Incremental MV query constraints

The SELECT query body of a fast materialized view has constraints that do not apply to complete-refresh views. The most disqualifying constraint is:

Important

Only INNER JOIN is supported. Join columns must be original columns of the base tables, have identical data types, and be indexed. You can join up to five base tables.

If a constraint prevents your use case, use a complete-refresh materialized view instead — it supports the full SELECT syntax with no join restrictions.

The following table lists all unsupported SQL features.

Feature

Supported?

Notes

INNER JOIN (multi-table)

Yes (V3.2.1.0+)

Up to 5 tables; join columns must be indexed and have matching data types

UNION ALL

Yes (V3.2.5.0+)

Requires special query structure (see below)

COUNT, SUM, MAX, MIN, AVG, APPROX_DISTINCT, COUNT(DISTINCT)

Yes

All other aggregate functions are not supported

AVG with DECIMAL

No

Use a different numeric type

COUNT(DISTINCT) with non-INTEGER

No

Supports INTEGER type only

Window functions

No

HAVING clause

No

ORDER BY clause

No

Nondeterministic expressions (NOW(), RAND())

No

UNION, EXCEPT, INTERSECT

No

UNION ALL is supported from V3.2.5.0

XUANWU_V2 tables as base tables

No (before V3.2.6.0)

XUANWU_V2 supports binary logging from V3.2.6.0

Partitioned tables as base tables

No (before V3.2.3.0)

INSERT OVERWRITE or TRUNCATE on base tables

No (before V3.2.3.1)

Returns an error

MAX(), MIN(), APPROX_DISTINCT(), COUNT(DISTINCT) with DELETE/UPDATE/REPLACE/INSERT ON DUPLICATE KEY UPDATE

No

Base tables support INSERT only

Fast MV as a base table (nested fast MVs)

Yes (V3.2.5.0+)

Requires binary logging enabled on the materialized view

To join more than five tables in a fast materialized view, contact technical support.

SELECT column rules

The columns in the SELECT list must follow these rules:

  • With GROUP BY and aggregate functions: Include all GROUP BY columns in the SELECT list.

    View examples

    Correct example (all GROUP BY columns included):

    CREATE MATERIALIZED VIEW demo_mv1
    REFRESH FAST ON DEMAND
     NEXT now() + INTERVAL 3 minute
    AS
    SELECT
      sale_id,    -- GROUP BY column
      sale_date,  -- GROUP BY column
      max(quantity) AS max,  -- expression columns require aliases
      sum(price) AS sum
    FROM sales
    GROUP BY sale_id, sale_date;

    Incorrect (missing GROUP BY column sale_date):

    CREATE MATERIALIZED VIEW false_mv1
    REFRESH FAST ON DEMAND
     NEXT now() + INTERVAL 3 minute
    AS
    SELECT
      sale_id,
      max(quantity) AS max,
      sum(price) AS sum
    FROM sales
    GROUP BY sale_id, sale_date;  -- sale_date is in GROUP BY but not in SELECT
  • With aggregate functions and no GROUP BY: The SELECT list may contain only aggregate columns, or only constant and aggregate columns.

    View examples

    -- Aggregate columns only
    CREATE MATERIALIZED VIEW demo_mv2
    REFRESH FAST ON DEMAND
     NEXT now() + INTERVAL 3 minute
    AS
    SELECT
      max(quantity) AS max,
      sum(price) AS sum
    FROM sales;
    
    -- Constant plus aggregate columns (the constant becomes the primary key)
    CREATE MATERIALIZED VIEW demo_mv3
    REFRESH FAST ON DEMAND
     NEXT now() + INTERVAL 3 minute
    AS
    SELECT
      1 AS pk,
      max(quantity) AS max,
      sum(price) AS sum
    FROM sales;
  • Without aggregation: Include all primary key columns of the base table.

    View examples

    -- Single primary key
    CREATE MATERIALIZED VIEW demo_mv4
    REFRESH FAST ON DEMAND
     NEXT now() + INTERVAL 3 minute
    AS
    SELECT
      sale_id,   -- primary key column of base table sales
      quantity
    FROM sales;
    
    -- Composite primary key: PRIMARY KEY(sale_id, sale_date)
    CREATE MATERIALIZED VIEW demo_mv5
    REFRESH FAST ON DEMAND
     NEXT now() + INTERVAL 3 minute
    AS
    SELECT
      sale_id,    -- first primary key column
      sale_date,  -- second primary key column
      quantity
    FROM sales1;
  • UNION ALL queries: Each branch must output a column named union_all_marker with a distinct constant value per branch. Include all base table primary key columns. The materialized view primary key must include both the base table primary key columns and union_all_marker.

    CREATE MATERIALIZED VIEW demo_union_all_mv (PRIMARY KEY(id, union_all_marker))
    REFRESH FAST NEXT now() + INTERVAL 5 minute
    AS
    SELECT customer_id AS id, "customer" AS union_all_marker
    FROM customer
    UNION ALL
    SELECT sale_id AS id, "sales" AS union_all_marker
    FROM sales;
  • Expression columns: All expression columns in the SELECT list must have aliases, for example SUM(price) AS total_price.

Limits

General limits

These limits apply to all materialized views.

  • You cannot run INSERT, DELETE, or UPDATE on a materialized view.

  • You cannot drop or rename a base table or its columns while a materialized view references them. Drop the materialized view first, then modify the base table.

  • Maximum materialized views per cluster:

    • V3.1.4.7 or later: 64

    • Earlier than V3.1.4.7: 8

To raise the quota, contact technical support.

Limits on complete-refresh materialized views

When you add or remove reserved nodes, asynchronous jobs are disabled. Because complete refresh is an asynchronous job, it cannot run during node scaling. Fast refresh is not affected.

Limits on fast (incremental) materialized views

See Incremental MV query constraints for the full list of SQL restrictions.

Refresh trigger: Only scheduled auto-refresh is supported. The interval must be between 5 seconds and 5 minutes.

Limitations of multi-table joins in incremental materialized views:

  • Up to five base tables can be joined.

  • To adjust this limit, to contact technical support based on your cluster specifications.
  • Only INNER JOIN is supported.

  • Join columns must be original columns of base tables, have identical data types, and be indexed.

FAQ

How do I keep only the most recent year of data in a materialized view?

Use a date column as the partition key (PARTITION BY) and set a LIFECYCLE value to limit how many partitions are retained. For a view partitioned by day, set LIFECYCLE 365 to keep the most recent 365 partitions (one year).

Example: the sales table receives new records daily, partitioned by sale_date:

CREATE MATERIALIZED VIEW sales_mv_lifecycle
PARTITION BY VALUE(DATE_FORMAT(sale_date, '%Y%m%d')) LIFECYCLE 365
REFRESH FAST NEXT now() + INTERVAL 100 second
AS
SELECT
  sale_date,
  SUM(price * quantity) AS price
FROM sales
GROUP BY sale_date;

Troubleshooting

Query execution error: Can not create FAST materialized view, because *demotable* doesn't support getting incremental data

Binary logging is not enabled for the base table demotable. Enable it with:

ALTER TABLE demotable binlog=true;

If you see XUANWU_V2 engine not support ALTER_BINLOG_ENABLE now, the base table uses the XUANWU_V2 engine and the cluster runs a kernel version earlier than V3.2.6.0. XUANWU_V2 supports binary logging from kernel version V3.2.6.0. Upgrade the cluster to V3.2.6.0 or later, and then run ALTER TABLE demotable binlog=true; again to enable binary logging on the base table.

Query execution error: PRIMARY KEY *id* must output to MV

Your fast materialized view uses a non-aggregate query without GROUP BY. In this case, the SELECT list must include all primary key columns of the base table.

Incorrect (missing sale_id, which is the primary key of sales):

CREATE MATERIALIZED VIEW wrong_example1
REFRESH FAST ON DEMAND
 NEXT now() + interval 200 second
AS
SELECT product_id, price
FROM sales;

Add the primary key column:

CREATE MATERIALIZED VIEW correct_example1
REFRESH FAST ON DEMAND
 NEXT now() + interval 200 second
AS
SELECT sale_id, product_id, price
FROM sales;

Query execution error: MV PRIMARY KEY must be equal to base table PRIMARY KEY

Your fast materialized view uses a non-aggregate query without GROUP BY, and the materialized view primary key definition includes columns that are not part of the base table primary key.

Incorrect (product_id is not a primary key column of sales):

CREATE MATERIALIZED VIEW wrong_example2
(PRIMARY KEY(sale_id, product_id))
REFRESH FAST ON DEMAND
 NEXT now() + interval 200 second
AS
SELECT sale_id, product_id, price
FROM sales;

Remove non-primary-key columns from the primary key definition:

CREATE MATERIALIZED VIEW correct_example2
(PRIMARY KEY(sale_id))
REFRESH FAST ON DEMAND
 NEXT now() + interval 200 second
AS
SELECT sale_id, product_id, price
FROM sales;

Query execution error: FAST materialized view must define PRIMARY KEY

This error has two possible causes:

  • No valid primary key defined. Update the materialized view definition to meet these rules:

    • Grouped aggregate query (with GROUP BY): the primary key must be the GROUP BY columns (for example, if GROUP BY a, b, set PRIMARY KEY(a, b)).

    • Non-grouped aggregate query (without GROUP BY): the primary key must be a constant.

    • Non-aggregate query: the primary key must exactly match the base table primary key (for example, if the base table has PRIMARY KEY(sale_id, sale_date), the view must also have PRIMARY KEY(sale_id, sale_date)).

  • A function is applied to a primary key column. Remove the function from the primary key column in the query.

Query execution error: The join graph is not supported

Join columns have mismatched data types. For example, if customer.id and sales.id have different types, the join fails. Align the types with:

ALTER TABLE tablename MODIFY COLUMN columnname newtype;

For more information, see Change the data type of a column.

Query execution error: Unable to use index join to refresh this fast MV

Join columns lack indexes. Add an index on each join column:

ALTER TABLE tablename ADD KEY idx_name(columnname);

For more information, see Create an index.

Query execution error: Query exceeded reserved memory limit

The query exceeded the per-node memory limit. Use the SQL diagnostics feature to identify high-memory stages and operators (Aggregation, TopN, Window, and Join are the common culprits), then optimize those operators. See also Memory metrics and Use stage and task details to analyze queries.

What's next