Delta Live MV

Updated at:

A Delta Live Materialized View (Delta Live MV) allows you to build simple incremental update pipelines. This topic describes how to use Delta Live MVs in MaxCompute.

Overview

Compared to a materialized view that requires a full refresh, a Delta Live MV balances data freshness and compute cost. It intelligently reuses existing computation results and applies incremental computation algorithms to reduce compute costs and improve data freshness.

Architecture

image

Key advantages

MaxCompute Delta Live MVs provide the following advantages:

  • Enables data warehouse layering with declarative, fully managed, and automated processes.

  • Simplified data warehouse architecture: A single set of compute logic and one engine supports both incremental and full computation, meeting demands for both low latency and high throughput.

  • Cost-effective: Balances data freshness and compute cost, and efficiently handles unified incremental and full processing.

Use cases

The Delta Live MV feature is suitable for the following use cases:

  • Near-real-time data warehouse

    Enables the evolution from 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 for high data freshness and cost-effectiveness.

    • (Optional) Backfill historical partitions for data archiving, correction, and large-scale data analysis.

  • Comprehensive support for incremental computation with various SQL constructs, including the following common SQL operators:

    • Two-stream INNER JOIN

    • Two-stream LEFT/RIGHT OUTER JOIN

    • All AGGREGATE functions (except UDAFs), including those without GROUP BY or AGG.

    • WINDOW

    • TableFunctionScan

    • UNION ALL

    • FILTER/Project

    • SUBQUERY

Prerequisites

  • You have a MaxCompute project.

  • The source table must have Change Data Capture (CDC) enabled. The following source table types are supported:

    • A Delta Table with the CDC feature explicitly enabled.

    • Another Delta Live MV. CDC is enabled by default for Delta Live MVs.

  • A Delta Live MV cannot contain non-deterministic computations, such as the RAND function or UDFs.

Create a Delta Live MV

Syntax

