All Products
Search
Document Center

ApsaraDB for SelectDB:Synchronous materialized view

Last Updated:Mar 28, 2026

A synchronous materialized view is a pre-computed dataset stored as a special table in ApsaraDB for SelectDB. Define it once with a SELECT statement, and SelectDB automatically reads from it whenever a matching query arrives—no query rewrites required on your part.

Sync vs. async materialized views

Choose the right type before you start building.

Synchronous materialized viewAsynchronous materialized view
Base table supportSingle table onlyMultiple tables
JOIN supportNoYes
Aggregate functionsLimited (SUM, MIN, MAX, COUNT, BITMAP_UNION, HLL_UNION)Full range
Query rewriteAutomaticAutomatic
Refresh strategySynchronous — updated on every data importAsynchronous — scheduled or manual refresh
Data consistencyAlways consistent with the base tableEventually consistent

Use a synchronous materialized view when your workload targets a single table and needs real-time consistency. Use an asynchronous materialized view for multi-table joins or richer aggregation logic.

Use cases

  • Aggregation acceleration: Pre-compute GROUP BY aggregations that run repeatedly on large datasets.

  • Prefix index matching: Create a view with a different leading sort column to match queries that cannot use the base table's prefix index.

  • Pre-filtering: Store a filtered subset of the base table to reduce scan volume.

  • Expression pre-computation: Materialize complex computed columns so queries read results directly.

When to create a materialized view

Create a synchronous materialized view when all of the following are true:

  • The query targets a single table.

  • The query runs frequently.

  • The query is expensive (heavy aggregation, large scan, or complex expression).

Skip the materialized view when any of the following apply:

  • The query is cheap and fast already.

  • Different aggregate functions are needed on the same column (not supported).

  • You already have more than 10 materialized views on the table — each additional view slows down every data import, because base table and all its views are updated together.

Limitations

  • Direct queries are not supported. Write queries against the base table. SelectDB picks the best matching materialized view automatically.

  • Unique model restriction: In the Unique model, a synchronous materialized view can only reorder columns — it cannot aggregate data.

  • Import performance: Each materialized view adds overhead to every data import. More than 10 materialized views on a single table can significantly slow down imports.

Create a materialized view

Syntax

CREATE MATERIALIZED VIEW <mv_name> AS <query>
[PROPERTIES ("key" = "value")]

query format:

SELECT select_expr [, select_expr ...]
FROM <base_table_name>
[GROUP BY column_name [, column_name ...]]
[ORDER BY column_name [, column_name ...]]

Parameters

ParameterRequiredDescription
mv_nameYesName of the materialized view. Must be unique per base table.
queryYesSELECT statement that defines the materialized view. Must reference a single table — subqueries are not allowed.
propertiesNoOptional configuration: short_key (number of sorting columns) and timeout (construction timeout in seconds).

SELECT statement constraints:

  • Single table only — no subqueries.

  • Columns must not include auto-increment columns, constants, duplicate expressions, or window functions.

  • If the SELECT includes partition key or bucketing columns, those columns must be Key columns in the materialized view.

  • Allowed clauses: WHERE, GROUP BY, ORDER BY.

  • Prohibited clauses: JOIN, HAVING, LIMIT, LATERAL VIEW.

Aggregate function constraints:

Important

Parameters must be single columns — expressions are not supported. For example, sum(a) is valid but sum(a+b) is not. Different aggregate functions on the same column are also not supported: SELECT sum(a), min(a) FROM table is invalid.

Supported aggregate functions:

FunctionUsage notes
SUM, MIN, MAX, COUNTStandard aggregation.
BITMAP_UNIONBITMAP_UNION(TO_BITMAP(col)): column must be an integer type (excluding largeint). BITMAP_UNION(col): base table must be an Aggregate model.
HLL_UNIONHLL_UNION(HLL_HASH(col)): column cannot be DECIMAL. HLL_UNION(col): base table must be an Aggregate model.

ORDER BY auto-supplement rules (when ORDER BY is omitted):

  • Aggregation-type view: all grouping columns become sorting columns.

  • Non-aggregation-type view: the first 36 bytes of columns become sorting columns.

  • If fewer than three columns are auto-supplemented, the first three columns are used.

  • If GROUP BY is specified, ORDER BY must match the grouping columns.

Design principles

  • Abstract shared patterns. Define the materialized view around aggregation patterns that multiple queries share. A view matched by only one query consumes storage for minimal benefit.

  • Cover common dimensions only. Not every dimension combination needs a materialized view. Target the combinations that appear most often in production queries.

Examples

The following examples use this base table:

CREATE TABLE duplicate_table (
    k1 INT NULL,
    k2 INT NULL,
    k3 BIGINT NULL,
    k4 BIGINT NULL
)
DUPLICATE KEY (k1, k2, k3, k4)
DISTRIBUTED BY HASH(k4) BUCKETS 3;

Example 1: Column subset with reordered prefix

CREATE MATERIALIZED VIEW k1_k2 AS
SELECT k2, k1 FROM duplicate_table;

