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 view | Asynchronous materialized view | |
|---|---|---|
| Base table support | Single table only | Multiple tables |
| JOIN support | No | Yes |
| Aggregate functions | Limited (SUM, MIN, MAX, COUNT, BITMAP_UNION, HLL_UNION) | Full range |
| Query rewrite | Automatic | Automatic |
| Refresh strategy | Synchronous — updated on every data import | Asynchronous — scheduled or manual refresh |
| Data consistency | Always consistent with the base table | Eventually 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
| Parameter | Required | Description |
|---|---|---|
mv_name | Yes | Name of the materialized view. Must be unique per base table. |
query | Yes | SELECT statement that defines the materialized view. Must reference a single table — subqueries are not allowed. |
properties | No | Optional 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:
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:
| Function | Usage notes |
|---|---|
| SUM, MIN, MAX, COUNT | Standard aggregation. |
| BITMAP_UNION | BITMAP_UNION(TO_BITMAP(col)): column must be an integer type (excluding largeint). BITMAP_UNION(col): base table must be an Aggregate model. |
| HLL_UNION | HLL_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:
| Field | Description |
|---|---|
TableName | Base table name. |
BaseIndexName | Base table index name. |
RollupIndexName | Materialized view name. |
State | Task status: PENDING (scheduled), RUNNING (in progress), FINISHED (complete), CANCELLED (canceled). |
Timeout | Construction 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>;| Parameter | Required | Description |
|---|---|---|
database | Yes | Database containing the base table. |
table_name | Yes | Base 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>;| Parameter | Required | Description |
|---|---|---|
IF EXISTS | No | Suppresses the error if the view does not exist. |
mv_name | Yes | Name of the materialized view to drop. |
table_name | Yes | Base 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 aggregation | Matched query aggregation |
|---|---|
| sum | sum |
| min | min |
| max | max |
| count | count |
| bitmap_union | bitmap_union, bitmap_union_count, count(distinct) |
| hll_union | hll_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_idis INT, so wrap it withto_bitmap()before applyingbitmap_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_idcannot beDECIMALwhen using theHLL_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_UNIONonly 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_hashorbitmap_hash64to compute a hash value from string columns before applyingbitmap_union.
What's next
Query Explain — understand EXPLAIN output to debug query planning and verify materialized view matching.