CREATE TABLE syntax
This topic describes the CREATE TABLE syntax, including table creation templates for 5 common scenarios, complete parameter descriptions, and FAQ.
Quick start guide
Select a table creation template below based on your business scenario. Each scenario provides a minimal CREATE TABLE statement and key notes. For detailed parameter descriptions, see the Parameters section below.
Scenario 1: Date-partitioned fact table (most common)
Suitable for scenarios where data is continuously written by date and needs to be queried and managed by date lifecycle, such as order tables, log tables, and event data tables.
CREATE TABLE sales (
sale_id BIGINT NOT NULL COMMENT 'Order ID',
customer_id VARCHAR NOT NULL COMMENT 'Customer ID',
revenue DECIMAL(15, 2) COMMENT 'Order amount',
sale_time TIMESTAMP NOT NULL COMMENT 'Order time',
PRIMARY KEY (sale_time, sale_id)
)
DISTRIBUTED BY HASH(sale_id)
PARTITION BY VALUE(DATE_FORMAT(sale_time, '%Y%m%d'))
LIFECYCLE 365;
-
The primary key must include the distribution key and partition key. In the example above,
sale_id(distribution key) andsale_time(partition key) are both in the primary key. See PRIMARY KEY. -
TIMESTAMP or DATE types are recommended for the partition key. See PARTITION BY.
-
When INSERT encounters duplicate primary key values, the system silently ignores the duplicate records without raising an error. Ensure the primary key uniquely identifies each record.
Scenario 2: Hot/cold tiered partitioned table (reduce storage cost)
Suitable for scenarios where historical data is queried infrequently and cold data should be stored on OSS to reduce costs while maintaining query performance for recent data.
CREATE TABLE order_history (
order_id BIGINT NOT NULL,
customer_id INT NOT NULL,
order_date DATE NOT NULL,
amount DECIMAL(15, 2),
PRIMARY KEY (order_date, order_id)
)
DISTRIBUTED BY HASH(order_id)
PARTITION BY VALUE(DATE_FORMAT(order_date, '%Y%m')) LIFECYCLE 120
STORAGE_POLICY='MIXED' HOT_PARTITION_COUNT=3;
-
COLD and MIXED policies only take effect on partitioned tables. For non-partitioned tables, data remains on SSD (equivalent to HOT) even if COLD or MIXED is set. See storage_policy.
-
Hot/cold tiering can only be set at the table level, not at the database level.
-
HOT_PARTITION_COUNT=3means the 3 most recent partitions are stored on hot storage (SSD), and the rest on cold storage (OSS). In the example above, data is partitioned by month, so the most recent 3 months are hot storage and data older than 3 months is cold storage.
Scenario 3: High-performance query table with clustered index
Suitable for tables with large data volumes and frequent range queries that need clustered indexes to optimize read performance, such as SaaS multi-tenant tables queried by tenant_id.
CREATE TABLE user_events (
tenant_id VARCHAR NOT NULL COMMENT 'Tenant ID',
event_id BIGINT NOT NULL,
event_time TIMESTAMP NOT NULL,
event_type VARCHAR,
CLUSTERED KEY idx_tenant(tenant_id, event_time DESC),
PRIMARY KEY (event_time, tenant_id, event_id)
)
DISTRIBUTED BY HASH(tenant_id)
PARTITION BY VALUE(DATE_FORMAT(event_time, '%Y%m%d'))
LIFECYCLE 90;
-
The clustered index takes effect only after BUILD completes. After creating a table or adding a clustered index via ALTER TABLE, wait for the BUILD task to complete (or manually execute
BUILD TABLE table_name). See CLUSTERED KEY. -
When query conditions do not include the distribution key, the query scans all shards. If business queries cannot cover the distribution key, consider creating a clustered index (CLUSTERED KEY) for frequently queried columns.
-
Each table can have only one clustered index. The clustered index defaults to ascending order; for descending queries, set the clustered index to DESC.
Scenario 4: Non-partitioned table (small/dimension table)
Suitable for tables with small data volumes (under tens of millions of rows) that do not require time-based lifecycle management.
CREATE TABLE product (
product_id BIGINT NOT NULL PRIMARY KEY,
product_name VARCHAR,
category VARCHAR,
price DECIMAL(10, 2)
)
DISTRIBUTED BY HASH(product_id);
-
When no primary key or distribution key is defined, the system automatically adds the
__adb_auto_id__column as the primary key and distribution key. -
All data in a non-partitioned table resides in a single partition. Index scan efficiency degrades when data exceeds tens of millions of rows. Partitioned tables are recommended for large tables.
Scenario 5: Broadcast table (small lookup table)
Suitable for dimension tables with small data volumes (recommended no more than 20,000 rows) that need to frequently JOIN with large tables.
CREATE TABLE dim_city (
city_id INT NOT NULL PRIMARY KEY,
city_name VARCHAR,
province VARCHAR
)
DISTRIBUTED BY BROADCAST;
-
Broadcast tables store a full copy of data on each node, eliminating cross-node data transfer during JOINs, but writes are broadcast to all nodes.
-
Data volume should not exceed 20,000 rows. Frequent inserts, updates, or deletes are not recommended.
Important notes
The following properties cannot be modified after table creation. Plan carefully before creating the table:
-
Primary key: Primary key columns cannot be added, removed, or changed.
-
Distribution key: Distribution key columns cannot be added, removed, or changed. Table recreation and data migration are required.
-
Partition key: A partition key cannot be added (a non-partitioned table cannot be converted to a partitioned table), and partition key columns cannot be added, removed, or modified. Table recreation is required.
-
Storage engine: The storage engine cannot be switched between XUANWU and XUANWU_V2. Table recreation is required.
Syntax
CREATE TABLE [IF NOT EXISTS] table_name
({column_name column_type [column_attributes] [ column_constraints ] [COMMENT 'column_comment']
| table_constraints}
[, ... ])
[table_attribute]
[partition_options]
[index_all]
[storage_policy]
[block_size]
[engine]
[table_properties]
[AS query_expr]
[COMMENT 'table_comment']
column_attributes:
[DEFAULT {constant | CURRENT_TIMESTAMP}]
[AUTO_INCREMENT]
column_constraints:
[{NOT NULL|NULL} ]
[PRIMARY KEY]
table_constraints:
[{INDEX|KEY} [index_name] (column_name|column_name->'$.json_path'|column_name->'$[*]')][,...]
[FULLTEXT [INDEX|KEY] [index_name] (column_name) [index_option]] [,...]
[PRIMARY KEY [index_name] (column_name,...)]
[CLUSTERED KEY [index_name] (column_name[ASC|DESC],...) ]
[[CONSTRAINT [symbol]] FOREIGN KEY (fk_column_name) REFERENCES pk_table_name (pk_column_name)][,...]
[ANN INDEX [index_name] (column_name,...) [index_option]] [,...]
table_attribute:
DISTRIBUTED BY HASH(column_name,...) | DISTRIBUTED BY BROADCAST
partition_options:
PARTITION BY
{VALUE(column_name) | VALUE(DATE_FORMAT(column_name, 'format')) | VALUE(FROM_UNIXTIME(column_name, 'format'))}
LIFECYCLE N
index_all:
INDEX_ALL= 'Y|N'
storage_policy:
STORAGE_POLICY= {'HOT'|'COLD'|'MIXED' {hot_partition_count=N}}
block_size:
BLOCK_SIZE= VALUE
engine:
ENGINE= 'XUANWU|XUANWU_V2'
Parameters
table_name, column_name, column_type, COMMENT
Examples
For complete table creation scenarios and templates, see Quick start guide. The following examples demonstrate specific features.
Create a non-partitioned table
No distribution or partition key defined; primary key used as distribution key
The table has a primary key but no distribution key. AnalyticDB for MySQL uses the primary key as the distribution key by default.
CREATE TABLE orders (
order_id BIGINT NOT NULL COMMENT 'Order ID',
customer_id INT NOT NULL COMMENT 'Customer ID',
order_status VARCHAR(1) NOT NULL COMMENT 'Order status',
total_price DECIMAL(15, 2) NOT NULL COMMENT 'Order amount',
order_date DATE NOT NULL COMMENT 'Order date',
PRIMARY KEY(order_id,order_date)
);
Query the CREATE TABLE statement. The primary key columns order_id and order_date are used as the distribution key.
SHOW CREATE TABLE orders;+---------+-----------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+---------+-----------------------------------------------------------------------------------------------------------------------------------------------+
| orders | CREATE TABLE `orders` ( |
| | `order_id` bigint NOT NULL COMMENT 'Order ID', |
| | `customer_id` int NOT NULL COMMENT 'Customer ID', |
| | `order_status` varchar(1) NOT NULL COMMENT 'Order status', |
| | `total_price` decimal(15, 2) NOT NULL COMMENT 'Order amount', |
| | `order_date` date NOT NULL COMMENT 'Order date', |
| | PRIMARY KEY (`order_id`,`order_date`) |
| | ) DISTRIBUTED BY HASH(`order_id`,`order_date`) INDEX_ALL='Y' STORAGE_POLICY='HOT' ENGINE='XUANWU' TABLE_PROPERTIES='{"format":"columnstore"}' |
+---------+-----------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.04 sec)
No primary key or distribution key defined; system auto-generates both
The table has no primary key or distribution key. AnalyticDB for MySQL adds a column __adb_auto_id__ as both the primary key and distribution key.
CREATE TABLE orders_new (
order_id BIGINT NOT NULL COMMENT 'Order ID',
customer_id INT NOT NULL COMMENT 'Customer ID',
order_status VARCHAR(1) NOT NULL COMMENT 'Order status',
total_price DECIMAL(15, 2) NOT NULL COMMENT 'Order amount',
order_date DATE NOT NULL COMMENT 'Order date'
);
Query the CREATE TABLE statement. An auto-increment column __adb_auto_id__ is automatically added, serving as both the primary key and distribution key.
SHOW CREATE TABLE orders_new;+-------------+-----------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-------------+-----------------------------------------------------------------------------------------------------------------------------------------------+
| orders_new | CREATE TABLE `orders_new` ( |
| | `__adb_auto_id__` bigint AUTO_INCREMENT, |
| | `order_id` bigint NOT NULL COMMENT 'Order ID', |
| | `customer_id` int NOT NULL COMMENT 'Customer ID', |
| | `order_status` varchar(1) NOT NULL COMMENT 'Order status', |
| | `total_price` decimal(15, 2) NOT NULL COMMENT 'Order amount', |
| | `order_date` date NOT NULL COMMENT 'Order date', |
| | PRIMARY KEY (`__adb_auto_id__`) |
| | ) DISTRIBUTED BY HASH(`__adb_auto_id__`) INDEX_ALL='Y' STORAGE_POLICY='HOT' ENGINE='XUANWU' TABLE_PROPERTIES='{"format":"columnstore"}' |
+-------------+-----------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.04 sec)
Primary and distribution key defined, no partition key
Create table supplier. supplier_id is an auto-increment column. The distribution key is supplier_id. Data is HASH-distributed based on supplier_id values.
CREATE TABLE supplier (
supplier_id BIGINT AUTO_INCREMENT PRIMARY KEY,
supplier_name VARCHAR,
address INT,
phone VARCHAR
)
DISTRIBUTED BY HASH(supplier_id);
Create indexes on specific columns
Create regular indexes on the id and date columns only; other columns are not indexed.
CREATE TABLE index_tb (
id INT,
sales DECIMAL(15, 2),
date DATE,
INDEX (id),
INDEX (date),
PRIMARY KEY (id)
)
DISTRIBUTED BY HASH(id);
Define a full-text index
Create a full-text index on the content column with the name fidx_c.
CREATE TABLE fulltext_tb (
id INT,
content VARCHAR,
keyword VARCHAR,
FULLTEXT INDEX fidx_c(content),
PRIMARY KEY (id)
)
DISTRIBUTED BY HASH(id);
For more information about creating and modifying full-text indexes, see Create a full-text index.
For information about full-text search, see Full-text search.
Define a vector index
Define short_feature and float_feature as vector columns of type array<float> with a dimension of 4.
Create vector index short_feature_index on short_feature, and vector index float_feature_index on float_feature.
CREATE TABLE fact_tb (
xid BIGINT NOT NULL,
cid BIGINT NOT NULL,
uid VARCHAR NOT NULL,
vid VARCHAR NOT NULL,
wid VARCHAR NOT NULL,
short_feature array<smallint>(4),
float_feature array<float>(4),
ann index short_feature_index(short_feature),
ann index float_feature_index(float_feature),
PRIMARY KEY (xid, cid, vid)
)
DISTRIBUTED BY HASH(xid) PARTITION BY VALUE(cid) LIFECYCLE 4;
For more information about vector indexes and vector search, see Vector search.
Define a foreign key index
Create a table named store_returns. Use the foreign key syntax FOREIGN KEY to associate sr_item_sk with the primary key column customer_id of the customer table.
CREATE TABLE store_returns (
sr_sale_id BIGINT NOT NULL PRIMARY KEY,
sr_store_sk BIGINT,
sr_item_sk BIGINT NOT NULL,
FOREIGN KEY (sr_item_sk) REFERENCES customer (customer_id)
);
Define a JSON Array index
Create a JSON Array index on the vj column with the name idx_vj.
CREATE TABLE json(
id INT,
vj JSON,
INDEX idx_vj(vj->'$[*]')
)
DISTRIBUTED BY HASH(id);
For more information about creating and modifying JSON Array indexes, see Usage notes and JSON Array index.
FAQ
Compression and storage
Column attributes and constraints
Distribution key, partition key, and lifecycle
Indexes
Columnar storage
Other
Common errors
Related topics
-
To write data to a table, see INSERT INTO.
-
To write or overwrite query results, see INSERT SELECT FROM or INSERT OVERWRITE SELECT.
-
To import data from RDS, MaxCompute, OSS, or other data sources, see Data import.