Result: a view containing only k2 and k1, with k2 as the leading sort column. Queries filtering on k2 can use this view's prefix index instead of the base table's.

Example 2: Explicit sort order

CREATE MATERIALIZED VIEW k2_order AS
SELECT k2, k1 FROM duplicate_table ORDER BY k2;

Example 3: Aggregation

CREATE MATERIALIZED VIEW k1_k2_sumk3 AS
SELECT k1, k2, sum(k3)
FROM duplicate_table
GROUP BY k1, k2;

Result: a view with k1, k2 as Key columns and sum(k3) as the aggregated value. SelectDB auto-supplements k1, k2 as sorting columns because GROUP BY is used and ORDER BY is omitted.

Check creation status

Creating a materialized view is an asynchronous operation. After submitting the CREATE statement, SelectDB builds the view in the background. Track progress with:

SHOW ALTER TABLE MATERIALIZED VIEW FROM <database>;

Result fields:

FieldDescription
TableNameBase table name.
BaseIndexNameBase table index name.
RollupIndexNameMaterialized view name.
StateTask status: PENDING (scheduled), RUNNING (in progress), FINISHED (complete), CANCELLED (canceled).
TimeoutConstruction timeout in seconds.

The view is ready to use when State is FINISHED.

Example:

SHOW ALTER TABLE MATERIALIZED VIEW FROM test_db;

+--------+---------------+---------------------+---------------------+---------------+-----------------+----------+---------------+----------+------+----------+---------+
| JobId  | TableName     | CreateTime          | FinishTime          | BaseIndexName | RollupIndexName | RollupId | TransactionId | State    | Msg  | Progress | Timeout |
+--------+---------------+---------------------+---------------------+---------------+-----------------+----------+---------------+----------+------+----------+---------+
| 494349 | sales_records | 2020-07-30 20:04:56 | 2020-07-30 20:04:57 | sales_records | store_amt       | 494350   | 133107        | FINISHED |      | NULL     | 2592000 |
+--------+---------------+---------------------+---------------------+---------------+-----------------+----------+---------------+----------+------+----------+---------+

List materialized views

List all materialized views on a base table along with their schemas:

DESC <table_name> ALL;

Example:

DESC duplicate_table ALL;

+-----------------+---------------+---------------+--------+--------------+------+-------+---------+-------+---------+------------+-------------+
| IndexName       | IndexKeysType | Field         | Type   | InternalType | Null | Key   | Default | Extra | Visible | DefineExpr | WhereClause |
+-----------------+---------------+---------------+--------+--------------+------+-------+---------+-------+---------+------------+-------------+
| duplicate_table | DUP_KEYS      | k1            | INT    | INT          | Yes  | true  | NULL    |       | true    |            |             |
|                 |               | k2            | INT    | INT          | Yes  | true  | NULL    |       | true    |            |             |
|                 |               | k3            | BIGINT | BIGINT       | Yes  | true  | NULL    |       | true    |            |             |
|                 |               | k4            | BIGINT | BIGINT       | Yes  | true  | NULL    |       | true    |            |             |
|                 |               |               |        |              |      |       |         |       |         |            |             |
| k2_order        | DUP_KEYS      | mv_k2         | INT    | INT          | Yes  | true  | NULL    |       | true    | `k2`       |             |
|                 |               | mv_k1         | INT    | INT          | Yes  | false | NULL    | NONE  | true    | `k1`       |             |
|                 |               |               |        |              |      |       |         |       |         |            |             |
| k1_k2           | DUP_KEYS      | mv_k2         | INT    | INT          | Yes  | true  | NULL    |       | true    | `k2`       |             |
|                 |               | mv_k1         | INT    | INT          | Yes  | true  | NULL    |       | true    | `k1`       |             |
|                 |               |               |        |              |      |       |         |       |         |            |             |
| k1_k2_sumk3     | AGG_KEYS      | mv_k1         | INT    | INT          | Yes  | true  | NULL    |       | true    | `k1`       |             |
|                 |               | mv_k2         | INT    | INT          | Yes  | true  | NULL    |       | true    | `k2`       |             |
|                 |               | mva_SUM__`k3` | BIGINT | BIGINT       | Yes  | false | NULL    | SUM   | true    | `k3`       |             |
+-----------------+---------------+---------------+--------+--------------+------+-------+---------+-------+---------+------------+-------------+

View the creation statement

Retrieve the SQL used to create a materialized view:

SHOW CREATE MATERIALIZED VIEW <mv_name> ON <table_name>;
This command only works for existing materialized views. Deleted views cannot be queried.

Example:

SHOW CREATE MATERIALIZED VIEW id_col1 ON table3;

+-----------+----------+----------------------------------------------------------------+
| TableName | ViewName | CreateStmt                                                     |
+-----------+----------+----------------------------------------------------------------+
| table3    | id_col1  | create materialized view id_col1 as select id,col1 from table3 |
+-----------+----------+----------------------------------------------------------------+
1 row in set (0.00 sec)

