Delta Live Materialized View (DLMV) quickstart

Updated at:

This topic provides an end-to-end example of how a Delta Live Materialized View (DLMV) automatically refreshes based on incremental changes (INSERT, UPDATE, and DELETE) in a source table, without requiring a full recomputation.

A Delta Live Materialized View (DLMV) is an incremental materialized view feature in MaxCompute. Unlike traditional materialized views that perform a full refresh, a DLMV captures Change Data Capture (CDC) data from a source table and processes only the incremental changes. This makes it suitable for near-real-time data processing scenarios with minute-level latency.

To learn about its working principles and key advantages, see Overview.

Prerequisites

Tools

Tool

Description

DataWorks (Recommended)

A visual SQL development platform that supports online editing and execution.

MaxCompute client (odpscmd)

A command-line tool suitable for scripted operations.

SQL Analysis

A lightweight SQL editor embedded in the MaxCompute console.

Procedure

Set the engine version (required for each execution)

The DLMV feature requires a specific SQL engine version. Before executing any DLMV-related SQL statements, you must run the following command:

SET odps.task.major.version = sql_flighting_dlmv;

How to verify the version is active: After running the set statement, if subsequent SQL statements execute without an error such as ODPS-0123XXX, the version is active. If an error indicates that the version is not supported or the feature is not enabled, submit a ticket to contact us.

Step 1: Create a source table

The source table for a DLMV must be a Delta Table (a transactional table) with Change Data Capture (CDC) enabled and a PRIMARY KEY defined. The DLMV uses the primary key to locate the target rows for incremental changes.

Run the following SQL statement to create the source table.

-- Create the source table.
CREATE TABLE IF NOT EXISTS t_department (
  dept_id     BIGINT NOT NULL PRIMARY KEY,
  name        STRING,
  description STRING
)
TBLPROPERTIES (
  'transactional'                      = 'true',
  'acid.cdc.mode.enable'               = 'true',
  'acid.cdc.build.async'               = 'false',
  'cdc.insert.into.passthrough.enable' = 'true'
)
LIFECYCLE 10;

The following table describes the key properties.

Property

Description

transactional = true

Declares the table as a Delta Table (transactional table).

acid.cdc.mode.enable = true

Enables CDC, allowing the table to generate records of data changes.

acid.cd.build.async = false

Specifies synchronous CDC data generation. When set to false, CDC data is immediately available after a DML operation completes. This is suitable for validation and testing.

cdc.insert.into.passthrough.enable = true

Allows INSERT INTO operations on this table.

Step 2: Write initial data

INSERT OVERWRITE TABLE t_department
SELECT * FROM (
    VALUES
        (1001, 'HR Department', 'Human Resources'),
        (1002, 'IT Department', 'Information Technology'),
        (1003, 'Finance Department', 'Financial Management')
) AS t (dept_id, name, description);

-- Run the following statement to verify that the data has been written.
SELECT * FROM t_department;

-- The following result is returned:
+------------+--------------------+------------------------+
| dept_id    | name               | description            |
+------------+--------------------+------------------------+
| 1003       | Finance Department | Financial Management   |
| 1001       | HR Department      | Human Resources        |
| 1002       | IT Department      | Information Technology |
+------------+--------------------+------------------------+

Step 3: Create a Delta Live Materialized View

Create a DLMV based on the source table and declare the incremental refresh mode.

In this example, the SQL logic for the DLMV is SELECT * FROM t_department. Because the system can automatically deduce that the primary key is dept_id from the source table, you do not need to explicitly declare a PRIMARY KEY.

SET odps.task.major.version = sql_flighting_dlmv;

CREATE MATERIALIZED VIEW IF NOT EXISTS dlmv_department
LIFECYCLE 10
TBLPROPERTIES (
  'refresh_mode'         = 'incremental',
  'refresh_job_settings' = 'set odps.task.major.version=sql_flighting_dlmv;'
)
AS SELECT * FROM t_department;

The following table describes the key properties.

Property

Description

refresh_mode = incremental

Declares the refresh mode as incremental, which defines the object as a DLMV.

refresh_job_settings

Specifies the session parameters for automatic refreshes. You must include the version setting. Otherwise, the automatic refresh task fails due to an engine version mismatch.

Step 4: Verify the initial data

After creation, the DLMV automatically performs an initial full refresh to populate the view. Run the following statement to view the data in the DLMV.

SELECT * FROM dlmv_department;

-- The following result is returned:
+------------+--------------------+------------------------+
| dept_id    | name               | description            |
+------------+--------------------+------------------------+
| 1003       | Finance Department | Financial Management   |
| 1001       | HR Department      | Human Resources        |
| 1002       | IT Department      | Information Technology |
+------------+--------------------+------------------------+

