CREATE TABLE
在许多数据库中,创建表是最复杂的操作之一,因为你可能需要:
- 手动指定引擎
- 手动指定索引
- 甚至指定数据分区或分片
Databend 以“开箱即用”为设计目标,创建表时无需上述任何操作。此外,CREATE TABLE 语句提供以下选项,帮助你在不同场景下轻松建表:
- CREATE TABLE:从零开始创建表。
- CREATE TABLE ... LIKE:按已有表的列定义创建新表。
- CREATE TABLE ... AS:创建表并将 SELECT 查询结果作为数据插入。
- CREATE TABLE ... CLONE:创建独立的表,初始时共享源表的不可变数据文件(实验性功能,v1.2.953)。
另请参阅:
CREATE TABLE
CREATE [ OR REPLACE ] TABLE [ IF NOT EXISTS ] [ <database_name>. ]<table_name>
(
<column_name> <data_type> [ NOT NULL | NULL ]
[ { DEFAULT <expr>
| { AUTOINCREMENT | IDENTITY }
[ { ( <start_num> , <step_num> )
| START <num> INCREMENT <num> } ]
[ { ORDER | NOORDER } ]
} ]
[ AS (<expr>) STORED | VIRTUAL ]
[ COMMENT '<comment>' ],
<column_name> <data_type> ...
...
)
[ ENGINE = FUSE ]
[ PARTITION BY ( <expr> [ , <expr> ... ] ) ]
[ CLUSTER BY ( <expr> [ , <expr> ... ] ) ]
[ TTL <ttl_expression> ]
[ <fuse_engine_option> = <value> ... ]
行级 TTL
TTL 定义行何时过期。TTL 定义本身不会调度清理任务。请使用 ALTER TABLE ... MATERIALIZE TTL 显式删除过期行。手动清理命令及以下表达式规则在 v1.2.953 中可用。
CREATE TABLE retained_events (
id INT,
event_time TIMESTAMP
) TTL event_time + INTERVAL 30 DAY;
TTL 表达式必须使用以下形式之一:
<time_column>:指定绝对过期时间。<time_column> + INTERVAL <nonnegative_integer> <unit>:指定保留时长。
列类型必须为 DATE、TIMESTAMP 或 TIMESTAMP_TZ。支持可空列和 STORED 计算列,不支持 VIRTUAL 计算列。支持的时间间隔单位为 MICROSECOND、SECOND、MINUTE、HOUR、DAY、WEEK、MONTH、QUARTER 和 YEAR。
请使用不带限定符的列名,以及不加引号的非负整数间隔。常量时间戳、函数调用、类型转换、多列表达式、负数间隔,以及 INTERVAL '30' DAY 这样的字符串间隔,都不是有效的定义。
TTL 仅支持持久化 Fuse 表。临时表和瞬态表不支持 TTL。SHOW CREATE TABLE 会包含已存储的 TTL 定义。通过查询创建表时,也可以在 AS SELECT 之前指定 TTL。
有关截止时间比较、权限、安全策略行为和示例,请参阅行级 TTL。
Fuse 表分区
PARTITION BY 使用一个或多个分区表达式对 Fuse 表进行物理隔离。Databend 会在写入和维护操作中保留分区边界,并通过分区元数据在扫描前裁剪无关数据。
CREATE TABLE events (
event_id BIGINT,
event_time TIMESTAMP,
tenant_id BIGINT
)
PARTITION BY (DATE_TRUNC(day, event_time))
CLUSTER BY (tenant_id, event_time);
每个表达式必须是确定性的,并且只能引用一个源列。复合分区键需要分别列出多个表达式:
PARTITION BY (DATE_TRUNC(month, event_time), BUCKET(16, tenant_id))
对于高基数键,BUCKET() 可将其映射到固定数量的哈希分区。WRITE_DISTRIBUTION_MODE = 'hash' 还能在分布式导入时,将分区值相同的行路由到同一个 Writer:
CREATE TABLE customer_events (
customer_id BIGINT,
event_time TIMESTAMP
)
PARTITION BY (BUCKET(32, customer_id))
WRITE_DISTRIBUTION_MODE = 'hash';
哈希写入模式必须与 PARTITION BY 一同使用,默认值为 'none'。有关分区策略、限制和完整示例,请参见 Fuse 表分区。
- 关于 Databend 支持的数据类型,参见 数据类型。
- 建议列名避免特殊字符;如必须使用,请用反引号包裹,例如:CREATE TABLE price(`$CA` int);
- Databend 会自动将列名转为小写。例如列名 Total 在结果中显示为 total。
CREATE TABLE ... LIKE
按已有表的列定义创建新表,复制列名、数据类型及非 NULL 约束,但不复制数据及其他属性(如 CLUSTER BY、TRANSIENT、COMPRESSION)。
语法:
CREATE TABLE [IF NOT EXISTS] [db.]table_name
LIKE [db.]origin_table_name
可显式指定 TRANSIENT 或 COMPRESSION:
create transient table t_new like t_old;
create table t_new compression='lz4' like t_old;
CREATE TABLE ... AS
创建表并用 SELECT 查询结果填充数据,不继承原表的任何属性(如 CLUSTER BY、TRANSIENT、COMPRESSION)。
语法:
CREATE TABLE [IF NOT EXISTS] [db.]table_name
AS SELECT query
可显式指定 TRANSIENT 或 COMPRESSION:
create transient table t_new as select * from t_old;
create table t_new compression='lz4' as select * from t_old;
列的可空性
默认情况下,所有列都允许 NULL。如需禁止 NULL,请使用 NOT NULL 约束。详见 NULL 值与 NOT NULL 约束。
列默认值
DEFAULT <expr> 为列设置默认值,支持:
- 固定常量,如
Marketing - 无入参的标量表达式,如
1 + 1、NOW()、UUID() - 序列动态值,如
NEXTVAL(staff_id_seq)- NEXTVAL 必须独立使用,不支持
NEXTVAL(seq1) + 1 - 需具备相应序列权限
- NEXTVAL 必须独立使用,不支持
自增列
使用 AUTOINCREMENT 或 IDENTITY 创建自增列,自动生成顺序数字,常用于唯一标识。
语法:
{ AUTOINCREMENT | IDENTITY }
[ { ( <start_num> , <step_num> )
| START <num> INCREMENT <num> } ]
[ { ORDER | NOORDER } ]
参数:
start_num:起始值(默认 1)step_num:步长(默认 1)ORDER:保证单调递增(可能有间隙)NOORDER:不保证顺序(默认)
要点:
- 内部由序列实现
- 删除列时关联序列一并删除
- 未指定值时自动生成
AUTOINCREMENT与IDENTITY等价
示例:
CREATE TABLE users (
user_id BIGINT AUTOINCREMENT,
order_id BIGINT AUTOINCREMENT START 100 INCREMENT 10,
username VARCHAR
);
INSERT INTO users (username) VALUES ('alice'), ('bob'), ('charlie');
SELECT * FROM users;
计算列
计算列通过标量表达式由其他列生成,支持两种类型:
- STORED:值持久化存储,依赖列变更时自动更新
- VIRTUAL:查询时实时计算,节省存储
语法:
<column_name> <data_type> [ NOT NULL | NULL ] AS (<expr>) { STORED | VIRTUAL }
<column_name> <data_type> [ NOT NULL | NULL ] GENERATED ALWAYS AS (<expr>) { STORED | VIRTUAL }
示例:
-- STORED:物理存储,立即更新
CREATE TABLE products (
id INT,
price FLOAT64,
quantity INT,
total_price FLOAT64 AS (price * quantity) STORED
);
-- VIRTUAL:查询时计算,无存储开销
CREATE TABLE employees (
id INT,
first_name VARCHAR,
last_name VARCHAR,
full_name VARCHAR AS (CONCAT(first_name, ' ', last_name)) VIRTUAL
);
频繁查询且对性能敏感时选 STORED;计算成本可接受且需节省空间时选 VIRTUAL。
持久化表达式限制
从 v1.2.952 开始,Databend 验证以下定义时会拒绝依赖会话或查询上下文的函数:
- 计算列,包括
STORED和VIRTUAL。 CLUSTER BY和PARTITION BY表达式。- 表 TTL 表达式。
存储层过滤器也采用此受限验证。这并不意味着每个普通查询的 WHERE 子句都受此限制。
不要在这些表达式中使用 current_database()、current_user()、角色函数、getvariable() / $var、last_query_id() 或 timezone()。将上下文函数嵌套在其他函数中也无效。Databend 会在它被折叠为字面量之前拒绝该函数。
用户定义函数也不允许使用,包括纯 SQL UDF。例如,即使 UDF 仅将输入值加一,也不能用于计算列定义。缓存中的 UDF 定义同样无法绕过此限制。
array_sort() 必须显式指定 NULL 排序顺序。array_sort(a) 和 array_sort(a, 'asc') 都依赖会话的默认 NULL 排序顺序,因此会在这些定义中被拒绝。请使用三个参数,例如 array_sort(a, 'asc', 'nulls last')。
-- Rejected: the computed expression reads session context.
CREATE TABLE invalid_context(
a STRING,
b STRING AS (concat(a, current_database())) STORED
);
-- Rejected: the null order depends on a session setting.
CREATE TABLE invalid_sort(
a ARRAY(INT),
b ARRAY(INT) AS (array_sort(a)) STORED
);
-- Explicit null ordering passes this context check.
-- Creating a computed column still requires an Enterprise license.
CREATE TABLE explicit_sort(
a ARRAY(INT),
b ARRAY(INT) AS (array_sort(a, 'asc', 'nulls last')) STORED
);
请使用满足各功能其他规则的确定性内置表达式。分区表达式仍必须仅引用一个源列。TTL 的语法和类型限制仍然有效。
这些检查并不保证每种类型转换或表达式都独立于会话设置。例如,普通的时区敏感类型转换仍使用捕获的函数上下文。已有存储键保留兼容性重绑定;此变更不是对现有键定义的迁移。
列的 DEFAULT 表达式使用独立的验证路径。它们仍可以使用上下文函数,例如 DEFAULT current_user()。
MySQL 兼容性
Databend 语法与 MySQL 的差异主要在数据类型及部分索引提示。
与 MySQL 不同,Databend 默认采用 PostgreSQL 风格的标识符大小写规则:未引用的列名会被转换为小写,而双引号包裹的列名则保留原始大小写且区分大小写。因此,如果建表时使用了保留大小写的引用列名(例如 "Employee_ID"),SELECT * 可以正常返回数据,但 SELECT Employee_ID 或 SELECT employee_id 会失败。有关大小写规则、相关设置以及如何排查此类问题,请参阅 SQL 标识符。
访问控制要求
| 权限 | 对象类型 | 描述 |
|---|---|---|
| CREATE | 全局、表 | 创建表 |
创建表时,用户或 current_role 需具备 CREATE 权限。
使用 STORAGE_PREFIX 的 CREATE TABLE 还需要在目标数据库级或全局拥有显式的 ATTACH TABLE 权限。CREATE、SUPER 和数据库所有权均不包含此额外权限。不使用 STORAGE_PREFIX 的普通建表操作不需要该权限。参见 ATTACH TABLE。
示例
创建表
创建带默认值的表:
CREATE TABLE books (
id BIGINT UNSIGNED,
title VARCHAR,
genre VARCHAR DEFAULT 'General'
);
查看表结构:
DESC books;
插入数据:
INSERT INTO books(id, title) VALUES(1, 'Invisible Stars');
查询结果:
SELECT * FROM books;
CREATE TABLE ... LIKE
按结构创建新表:
CREATE TABLE books_copy LIKE books;
CREATE TABLE ... AS
创建并复制数据:
CREATE TABLE books_backup AS SELECT * FROM books;
计算列示例
CREATE TABLE IF NOT EXISTS products (
id INT,
price FLOAT64,
quantity INT,
total_price FLOAT64 AS (price * quantity) STORED
);
INSERT INTO products VALUES (1,10.5,3),(2,15.2,5),(3,8.7,2);
SELECT * FROM products;
CREATE TABLE student_profiles (
id STRING,
profile VARIANT,
age INT NULL AS (profile['age']::INT) VIRTUAL
);
INSERT INTO student_profiles VALUES
('d78236','{"id":"d78236","name":"Arthur Read","age":"16"}'),
('f98112','{"name":"Buster Bunny","age":"15","id":"f98112"}'),
('t63512','{"name":"Ernie Narayan","id":"t63512"}');
SELECT * FROM student_profiles;

