MaxCompute introduces the Delta Live Materialized View (Delta Live MV) feature to help you build simple, easy-to-use incremental update pipelines. This topic describes Delta Live MV operations in MaxCompute.
Introduction
Compared to a materialized view that uses a full refresh, a Delta Live Materialized View balances data freshness and compute cost. It fully leverages existing computation results and uses an intelligent incremental computation algorithm to reduce compute cost and improve data freshness.
This feature is currently in Invitational Preview. For usage details, see Usage instructions.
Architecture

Key benefits
The Delta Live MV feature in MaxCompute provides the following benefits:
Declarative SQL, maintenance-free, and automated data warehouse layering.
Simplified data warehouse architecture. A single computation logic and engine supports both incremental and full computation, providing both low latency and high throughput.
Cost-effective. It balances data freshness and cost, and efficiently handles unified incremental and full ETL processing.
Use cases
The Delta Live MV feature is suitable for the following scenarios:
Near-real-time processing for offline workloads
Evolve a T+1 data warehouse to a near-real-time data warehouse with minute-level latency.
Unified incremental and full processing
Near-real-time incremental computation on the current day's partition: Ideal for scenarios that require high data freshness and cost-effective computation.
(Optional) Backfill historical partitions: Archive or correct data for large-scale data analysis.
Comprehensive support for incremental computation with various types of SQL logic, including the following common SQL operators:
Two-stream INNER JOIN
Two-stream (LEFT/RIGHT) OUTER JOIN
Any AGGREGATE function (except for user-defined aggregate functions (UDAFs)), including functions without a GROUP BY clause.
WINDOW
TableFunctionScan
UNION ALL
FILTER/Project
SUBQUERY
Prerequisites
You have created a MaxCompute project.
Change Data Capture (CDC) is enabled on the source table. The following source table types are supported:
A Delta Table with the CDC feature enabled.
Another Delta Live MV. CDC is enabled by default for a Delta Live MV.
A Delta Live MV cannot use non-deterministic computation, such as the RAND function or user-defined functions (UDFs).
Create a Delta Live MV
Syntax
CREATE MATERIALIZED VIEW [IF NOT EXISTS][<project_name>.]<mv_name>
[LIFECYCLE <days>] -- Specifies the lifecycle.
[BUILD DEFERRED] -- Creates only the table schema without populating data upon creation.
[(<col_name> [COMMENT <col_comment>],...)] -- Specifies column comments.
[DISABLE REWRITE] -- Specifies whether the view is used for query rewrite.
[COMMENT <table comment>] -- Specifies the table comment.
[PARTITIONED ON/BY (<col_name> [, <col_name>, ...]) -- Creates the materialized view as a partitioned table.
[REFRESH EVERY <num> MINUTES/HOURS/DAYS] -- Sets the scheduled refresh interval.
TBLPROPERTIES(
"refresh_mode"="incremental"
[,"enable_auto_refresh"="true"] -- Specifies whether to enable auto-refresh.
[,"refresh_cron"="xx"] -- Uses a cron expression to configure interval-based, fixed-time, or mixed refresh schedules.
[,"refresh_job_settings"="xx"]
)
AS <select_statement>;The syntax for a Delta Live MV is mostly compatible with that of a standard materialized view, with the following key differences:
You cannot create a Delta Live MV as a clustered table.
You cannot set the
enable_auto_substituteparameter to true for a Delta Live MV. A Delta Live MV is a type of asynchronous materialized view, so the data from the base table might not be the latest version. This conflicts with the behavior expected whenenable_auto_substituteis set to true.
Parameters
Parameter | Required | Description |
project_name | No | The name of the project. |
mv_name | Yes | The name of the Delta Live MV. |
LIFECYCLE <days> | No | Specifies the data lifecycle. |
BUILD DEFERRED | No | Specifies that only the table schema is created, without populating data upon creation. |
col_name | No | The column name. |
col_comment | No | The column comment. |
DISABLE REWRITE | No | Prevents the view from being used for query rewrite. |
table comment | No | The table comment. |
REFRESH EVERY <num> MINUTES/HOURS/DAYS | No | Specifies the scheduled refresh interval. The minimum value is 1 minute. |
enable_auto_refresh | No | Specifies whether to enable auto-refresh.
|
refresh_mode | No | The refresh mode.
|
refresh_cron | No | Specifies a Cron expression to set the refresh frequency. You can configure interval-based, fixed-time, or mixed refresh schedules. The value is a string in QUARTZ Cron format. For more information, see Cron expression examples. Example: |
refresh_job_settings | No |
|
select_statement | Yes | The SQL query. |
Examples
Example 1: Create a simple Delta Live MV
Defines a Delta Live MV named mv1 that performs an incremental refresh automatically every 5 minutes. The source table is a Delta Table with CDC enabled.
CREATE MATERIALIZED VIEW IF NOT EXISTS mv1
REFRESH EVERY 5 MINUTES
TBLPROPERTIES("enable_auto_refresh"="true", "refresh_mode"="incremental")
AS
SELECT name, COUNT(*) FROM source GROUP BY name;Example 2: Create a tuned Delta Live MV
SET odps.task.major.version=sql_flighting_dlmv;
CREATE MATERIALIZED VIEW IF NOT EXISTS part_dlmv_department
PRIMARY KEY(dept_id) -- The primary key `dept_id` can be inferred from the SQL logic based on the base table's primary key.
-- Explicit declaration is typically not required. However, because partitioned MVs currently only support BUILD DEFERRED mode,
-- which does not support automatic key inference, you must declare the primary key explicitly.
LIFECYCLE 10
BUILD DEFERRED
PARTITIONED BY (pt)
TBLPROPERTIES('refresh_mode'='incremental',
'refresh_job_settings'='set odps.task.major.version=sql_flighting_dlmv;')
AS
SELECT *, get_setting('odps.custom.setting.department.pt') AS pt FROM t_department; Primary key requirements for MV refresh
Currently, a materialized view refresh requires the MV to have a primary key (PK). Unlike a PK Delta Table, an MV is generated from SQL logic. Whether you need to explicitly declare a primary key when creating an MV depends on the following situations:
When the primary key can be inferred from the SQL logic
For example, if a SQL statement contains GROUP BY key, the primary key of the MV is automatically inferred to be key and does not need to be explicitly declared. You can run the
DESC EXTENDED mvName;command to view the inferred primary key column.When the primary key cannot be inferred from the SQL logic
Method 1: Modify the MV's SQL logic by adding a
GROUP BYclause to enable automatic inference.Method 2: Explicitly declare the primary key. Note that the data must satisfy the uniqueness constraint. The system performs a best-effort check for data uniqueness during each incremental refresh. Support for primary key uniqueness checks during the initial build phase will be enhanced in a future release.
Special requirements for partitioned MVs
Partitioned MVs currently support only the
BUILD DEFERREDmode. In this mode, automatic primary key inference is not supported. Therefore, you must explicitly declare the primary key.
Partitioned Delta Live MV
Scenario 1: Use a partitioned Delta Live MV to represent incremental data for the current day and full historical data
SET odps.task.major.version=sql_flighting_dlmv;
CREATE MATERIALIZED VIEW IF NOT EXISTS part_dlmv_department
PRIMARY KEY(dept_id) -- The primary key `dept_id` can be inferred from the SQL logic based on the base table's primary key.
-- Explicit declaration is typically not required. However, because partitioned MVs currently only support BUILD DEFERRED mode,
-- which does not support automatic key inference, you must declare the primary key explicitly.
LIFECYCLE 10
BUILD DEFERRED
PARTITIONED BY (pt)
TBLPROPERTIES('refresh_mode'='incremental',
'refresh_job_settings'='set odps.task.major.version=sql_flighting_dlmv;')
AS
-- t_department is a near-real-time ingestion table that describes the incremental data for the current day.
SELECT *, get_setting('odps.custom.setting.department.pt') AS pt FROM t_department;
UNION ALL
-- history_t_department is a historical partitioned table that contains all historical data.
SELECT * FROM history_t_department;Scenario 2: Infer the Delta Live MV's primary key by usinggroup by key
-- PK Delta Table
CREATE TABLE dlmv_base_table(
key STRING NOT NULL PRIMARY KEY,
value BIGINT,
value2 BIGINT
)
STORED AS ALIORC
TBLPROPERTIES (
'transactional' = 'true',
'cdc.insert.into.passthrough.enable' = 'true',
'acid.cdc.mode.enable' = 'true',
'acid.cdc.build.async' = 'false'
);
CREATE MATERIALIZED VIEW dlmv_pt
PRIMARY KEY(value) -- The primary key `value` can be inferred from the SQL logic (GROUP BY value).
-- Explicit declaration is typically not required. However, because partitioned DLMVs currently support only the BUILD DEFERRED mode,
-- which does not support automatic key inference, you must declare the primary key explicitly.
BUILD DEFERRED
PARTITIONED BY (pt)
TBLPROPERTIES (
'refresh_mode' = 'incremental',
'enable_auto_refresh' = 'true'
)
AS SELECT *, get_setting('odps.custom.setting.dlmv_pt.pt') AS pt FROM (SELECT value, MAX(value2) FROM dlmv_base_table GROUP BY value) t;Scenario 3: Infer the Delta Live MV's primary key from the base table's primary key
-- PK Delta Table
CREATE TABLE dlmv_base_table(
key STRING NOT NULL PRIMARY KEY,
value BIGINT,
value2 BIGINT
)
STORED AS ALIORC
TBLPROPERTIES (
'transactional' = 'true',
'cdc.insert.into.passthrough.enable' = 'true',
'acid.cdc.mode.enable' = 'true',
'acid.cdc.build.async' = 'false'
);
CREATE MATERIALIZED VIEW dlmv_pt
PRIMARY KEY(key) -- The primary key `key` can be inferred from the SQL logic (derived from the base table's PK).
-- Explicit declaration is typically not required. However, because partitioned DLMVs currently support only the BUILD DEFERRED mode,
-- which does not support automatic key inference, you must declare the primary key explicitly.
BUILD DEFERRED
PARTITIONED BY (pt)
TBLPROPERTIES (
'refresh_mode' = 'incremental',
'enable_auto_refresh' = 'true'
)
AS
SELECT key, value, value2, get_setting('odps.custom.setting.dlmv_pt.pt') as pt FROM dlmv_base_table;Scenario 4: The Delta Live MV's primary key cannot be inferred
-- PK Delta Table
CREATE TABLE dlmv_base_table(
key STRING NOT NULL PRIMARY KEY,
value BIGINT,
value2 BIGINT
)
STORED AS ALIORC
TBLPROPERTIES (
'transactional' = 'true',
'cdc.insert.into.passthrough.enable' = 'true',
'acid.cdc.mode.enable' = 'true',
'acid.cdc.build.async' = 'false'
);
# 1. Explicitly declare the PK column.
CREATE MATERIALIZED VIEW dlmv_pt
PRIMARY KEY(value) -- The PK cannot be inferred from the SQL logic, but you know the data satisfies PK uniqueness. The PK must be declared explicitly.
BUILD DEFERRED
PARTITIONED BY (pt)
TBLPROPERTIES (
'refresh_mode' = 'incremental',
'enable_auto_refresh' = 'true'
)
AS
SELECT value, value2, get_setting('odps.custom.setting.dlmv_pt.pt') as pt FROM dlmv_base_table;
# 2. Modify the SQL logic to allow PK inference.
-- If you cannot guarantee the uniqueness of the 'value' column, modify the logic with a GROUP BY clause, as shown below:
CREATE MATERIALIZED VIEW dlmv_pt
PRIMARY KEY(value) -- The PK `value` can now be inferred from the SQL logic (GROUP BY value).
-- Explicit declaration is typically not required. However, because partitioned DLMVs currently support only the BUILD DEFERRED mode,
-- which does not support automatic key inference, you must declare the primary key explicitly.
BUILD DEFERRED
PARTITIONED BY (pt)
TBLPROPERTIES (
'refresh_mode' = 'incremental',
'enable_auto_refresh' = 'true'
)
AS SELECT value, MAX(value2), get_setting('odps.custom.setting.dlmv_pt.pt') as pt FROM dlmv_base_table GROUP BY value;Non-partitioned Delta Live MV
Scenario 1: Infer the Delta Live MV's primary key by usinggroup by key
-- PK Delta Table
CREATE TABLE dlmv_base_table(
key STRING NOT NULL PRIMARY KEY,
value BIGINT,
value2 BIGINT
)
STORED AS ALIORC
TBLPROPERTIES (
'transactional' = 'true',
'cdc.insert.into.passthrough.enable' = 'true',
'acid.cdc.mode.enable' = 'true',
'acid.cdc.build.async' = 'false'
);
CREATE MATERIALIZED VIEW dlmv
-- PRIMARY KEY(value) -- The PK `value` can be inferred from the SQL logic (GROUP BY value), so no explicit declaration is needed.
TBLPROPERTIES (
'refresh_mode' = 'incremental',
'enable_auto_refresh' = 'true'
)
AS SELECT value, MAX(value2) FROM dlmv_base_table GROUP BY value;Scenario 2: Infer the Delta Live MV's primary key from the base table's primary key
-- PK Delta Table
CREATE TABLE dlmv_base_table(
key STRING NOT NULL PRIMARY KEY,
value BIGINT,
value2 BIGINT
)
STORED AS ALIORC
TBLPROPERTIES (
'transactional' = 'true',
'cdc.insert.into.passthrough.enable' = 'true',
'acid.cdc.mode.enable' = 'true',
'acid.cdc.build.async' = 'false'
);
CREATE MATERIALIZED VIEW dlmv
-- PRIMARY KEY(key) -- The PK `key` can be inferred from the SQL logic (derived from the base table's PK), so no explicit declaration is needed.
TBLPROPERTIES (
'refresh_mode' = 'incremental',
'enable_auto_refresh' = 'true'
)
AS
SELECT key, value, value2 FROM dlmv_base_table;Scenario 3: The Delta Live MV's primary key cannot be inferred
-- PK Delta Table
CREATE TABLE dlmv_base_table(
key STRING NOT NULL PRIMARY KEY,
value BIGINT,
value2 BIGINT
)
STORED AS ALIORC
TBLPROPERTIES (
'transactional' = 'true',
'cdc.insert.into.passthrough.enable' = 'true',
'acid.cdc.mode.enable' = 'true',
'acid.cdc.build.async' = 'false'
);
# 1. Explicitly declare the PK column.
CREATE MATERIALIZED VIEW dlmv
PRIMARY KEY(value) -- The PK cannot be inferred from the SQL logic, but you know the data satisfies PK uniqueness. The PK must be declared explicitly.
TBLPROPERTIES (
'refresh_mode' = 'incremental',
'enable_auto_refresh' = 'true'
)
AS
SELECT value, value2 FROM dlmv_base_table;
# 2. Modify the SQL logic to allow PK inference.
-- If you cannot guarantee the uniqueness of the 'value' column, modify the logic with a GROUP BY clause, as shown below:
CREATE MATERIALIZED VIEW dlmv
-- PRIMARY KEY(value) -- The PK `value` can now be inferred from the SQL logic (GROUP BY value), so no explicit declaration is needed.
TBLPROPERTIES (
'refresh_mode' = 'incremental',
'enable_auto_refresh' = 'true'
)
AS SELECT value, MAX(value2) FROM dlmv_base_table GROUP BY value;Example 3: Refresh a single MV partition
When you create a partitioned Delta Live MV, you must include the BUILD DEFERRED keyword, which performs only the DDL operation.
-- Create the Delta Live MV.
CREATE MATERIALIZED VIEW dlmv_pt
PRIMARY KEY(value) BUILD DEFERRED PARTITIONED BY (ds) TBLPROPERTIES
('refresh_mode'='incremental', 'enable_auto_refresh'='true')
AS SELECT value, AVG(value2), ds FROM dlmv_pt_src GROUP BY value, ds;
-- Refresh a single partition.
ALTER MATERIALIZED VIEW dlmv_pt REBUILD PARTITION(ds='20250730');For more information about refreshing a Delta Live MV, see Manual refresh.
Example 4: Create a parameterized Delta Live MV
You can use parameterized definitions to migrate offline partition jobs to incremental jobs.
The
get_settingfunction retrieves parameter values set in Session Flags. The parameter name must be prefixed withodps.custom.setting.In traditional offline jobs, replace variables like
${biz_date}withget_setting(odps.custom.setting.xx)to parameterize the definition.Add the Session Flag
set odps.custom.setting.xx=yybefore the refresh statement for the Delta Live MV.During runtime, the MaxCompute optimizer automatically replaces
get_setting(odps.custom.setting.xx)in the Delta Live MV withyy.
The following is an example:
-- Create the Delta Live MV.
CREATE MATERIALIZED VIEW mv1
BUILD DEFERRED -- Performs only the DDL operation and does not populate data.
PARTITIONED BY (ds)
REFRESH EVERY 5 minutes
TBLPROPERTIES("enable_auto_refresh"="true", "refresh_mode"="incremental")
AS
SELECT A.* FROM A JOIN B ON A.c1 = B.c1
AND A.ds=get_setting('odps.custom.setting.bizdate.a')
AND B.ds=get_setting('odps.custom.setting.bizdate.b');
-- Refresh logic. DataWorks automatically replaces ${biz_date} and ${yesterday} during scheduling.
SET odps.custom.setting.bizdate.a=${biz_date};
SET odps.custom.setting.bizdate.b=${yesterday};
ALTER MATERIALIZED VIEW mv1 REBUILD PARTITION(ds=${biz_date});Manage a Delta Live MV
Drop a Delta Live MV
DROP MATERIALIZED VIEW [IF EXISTS] [<project_name>.]<mv_name>;Manual refresh
You can manually refresh a Delta Live MV. Only single-partition refreshes are supported. The syntax is the same as that for a standard materialized view:
ALTER MATERIALIZED VIEW [<project_name>.]<mv_name>
REBUILD [PARTITION(<ds>=max_pt(<table_name>),<expression1>...)];In this syntax, ds is the partition column.
Disable auto-refresh
Run the following command to modify the TBLPROPERTIES of the materialized view to disable the auto-refresh feature:
ALTER MATERIALIZED VIEW <mv_name> SET TBLPROPERTIES("enable_auto_refresh"="false");Resume auto-refresh
Run the following command to modify the TBLPROPERTIES of the materialized view to enable or resume auto-refresh:
ALTER MATERIALIZED VIEW <mv_name> SET TBLPROPERTIES("enable_auto_refresh"="true");Change the refresh frequency
Run the following command to change the refresh frequency of a Delta Live MV:
ALTER MATERIALIZED VIEW <mv_name>
SET TBLPROPERTIES("refresh_interval_minutes"="xx");The minimum value for the refresh_interval_minutes parameter is 1. We recommend that you set this value to be shorter than the CDC lifecycle of the base table.
View a Delta Live MV
View data change history
Run the following command to view the data change history of a Delta Live MV:
SHOW HISTORY FOR TABLE <mv_name>;Sample result:
ObjectType ObjectId ObjectName VERSION(LSN) Time Operation
TABLE d95ec7015e8b432e8e0092d01da962a9 incremental_mv 0000000000000001 2024-08-18 21:06:32 CREATE
TABLE d95ec7015e8b432e8e0092d01da962a9 incremental_mv 0000000000000002 2024-08-18 21:11:13 UPDATEView refresh history
Run the following command to view the refresh history of a Delta Live MV.
SELECT * FROM
Delta_Live_MV_Refresh_History(['<project_name>', '<schema_name>',]'<table_name>');Parameters
Parameter | Description |
project_name | The name of the project. |
schema_name | The name of the schema. |
table_name | The name of the table. |
Return values
Parameter | Description |
project_name | The project that contains the Delta Live MV. |
schema_name | The schema that contains the Delta Live MV. |
name | The name of the Delta Live MV. |
refresh_start_time | The time when the refresh started. |
refresh_end_time | The time when the refresh ended. If the job state is RUNNING, the value of this field is NULL. |
instance_id | The job ID. You can use this ID to obtain the Logview. |
duration_in_seconds | The duration of the refresh, in seconds. |
state | The job state.
|
refresh_trigger | The method that triggered the refresh.
|
refresh_mode | The refresh mode.
|
error_message | The error message if the refresh failed. If the refresh is successful, this field is NULL. |
source_tables | The names of the base tables used by the Delta Live MV and their corresponding versions. |
numInsertedRows | The number of rows that were inserted. |
numDeletedRows | The number of rows that were deleted. |
Billing
A Delta Live MV incurs compute and storage fees. The billing method is the same as that for standard materialized view operations.
Compute fees
Creating or refreshing a Delta Live MV may initiate computation jobs. These jobs consume computing resources and incur compute fees. The billing rules are the same as those for standard SQL jobs.
If the auto-refresh feature detects no data changes, it does not initiate a refresh job, and no fees are incurred.
Placing Delta Live MVs in a dedicated project makes it easier to track auto-refresh jobs and their resource consumption and fees.
Storage fees
Storage for a Delta Live MV is billed in the same way as for a standard materialized view or a regular table.
For certain operators, a Delta Live MV may use a state-based incremental computation algorithm, which generates internal state tables that consume additional storage space.
A Delta Live MV requires storage overhead for incremental CDC and Time Travel. This storage overhead is similar to that of a standard Delta Table.