Step 5: Simulate incremental changes

Execute INSERT, UPDATE, and DELETE operations on the source table to simulate data changes in a real-world workload.

SET odps.task.major.version = sql_flighting_dlmv;

-- Insert a new record.
INSERT INTO t_department VALUES (1004, 'Marketing', 'Marketing Department');

-- Update a record.
UPDATE t_department SET description = 'HR and Admin' WHERE dept_id = 1001;

-- Delete a record.
DELETE FROM t_department WHERE dept_id = 1003;

Step 6: Trigger an incremental refresh

Run the following statement to manually trigger an incremental refresh. The system processes only the incremental changes from Step 5, rather than recomputing the entire table.

SET odps.task.major.version = sql_flighting_dlmv;

ALTER MATERIALIZED VIEW dlmv_department REBUILD;

Step 7: Verify the refresh result

SELECT * FROM dlmv_department;

-- The following result is returned:
+------------+---------------+------------------------+
| dept_id    | name          | description            |
+------------+---------------+------------------------+
| 1004       | Marketing     | Marketing Department   |  -- New record added.
| 1001       | HR Department | HR and Admin           |  -- The description is updated from 'Human Resources' to 'HR and Admin'.
| 1002       | IT Department | Information Technology |  -- The record with dept_id = 1003 is deleted.
+------------+---------------+------------------------+

Step 8: View the refresh history

Use the following statement to view the refresh history of the DLMV and check the status and duration of each refresh.

SELECT * FROM delta_live_mv_refresh_history('dlmv_department');

-- Sample result:
+--------------+-------------+------+--------------------+------------------+-------------+---------------------+-------+-----------------+--------------+---------------+---------------+-----------------+----------------+--------------------------+---------------------+
| project_name | schema_name | name | refresh_start_time | refresh_end_time | instance_id | duration_in_seconds | state | refresh_trigger | refresh_mode | error_message | source_tables | numinsertedrows | numdeletedrows | refresh_mode_reason_code | refresh_mode_reason |
+--------------+-------------+------+--------------------+------------------+-------------+---------------------+-------+-----------------+--------------+---------------+---------------+-----------------+----------------+--------------------------+---------------------+
| *testproject | default     | dlmv_department | 2026-07-01T17:19:07.37 | 2026-07-01T17:19:55.108 | 20260701091907370gkrrqdpv0gg | 47                  | TERMINATED | MANUAL          | INCREMENTAL  |               | [{"table_name":"*test_project.default.t_department","table_id":"757f8****fe012c","lsn":"0000000000000006","tx_id":"182****1794"}] | 2               | 2              | NULL                     | NULL                |
+--------------+-------------+------+--------------------+------------------+-------------+---------------------+-------+-----------------+--------------+---------------+---------------+-----------------+----------------+--------------------------+---------------------+

The following table describes the returned fields.

Field

Description

refresh_start_time

The time when the refresh started.

refresh_end_time

The time when the refresh ended.

state

The refresh status. Valid values: RUNNING, TERMINATED (success), and FAILED.

refresh_mode

The refresh mode for this run: FULL or INCREMENTAL.

duration_in_seconds

The refresh duration in seconds.

numInsertedRows

The number of rows inserted during this refresh.

numDeletedRows

The number of rows deleted during this refresh.

Step 9: Clean up test resources

After you complete the proof-of-concept (POC) test, run the following statements to clean up the resources and avoid unnecessary storage fees.

-- Drop the DLMV first. This is required before dropping the source table.
DROP MATERIALIZED VIEW IF EXISTS dlmv_department;

-- Then, drop the source table.
DROP TABLE IF EXISTS t_department;

You must drop the DLMV before you drop the source table. If you drop the source table first, the DLMV may be left in an inconsistent state.

Advanced: Create a partitioned DLMV

A partitioned DLMV manages incremental data in batches, such as by day, making it ideal for near-real-time data warehousing scenarios in a production environment. The following example uses the source table t_department to demonstrate how to create and refresh a partitioned DLMV.

Differences between partitioned and non-partitioned DLMVs

image

Item

Non-partitioned DLMV

Partitioned DLMV

BUILD DEFERRED

Optional

Required (Partitioned MVs currently support only this mode.)

PRIMARY KEY

Can be inferred automatically; usually does not need to be declared.

Must be explicitly declared (Cannot be inferred in BUILD DEFERRED mode.)

Partition value

Not applicable

Dynamically passed in through the get_setting() function

Refresh method

ALTER MV REBUILD;

ALTER MV REBUILD PARTITION(pt = ...);

Create a partitioned DLMV

SET odps.task.major.version = sql_flighting_dlmv;

