All Products
Search
Document Center

AnalyticDB:Create a materialized view

Last Updated:Mar 28, 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 refreshFast (incremental) refresh
How it worksReplaces all data in the materialized viewApplies only the changes since the last refresh
Base tablesInternal tables, external tables, existing materialized views, and viewsInternal tables only
SQL supportFull SELECT syntaxSubset of SELECT (see Incremental MV query constraints)
Refresh triggersScheduled auto-refresh, auto-refresh on base table overwrite, or manual refreshScheduled auto-refresh only (interval: 5 seconds to 5 minutes)
Minimum version3.1.3.43.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;

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.

FeatureSupported?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 ALLYes (V3.2.5.0+)Requires special query structure (see below)
COUNT, SUM, MAX, MIN, AVG, APPROX_DISTINCT, COUNT(DISTINCT)YesAll other aggregate functions are not supported
AVG with DECIMALNoUse a different numeric type
COUNT(DISTINCT) with non-INTEGERNoSupports INTEGER type only
Window functionsNo
HAVING clauseNo
ORDER BY clauseNo
Nondeterministic expressions (NOW(), RAND())No
UNION, EXCEPT, INTERSECTNoUNION ALL is supported from V3.2.5.0
XUANWU_V2 tables as base tablesNo (before V3.2.6.0)XUANWU_V2 does not support binary logging
Partitioned tables as base tablesNo (before V3.2.3.0)
INSERT OVERWRITE or TRUNCATE on base tablesNo (before V3.2.3.1)Returns an error
MAX(), MIN(), APPROX_DISTINCT(), COUNT(DISTINCT) with DELETE/UPDATE/REPLACE/INSERT ON DUPLICATE KEY UPDATENoBase 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 table uses the XUANWU_V2 engine, which does not support binary logging. Because you cannot change a table's engine after creation, you must:

    1. Disable XUANWU_V2 (see options below).

    2. Recreate the table using the XUANWU engine.

    3. Migrate data from the XUANWU_V2 table to the new XUANWU table.

    4. Create the fast materialized view on the XUANWU table.

    To disable XUANWU_V2:

    • For tables created by DTS, zero-ETL, or console data synchronization — disable at the cluster level. All new tables will use XUANWU.

      SET adb_config RC_DDL_ENGINE_REWRITE_XUANWUV2=false;
    • For manually created tables — disable at the statement level. Only that specific CREATE TABLE statement uses XUANWU; others still use XUANWU_V2.

      /*+ RC_DDL_ENGINE_REWRITE_XUANWUV2=false */ CREATE 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