Delete a materialized view

Cancel an in-progress creation

If the creation job has not yet finished, cancel it with:

CANCEL ALTER TABLE MATERIALIZED VIEW FROM <database>.<table_name>;
ParameterRequiredDescription
databaseYesDatabase containing the base table.
table_nameYesBase table name.

Example:

CANCEL ALTER TABLE MATERIALIZED VIEW FROM test_db.duplicate_table;

If the view is already built, this command has no effect — use DROP instead.

Drop a completed materialized view

DROP MATERIALIZED VIEW [IF EXISTS] <mv_name> ON <table_name>;
ParameterRequiredDescription
IF EXISTSNoSuppresses the error if the view does not exist.
mv_nameYesName of the materialized view to drop.
table_nameYesBase table of the materialized view.

Example:

-- List materialized views before dropping
DESC duplicate_table ALL;

-- Drop the view named k1_k2
DROP MATERIALIZED VIEW k1_k2 ON duplicate_table;

-- Confirm the view is gone
DESC duplicate_table ALL;

Query automatic matching

After a materialized view is created and its State is FINISHED, all existing queries continue to target the base table unchanged. SelectDB transparently selects the best matching materialized view and rewrites the query internally.

The matching table for aggregate functions is:

Materialized view aggregationMatched query aggregation
sumsum
minmin
maxmax
countcount
bitmap_unionbitmap_union, bitmap_union_count, count(distinct)
hll_unionhll_raw_agg, hll_union_agg, ndv, approx_count_distinct

When bitmap or hll aggregation matches, SelectDB rewrites the query's aggregation operator based on the materialized view's schema.

To confirm a query is hitting a materialized view, run EXPLAIN against it:

EXPLAIN <your_query>;

In the output, look for OlapScanNode. The rollup attribute shows which index is being scanned. If it shows the materialized view name instead of the base table name, the match is confirmed. For more details on reading EXPLAIN output, see Query Explain.

Best practices

Exact count distinct with BITMAP_UNION

Scenario: Counting unique users (or any high-cardinality integer column) grouped by multiple dimensions.

Typical query:

SELECT advertiser, channel, COUNT(DISTINCT user_id)
FROM advertiser_view_record
GROUP BY advertiser, channel;

COUNT(DISTINCT ...) on large tables is expensive. A materialized view with BITMAP_UNION pre-deduplicates the data so the query reads aggregated bitmaps instead of raw rows.

Create the view:

CREATE MATERIALIZED VIEW advertiser_uv AS
SELECT advertiser, channel, bitmap_union(to_bitmap(user_id))
FROM advertiser_view_record
GROUP BY advertiser, channel;
user_id is INT, so wrap it with to_bitmap() before applying bitmap_union. This converts integers to bitmap format required by the function.

After the view is built (State = FINISHED), the original query is automatically rewritten to:

SELECT advertiser, channel, bitmap_union_count(to_bitmap(user_id))
FROM advertiser_uv
GROUP BY advertiser, channel;

Verify the match:

EXPLAIN SELECT advertiser, channel, COUNT(DISTINCT user_id)
FROM advertiser_view_record
GROUP BY advertiser, channel;

In the EXPLAIN output, find OlapScanNode. Confirm that the rollup value is advertiser_uv. Also confirm that count(distinct) has been rewritten to bitmap_union_count(to_bitmap).

Approximate count distinct with HLL_UNION

Scenario: Estimating unique counts where exact precision is not required and query speed matters more.

CREATE MATERIALIZED VIEW approx_uv AS
SELECT advertiser, channel, hll_union(hll_hash(user_id))
FROM advertiser_view_record
GROUP BY advertiser, channel;

Queries using approx_count_distinct, ndv, hll_union_agg, or hll_raw_agg on user_id automatically match this view.

user_id cannot be DECIMAL when using the HLL_UNION(HLL_HASH(col)) format.

Prefix index reordering

Scenario: A frequently run query filters on a column that is not the leading column of the base table's sort key.

If the base table uses DUPLICATE KEY (k1, k2, k3, k4) but queries often filter on k2:

CREATE MATERIALIZED VIEW k2_order AS
SELECT k2, k1 FROM duplicate_table ORDER BY k2;

Queries with WHERE k2 = ... can now use this view's prefix index, reducing the scan range significantly.

Troubleshooting

Error: `DATA_QUALITY_ERR: "The data quality does not satisfy, please check your data."`

This error occurs when data quality issues or schema changes cause memory usage to exceed limits during view construction. If the cause is memory pressure, increase the memory_limitation_per_thread_for_schema_change_bytes parameter.

Two additional causes specific to bitmap views:

  • Negative integers in source data: BITMAP_UNION only supports positive integers. If the column contains negative values, view creation fails. Check the source data and filter or transform negative values before creating the view.

  • String columns: Use bitmap_hash or bitmap_hash64 to compute a hash value from string columns before applying bitmap_union.

What's next

  • Query Explain — understand EXPLAIN output to debug query planning and verify materialized view matching.