CREATE MATERIALIZED VIEW IF NOT EXISTS part_dlmv_department
PRIMARY KEY(dept_id)        -- The PK must be explicitly declared (cannot be inferred in BUILD DEFERRED mode).
LIFECYCLE 10
BUILD DEFERRED              -- Creates only the table schema without immediately refreshing data.
PARTITIONED BY (pt)         -- Declares the partition column.
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;

The following table describes the key syntax.

Syntax

Description

PRIMARY KEY(dept_id)

Explicitly declares the primary key. In BUILD DEFERRED mode, the system does not automatically infer the primary key, so you must specify it manually.

BUILD DEFERRED

Creates only the table schema without initializing data. Data is populated by subsequent REBUILD operations.

PARTITIONED BY (pt)

Declare pt as the partition column.

get_setting('odps.custom.setting.department.pt')

  • You can dynamically obtain the value of a session variable and use it as a partition value. The prefix odps.custom.setting. is the naming convention for MaxCompute custom variables.

  • The get_setting() function works similarly to the ${bizdate} parameter in offline scheduling. During each refresh, you can use a SET statement to pass different values and write data to different partitions by day or by batch.

Refresh a specific partition

SET odps.task.major.version = sql_flighting_dlmv;

-- Set the partition value for this refresh.
SET odps.custom.setting.department.pt = 20260526;

-- Refresh the specified partition.
ALTER MATERIALIZED VIEW part_dlmv_department REBUILD PARTITION(pt = get_setting('odps.custom.setting.department.pt'));

Verify the partition data

SELECT * FROM part_dlmv_department WHERE pt = '20260526';

-- The following result is returned:
-- All current data from the source table is expected, with each row containing the partition column pt = 20260526.
+------------+---------------+------------------------+----------+
| dept_id    | name          | description            | pt       |
+------------+---------------+------------------------+----------+
| 1004       | Marketing     | Marketing Department   | 20260526 |
| 1001       | HR Department | HR and Admin           | 20260526 |
| 1002       | IT Department | Information Technology | 20260526 |
+------------+---------------+------------------------+----------+

Clean up the partitioned DLMV

DROP MATERIALIZED VIEW IF EXISTS part_dlmv_department;

Overview

  • Key advantages

    • Declarative SQL: Use standard SQL to define data processing logic. The system automatically handles incremental computation, eliminating the need for manual incremental processing logic.

    • Cost-effective: Processes only incremental data, which significantly reduces computation compared to a full refresh.

    • Unified incremental and full processing: The same SQL logic supports both incremental and full computations, balancing low-latency and high-throughput requirements.

  • How it works

    image

  • DLMV vs. traditional materialized views

    image

Next steps

After you complete this quickstart, you can refer to the following topics to learn more about Delta Live Materialized Views:

Actions

Description

Delta Live Materialized Views (Delta Live MV)

Learn about the full capabilities of DLMVs, including partitioned DLMVs, automatic refresh, and primary key inference rules.

CDC (invitation only)

Learn about the detailed mechanics of CDC and how to use the table_changes function.

Stream

Learn how to consume incremental data by using a Stream object.

Scheduled tasks (invitation only)

Learn how to configure scheduled tasks to automatically process incremental data.

FAQ

SQL error: Version not supported

Symptom: An error such as ODPS-0123xxx is returned, or a message indicates that the feature is not supported.

Solution:

  • Make sure that the SQL statement starts with set odps.task.major.version = sql_flighting_dlmv;.

  • Ensure that the DLMV feature is enabled for your account.

Refresh error: CDC data has expired

Symptom: A REBUILD operation fails with an error indicating that the incremental query range exceeds the retention period.

Cause: The time interval between two refresh operations exceeds the CDC data retention period of the source table, which is 24 hours by default.

Solution: Increase the CDC data retention period for the source table.

ALTER TABLE t_department SET TBLPROPERTIES(
  'acid.data.retain.hours' = '168',
  'cdc.data.retain.hours'  = '168'
);

The acid.data.retain.hours parameter, which specifies the table data retention period, must be greater than or equal to the cdc.data.retain.hours parameter, which specifies the CDC data retention period. Both parameters have a maximum value of 168 hours (7 days).

Creation error: Primary key required

Symptom: An error occurs when you create a DLMV, indicating that a primary key is required.

Cause: A DLMV must have a primary key. When the system cannot infer the primary key from the SQL logic, you must declare it explicitly.

Solution: Add a PRIMARY KEY declaration in the CREATE MATERIALIZED VIEW statement.

CREATE MATERIALIZED VIEW IF NOT EXISTS dlmv_example
PRIMARY KEY(your_key_column)   -- Explicitly declare the primary key.
TBLPROPERTIES ('refresh_mode' = 'incremental', ...)
AS SELECT ...;

For more information about primary key inference rules, see Delta Live Materialized Views (Delta Live MV).