CREATE MATERIALIZED VIEW [IF NOT EXISTS][<project_name>.]<mv_name>
[LIFECYCLE <days>]    --Specify the lifecycle.
[BUILD DEFERRED]    --Create only the table schema without populating data.
[(<col_name> [COMMENT <col_comment>],...)]    --Column comment.
[DISABLE REWRITE]    --Disables query rewrite for this view.
[COMMENT <table comment>]    --Table comment.
[PARTITIONED ON/BY (<col_name> [, <col_name>, ...])    --Create the materialized view as a partitioned table.
[REFRESH EVERY <num> MINUTES/HOURS/DAYS] --Set the scheduled refresh interval for the materialized view.
TBLPROPERTIES(
  "refresh_mode"="incremental"
  [,"enable_auto_refresh"="true"]    --Specify whether to enable auto-refresh.
  [,"refresh_cron"="xx"]             --Configure scheduled interval, point-in-time, or combined refreshes by using a cron expression.
  [,"refresh_job_settings"="xx"]
              )
AS <select_statement>;

The syntax for a Delta Live MV is compatible with that of a standard materialized view, with the following differences:

  • A Delta Live MV cannot be created as a clustered table.

  • Theenable_auto_substitute parameter cannot be set to true for a Delta Live MV. A Delta Live MV is an asynchronous materialized view, so the data from the base table might not be the latest version. This conflicts with the behavior whenenable_auto_substitute is set to true.

Parameters

Parameter

Required

Description

project_name

No

The project name.

mv_name

Yes

The name of the Delta Live MV.

LIFECYCLE <days>

No

The data lifecycle in days.

BUILD DEFERRED

No

Creates the table schema without generating data.

col_name

No

The column name.

col_comment

No

The column comment.

DISABLE REWRITE

No

Disables query rewrite for the view.

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.

  • true: Enables auto-refresh.

  • false: Disables auto-refresh.

refresh_mode

No

The refresh mode.

  • full: full refresh.

  • incremental: incremental refresh.

refresh_cron

No

A QUARTZ Cron expression to set the refresh frequency. You can configure interval-based, point-in-time, or a combination of refresh schedules.

The value is a string in QUARTZ Cron format. For more information, see Cron expression examples. The following is an example:

TBLPROPERTIES(
  "enable_auto_refresh"="true",
  "refresh_cron"="xx"
)

refresh_job_settings

No

  • Sets general tuning parameters that are automatically applied during a refresh. The following is an example:

    'refresh_job_settings'='set odps.sql.split.size=128;set odps.sql.reshuffle.dynamicpt
    =false;'
  • Flags set with this parameter have higher priority than flags in the current session.

select_statement

Yes

The SQL query statement.

Examples

Example 1: Create a simple Delta Live MV

Define 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 view with tuning parameters

SET odps.task.major.version=sql_flighting_dlmv;

CREATE MATERIALIZED VIEW IF NOT EXISTS part_dlmv_department
PRIMARY KEY(dept_id) -- The primary key can be inferred from the SQL logic. However, because partitioned MVs only support BUILD DEFERRED mode, which requires an explicitly declared primary key, you must declare it here.
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:

To support refreshes, a Delta Live MV must have a primary key (PK). Whether you must explicitly declare a PK depends on the situation:

  • When the primary key can be automatically inferred from the SQL logic

    For example, if the SQL contains a GROUP BY key, the MV's primary key is automatically inferred as key, and you do not need to explicitly declare it. You can view the inferred primary key column by running theDESC EXTENDED mvName; command.

  • When the primary key cannot be automatically inferred from the SQL logic

    • Method 1: Modify the MV's SQL logic by adding a GROUP BY clause to meet the automatic inference condition.

    • Method 2: Explicitly declare the primary key. The data itself must satisfy the uniqueness constraint. The system attempts to verify data uniqueness for each incremental refresh. The uniqueness check for the initial data load is being optimized and will be available in a future release.

  • Special requirements for partitioned MVs

    A partitioned MV currently supports only the BUILD DEFERRED mode, which does not support automatic primary key inference. 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 data for historical records.

SET odps.task.major.version=sql_flighting_dlmv;

CREATE MATERIALIZED VIEW IF NOT EXISTS part_dlmv_department
PRIMARY KEY(dept_id) -- The primary key can be inferred from the SQL logic. However, because partitioned MVs only support BUILD DEFERRED mode, which requires an explicitly declared primary key, you must declare it here.
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 mainly contains 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 containing all historical data.
SELECT * FROM history_t_department;

Scenario 2: Use group by key to derive the PK column of a dlmv.

// 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 can be inferred from the SQL logic. However, because partitioned MVs only support BUILD DEFERRED mode, which requires an explicitly declared primary key, you must declare it here.
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 primary key column from the primary key of the base table.

// 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 can be inferred from the SQL logic. However, because partitioned MVs only support BUILD DEFERRED mode, which requires an explicitly declared primary key, you must declare it here.
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 primary key column 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 primary key column
CREATE MATERIALIZED VIEW dlmv_pt
primary key(value) -- The primary key column cannot be inferred as 'value' from the SQL logic, but the user knows the data itself satisfies the primary key uniqueness. The primary key must be explicitly declared.
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 primary key inference.
-- If the uniqueness of the 'value' column cannot be guaranteed, the logic can be modified with a group by clause, as shown below:
CREATE MATERIALIZED VIEW dlmv_pt
primary key(value) -- The primary key can be inferred from the SQL logic. However, because partitioned MVs only support BUILD DEFERRED mode, which requires an explicitly declared primary key, you must declare it here.
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: By using group by key, you can derive the PK column of the DLMV.

// 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) can be inferred from the SQL logic (group by value), so explicit declaration is not 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 primary key column from the primary key of the base table.

// 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) can be inferred from the SQL logic (derived from the base table's primary key), so explicit declaration is not needed.
TBLPROPERTIES (
    'refresh_mode'        = 'incremental',
    'enable_auto_refresh' = 'true'
)
AS
SELECT key, value, value2 FROM dlmv_base_table;

Scenario 3: The Delta Live MV primary key column 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 primary key column
CREATE MATERIALIZED VIEW dlmv
primary key(value) -- The primary key column cannot be inferred as 'value' from the SQL logic, but the user knows the data itself satisfies the primary key uniqueness. The primary key must be explicitly declared.
TBLPROPERTIES (
    'refresh_mode'        = 'incremental',
    'enable_auto_refresh' = 'true'
)
AS
SELECT value, value2 FROM dlmv_base_table;

# 2. Modify the SQL logic to allow primary key inference
-- If the uniqueness of the 'value' column cannot be guaranteed, the logic can be modified with a group by clause, as shown below:
CREATE MATERIALIZED VIEW dlmv
-- primary key(value) can be inferred from the SQL logic (group by value), so explicit declaration is not 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 partition

When you create a partitioned Delta Live MV, you must add the BUILD DEFERRED keyword to indicate that only DDL operations are performed.

-- 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');
Note

For more information about how to refresh a Delta Live MV, see Refresh a Delta Live MV manually.

Example 4: Use parameterized definitions

Using parameterized definitions can help you migrate offline partitioned jobs to incremental ones.

  • The get_setting function is supported for obtaining the values of parameters set in the session flag. The parameters must be prefixed with dps.custom.setting.

  • Replace ${biz_date} in traditional offline jobs with get_setting(odps.custom.setting.xx) to complete the parameterization.

  • Add the session flag set odps.custom.setting.xx=yy before the Delta Live MV refresh statement.

  • At runtime, the MaxCompute optimizer automatically replaces get_setting(odps.custom.setting.xx) in a Delta Live MV with yy.

Example:

-- Create the Delta Live MV.
CREATE MATERIALIZED VIEW mv1 
BUILD DEFERRED -- DDL only; no data is generated.
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 scheduling automatically replaces ${biz_date} and ${yesterday}.
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 refresh a Delta Live MV manually. Manual refreshes support only single partitions.

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

To disable the auto-refresh feature, run the following command:

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");
Note

The minimum value for the refresh_interval_minutes parameter is 1. We recommend that you set this value to be less 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 records 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     UPDATE

View 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 project name.

schema_name

The schema name.

table_name

The table name.

Return values

Field

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's state is RUNNING, this field is NULL.

instance_id

The job ID. You can use this ID to open Logview.

duration_in_seconds

The refresh duration.

state

The job state.

  • RUNNING

  • TERMINATED

  • FAILED

  • CANCELLED

refresh_trigger

The refresh method.

  • MANUAL: The refresh was triggered manually by a user or scheduled by using DataWorks.

  • SYSTEM_SCHEDULED: The refresh is triggered by the internal scheduler of MaxCompute.

refresh_mode

The refresh mode.

  • FULL

  • INCREMENTAL

  • NO_DATA

error_message

Information about a refresh failure. If the refresh is successful, this value is NULL.

source_tables

The names and versions of the base tables used for the refresh.

numInsertedRows

The number of inserted rows.

numDeletedRows

The number of deleted rows.

Billing

Delta Live MVs incur fees for both computing and storage. The billing method is the same as that for regular materialized view operations.

  • Compute fees

    • When you create or refresh a Delta Live MV, if a job is started to compute data, it consumes compute resources and incurs compute fees. The billing rules are the same as for standard SQL jobs.

    • If an auto-refresh is triggered but there are no data changes, MaxCompute does not start a refresh job, and you incur no fees.

    • We recommend that you place Delta Live MVs in a dedicated project to easily track auto-refresh jobs, compute resource usage, and costs.

  • Storage fees

    • Delta Live MVs are billed for storage in the same way as standard materialized views and regular tables.

    • For some operators, a Delta Live MV may use a state-based incremental computation algorithm, which generates internal state tables and consumes additional storage.

    • A Delta Live MV incurs storage overhead for CDC and Time Travel. This overhead is similar to that of a standard Delta Table.