虚拟表
虚拟超级表继承
虚拟超级表(VST)可以通过 BASE ON 子句从一个或多个父虚拟超级表继承列和标签。这使得构建层次化的虚拟表拓扑成为可能——例如,一个设备类型 VST 从基础设备 VST 继承公共字段,再添加自身的特有列。
创建继承的虚拟超级表
CREATE STABLE [IF NOT EXISTS] [db_name.]stb_name
(col_name col_type [, ...])
TAGS (tag_name tag_type [, ...])
BASE ON [db_name.]parent_stb_name [, [db_name.]parent_stb_name] ...
VIRTUAL 1
BASE ON 子句指定一个或多个父虚拟超级表。子 VST 自动继承每个父 VST 的全部列和标签,子 VST 自身的列和标签追加在继承的列之后。
示例:
-- 父 VST:通用设备字段
CREATE STABLE p_device (ts timestamp, status int) TAGS (region int) VIRTUAL 1;
-- 子 VST:继承 status + region,增加温度列
CREATE STABLE p_temp (ts timestamp, temp float) TAGS (sensor_id int)
BASE ON test_db.p_device VIRTUAL 1;
-- 在子 VST 下创建虚拟子表
CREATE VTABLE vct_t1 (status FROM src.c1, temp FROM src.c2)
USING test_db.p_temp TAGS (100, 1);
-- 查询子 VST(叶子)
SELECT * FROM test_db.p_temp;
-- 查询父 VST(非叶子)——返回所有后代 VCT 的 UNION ALL
SELECT count(*) FROM test_db.p_device;
修改继承关系
-- 添加父表
ALTER STABLE [db_name.]stb_name ADD BASE ON [db_name.]parent_stb_name;
-- 移除父表(该父表贡献的列和标签会被级联删除)
ALTER STABLE [db_name.]stb_name DROP BASE ON [db_name.]parent_stb_name;
非叶子 VST 查询
查询非叶子 VST(即有子 VST 继承的 VST)时,引擎自动将查询重写为所有叶子后代 VCT 的 UNION ALL:
SELECT * FROM parent_vst返回所有后代 VCT 的数据。SELECT count(*) FROM parent_vst跨所有后代聚合。- 支持多层继承(祖父→父→叶子)——查询祖父会遍历整个后代树。
查看继承关系
SHOW VTABLE INHERITS;
显示 VST 之间的父子继承关系,包括数据库名、子表名、父表名。
约束条件
- 仅限虚拟表:
BASE ON要求父表和子表都是虚拟超级表(VIRTUAL 1)。 - 同库限制:父表和子表必须在同一个数据库中。
- 列名/标签名冲突:子表自身的列名和标签名不能与继承自任何父表的名称冲突。
- 循环继承:循环依赖链会被检测并拒绝。
- 最大父表数:一个 VST 最多可继承 10 个父 VST。
- 非叶子限制:非叶子 VST(即有子表的 VST)不能直接创建 VCT——VCT 只能在叶子 VST 下创建。已有 VCT 的父表不能被用作
BASE ON目标。 - Schema 变更:对有子表的父 VST 执行
ADD COLUMN/DROP COLUMN会被拒绝。父表一旦成为非叶子,其 Schema 即被冻结。
创建虚拟表
CREATE VTABLE 语句用于创建虚拟普通表和以虚拟超级表为模板创建虚拟子表。
创建虚拟超级表
见 创建超级表 中的 VIRTUAL 参数。
创建虚拟普通表
CREATE VTABLE [IF NOT EXISTS] [db_name].vtb_name
ts_col_name timestamp,
(create_definition[ ,create_definition] ...)
create_definition:
vtb_col_name column_definition
column_definition:
type_name [FROM [db_name.]table_name.col_name]
创建虚拟子表
CREATE VTABLE [IF NOT EXISTS] [db_name].vtb_name
(create_definition[ ,create_definition] ...)
USING [db_name.]stb_name
[(tag_name [, tag_name] ...)]
TAGS (tag_value [, tag_value] ...)
create_definition:
[stb_col_name FROM] [db_name.]table_name.col_name
tag_value:
const_value
| [tag_name] FROM [db_name.]table_name.tag_name
tag_value 语法说明:
const_value:使用字面量常量作为标签值,与普通子表行为一致。FROM [db_name.]table_name.tag_name:标签引用(tag-ref),引用指定表的标签列。当省略tag_name前缀时,目标标签名与源标签名相同。当指定tag_name FROM ...时,可将源标签映射到不同名称的目标标签。
此语法与列引用的 [stb_col_name FROM] table_name.col_name 设计一致:FROM 关键字前的名称为虚拟表侧的名称,FROM 后为数据源侧的名称。
使用说明 :
- 虚拟表(列)名命名规则参见 名称命名规则。
- 虚拟表中列的最大个数为 32767。
- 表名最大长度为 192。
- 表的第一个字段必须是 TIMESTAMP,并且系统自动将其设为主键。
- 表的每行长度不能超过 512KB(注意:每个 VARCHAR/NCHAR/GEOMETRY 类型的列还会额外占用 2 个字节的存储位置)。
- 使用数据类型 VARCHAR/NCHAR/GEOMETRY,需指定其最长的字节数,如 VARCHAR(20),表示 20 字节。
- 创建虚拟表时使用 FROM 来指定列的数据源,支持使用 db_name 跨库指定数据源,不指定 db_name 时默认使用当前 use 的数据库,若不指定 db_name 且未 use 数据库,则会报错。
- 创建虚拟表时不显式的指定 ts 列的数据源,ts 列的取值是查询虚拟表时查询语句中包含的所有列对应的原始表的主键时间戳合并的结果。
- 虚拟超级表下只支持创建虚拟子表,虚拟子表也只能以虚拟超级表为模版来创建。
- 创建虚拟表时需要保证虚拟表中的列、标签和指定的数据来源列、标签的数据类型相同,否则会报错。
- 在同一个数据库内,虚拟表名称不允许重名,虚拟表名和表名也不允许重名。虚拟表名和视图名允许重名(不推荐)当出现视图与虚拟表名重名时,写入、查询、授权、回收权限等操作优先使用同名表。
- 创建虚拟子表和虚拟普通表时,使用 FROM 指定某一列的数据来源时,该列支持来源于普通表、普通子表以及已有虚拟表;不支持来源于超级表、视图,也不支持来源于有复合主键的表。
- 创建虚拟子表时,
TAGS (...)中的标签既可以使用常量值,也可以使用 tag-ref(语法见上方tag_value定义)。跨库时可写成db_name.table.tag。 - 引用相关的限制和行为详见下方"当前支持的引用能力"章节。
当前支持的引用能力
引用的含义
"引用"指虚拟表的列或标签不存储实际数据,而是在查询时动态从源表获取数据。引用是动态绑定而非快照:
- tag-ref:虚拟子表的标签值引用另一张表的标签列,查询时实时解析为源标签的当前值。如果源表标签被
ALTER TABLE ... SET TAG修改,虚拟表查询结果会立即反映新值。 - col-ref:虚拟表的数据列引用另一张表(含虚拟表)的数据列,查询时从源表读取对应数据。如果源表有新数据写入,虚拟表查询即可看到。
引用链
- 虚拟表列支持引用已有虚拟表的列,因此可以构建多跳引用链(VChild → VChild → ... → 物理表)。
- tag-ref 与 col-ref 可以混合多跳;当前引用深度上限为 32,超过时校验或查询会返回错误码
0x8000620C。 - 支持同库和跨库引用场景。
变更场景下的行为
| 变更操作 | 对虚拟表的影响 |
|---|---|
源表标签被修改 (ALTER TABLE SET TAG) | tag-ref 查询立即反映新值 |
| 源表写入新数据 | col-ref 查询可见新数据 |
源表被删除 (DROP TABLE) | 虚拟表查询报错(引用源不存在) |
源表列被删除 (ALTER TABLE DROP COLUMN) | 虚拟表查询报错(引用列不存在) |
| 虚拟表自身被删除 | 引用它的其他虚拟表查询报错 |
| 源表标签列类型变更 | 不允许:有 tag-ref 依赖时 ALTER 会拒绝 |
限制
- tag-ref 只能引用标签列,不能引用数据列;源标签与目标标签数据类型必须一致。
- col-ref 只能引用数据列,不能引用标签列;源列与目标列数据类型必须一致。
- 不支持引用超级表、视图,不支持引用有复合主键的表。
- 引用链中不允许出现环(A→B→A),创建时会校验。
- 引用深度上限为 32 跳。
查询虚拟表
虚拟表与正常表无论是查询语法还是范围都没有区别,不同之处在于虚拟表所呈现的数据集在不同的查询中可能是不相同的,具体可以参考虚拟表数据生成规则。
虚拟表数据生成规则
- 虚拟表以时间戳为基准,对多个原始表的数据进行对齐。
- 如果多个原始表在相同时间戳下有数据,则这些列的值组合成同一行;否则,对于缺失的列,填充 NULL。
- 虚拟表的时间戳的值是查询中包含的所有列所在的原始表的时间戳的并集,因此当不同查询选择列不同时可能出现结果集行数不一样的情况。
- 用户可以从多个表中选择任意列进行组合,未选择的列不会出现在虚拟表中。
示例 :
假设有表 t1、t2、t3 结构和数据如下:
| t1 | t2 | t3 | ||||||
|---|---|---|---|---|---|---|---|---|
| ts | value | ts | value | ts | value1 | value2 | ||
| 0:00:01 | 1 | |||||||
| 0:00:02 | 20 | |||||||
| 0:00:03 | 300 | 3000 | ||||||
| 0:00:04 | 4 | 0:00:04 | 40 | |||||
| 0:00:05 | 50 | 0:00:05 | 500 | 5000 | ||||
并且有虚拟普通表 v1,创建方式如下:
create vtable v1 (
ts timestamp,
c1 int from t1.value,
c2 int from t2.value,
c3 int from t3.value1,
c4 int from t3.value2);
那么根据虚拟表对于多表数据的整合规则,执行如下查询时:
select * from v1;
结果如下:
| v1 | ||||
|---|---|---|---|---|
| ts | c1 | c2 | c3 | c4 |
| 0:00:01 | 1 | |||
| 0:00:02 | 20 | |||
| 0:00:03 | 300 | 3000 | ||
| 0:00:04 | 4 | 40 | ||
| 0:00:05 | 50 | 500 | 5000 | |
如果没有选择全部列,只是选择了部分列,查询的结果只会包含选择的列的原始表的时间戳,例如执行如下查询:
select c1, c2 from v1;
得到的结果如下图所示:
| v1 | ||||
|---|---|---|---|---|
| ts | c1 | c2 | ||
| 0:00:01 | 1 | |||
| 0:00:02 | 20 | |||
| 0:00:04 | 4 | 40 | ||
| 0:00:05 | 50 | |||
因为 c1、c2 列对应的原始表 t1、t2 中没有 0:00:03 这个时间戳,所以最后的结果也不会包含这个时间戳。
修改虚拟普通表
ALTER VTABLE [db_name.]vtb_name alter_table_clause
alter_table_clause: {
ADD COLUMN vtb_col_name vtb_column_type [FROM table_name.col_name]
| DROP COLUMN vtb_col_name
| ALTER COLUMN vtb_col_name SET {table_name.col_name | NULL }
| MODIFY COLUMN col_name column_type
| RENAME COLUMN old_col_name new_col_name
}
使用说明
对虚拟普通表可以进行如下修改操作
- ADD COLUMN:添加列。
- DROP COLUMN:删除列。
- MODIFY COLUMN:修改列定义,如果数据列的类型是可变长类型,那么可以使用此指令修改其宽度,只能改大,不能改小。如果虚拟表该列已指定数据源,那么修改列宽会因为修改后的列宽和数据源的列宽不匹配而报错,可以先将数据源置为空后再修改列宽。
- RENAME COLUMN:修改列名称。
- ALTER COLUMN .. SET:修改列的数据源。SET NULL 表示将虚拟表某列的数据源置为空。
增加列
ALTER VTABLE vtb_name ADD COLUMN vtb_col_name vtb_col_type [FROM [db_name].table_name.col_name]
删除列
ALTER VTABLE vtb_name DROP COLUMN vtb_col_name
修改列宽
ALTER VTABLE vtb_name MODIFY COLUMN vtb_col_name data_type(length);
修改列名
ALTER VTABLE vtb_name RENAME COLUMN old_col_name new_col_name
修改列的数据源
ALTER VTABLE vtb_name ALTER COLUMN vtb_col_name SET {[db_name.]table_name.col_name | NULL}
修改虚拟子表
ALTER VTABLE [db_name.]vtb_name alter_table_clause
alter_table_clause: {
ALTER COLUMN vtb_col_name SET table_name.col_name
| SET TAG tag_name = {new_tag_value | [db_name.]table_name.tag_name}
}
使用说明 :
- 对虚拟子表的列和标签的修改,除了更改标签值以外,都要通过虚拟超级表才能进行。
修改虚拟子表标签值
ALTER VTABLE tb_name SET TAG tag_name1=new_tag_value1, tag_name2=new_tag_value2 ...;
SET TAG 既可以把标签改成静态字面量值,也可以把标签改成 tag-ref(标签引用)。
把标签设置为字面量
ALTER VTABLE v0 SET TAG local_tag='local0_updated';
将标签设置为字面量值时,如果该标签原本是 tag-ref,则其引用关系会被清除,之后该标签变为一个静态值,不再随源表变化。
把标签设置为 tag-ref(设置或重设引用)
-- 同库引用:引用同库中 src0 表的 city 标签
ALTER VTABLE v0 SET TAG ref_city=src0.city;
-- 跨库引用:使用 db_name.table.tag 三段式
ALTER VTABLE v0 SET TAG ref_city=db1.src1.city;
将标签设置为 tag-ref 后,查询时会实时解析为被引用标签的当前值。该操作既可以为一个原本是字面量的标签新增引用,也可以把一个已有的 tag-ref 重新指向另一个源标签(重设引用)。约束与 CREATE VTABLE 时的 tag-ref 一致:
- 被引用对象必须是标签列(普通子表或虚拟子表的标签),不能是数据列。
- 源标签与目标标签的数据类型必须一致。
- 不允许形成引用环(例如把
v_a的标签指向最终又引用回v_a的虚拟表),创建/修改时会校验并报错。 - 引用链总深度不能超过 32 跳,超过会返回错误码
0x8000620C。
多跳引用链下的重设行为
当虚拟子表的标签是多跳引用链的一环时(例如 v2_0.l2_ref_city -> v0.ref_city -> src0.city),可以在任意一层用 SET TAG 调整引用,变更会按照动态绑定的规则在查询时向上传导:
- 重设链路中间层的引用(如
ALTER VTABLE v0 SET TAG ref_city=src1.city),上层引用它的虚拟表查询结果会随之改变。 - 重设最上层的引用(如
ALTER VTABLE v2_0 SET TAG l2_ref_city=db.v1.ref_city),可以让其指向另一条引用链,甚至直接指向物理表标签从而"压平"引用链。 - 把链路中间层的标签设置为字面量,会清除该层的引用,从而切断上层对原始物理源的传导,上层将解析为该字面量值。
说明:通过虚拟超级表批量修改标签的形式
ALTER VTABLE USING stb_name SET TAG ... WHERE ...只接受字面量值,不支持把标签设置为 tag-ref。如需设置或重设 tag-ref,请使用上面针对单个虚拟子表的ALTER VTABLE vtb_name SET TAG ...语法。
修改列的数据源
ALTER VTABLE vtb_name ALTER COLUMN vtb_col_name SET {[db_name.]table_name.col_name | NULL}
删除虚拟表
DROP VTABLE [IF EXISTS] [dbname].vtb_name;
查看虚拟表的信息
显示某个数据库下所有虚拟表
如下 SQL 语句可以列出当前数据库中的所有虚拟表名。
SHOW [NORMAL | CHILD] [db_name.]VTABLES [LIKE 'pattern'];
使用说明 :
- 如果没有指定 db_name,显示当前数据库下的所有虚拟普通表和虚拟子表的信息。若没有使用数据库并且没有指定 db_name, 则会报错 database not specified。可以使用 LIKE 对表名进行模糊匹配。NORMAL 指定只显示虚拟普通表信息,CHILD 指定只显示虚拟子表信息。
SHOW TABLES不会返回这里展示的虚拟普通表和虚拟子表,请使用SHOW VTABLES查看。
显示虚拟表创建语句
SHOW CREATE VTABLE [db_name.]vtable_name;
显示 vtable_name 指定的虚拟表的创建语句。支持虚拟普通表和虚拟子表。常用于数据库迁移。对一个已经存在的虚拟表,返回其创建语句;在另一个集群中执行该语句,就能得到一个结构完全相同的虚拟表。 对于使用 tag-ref 创建的虚拟子表,返回结果会保留对应的标签引用定义。
获取虚拟表结构信息
DESCRIBE [db_name.]vtb_name;
DESCRIBE 会展示虚拟表的列与标签结构;对于 tag-ref/col-ref,结果中会包含对应的引用来源信息。
查看虚拟子表当前标签值
SHOW TAGS FROM child_table_name [FROM db_name];
SHOW TAGS FROM [db_name.]child_table_name;
对于使用 tag-ref 的虚拟子表,SHOW TAGS 返回的是当前解析后的标签值。
校验虚拟表引用关系
SHOW VTABLE VALIDATE FOR [db_name.]vtb_name;
SHOW VTABLE VALIDATE 用于检查虚拟普通表或虚拟子表的列/标签引用关系,并返回与 information_schema.ins_virtual_tables_referencing 一致的校验结果(包括 err_code、err_msg)。
查看虚拟普通表和虚拟子表信息
SELECT ... FROM information_schema.ins_tables where type = 'VIRTUAL_NORMAL_TABLE' or type = 'VIRTUAL_CHILD_TABLE';
SELECT ... FROM information_schema.ins_virtual_tables_referencing;
其中 ins_virtual_tables_referencing 可用于批量查询虚拟表列/标签的源库、源表、源列以及校验状态。
写入虚拟表
不支持向虚拟表中写入数据,以及不支持删除虚拟表中的数据。虚拟表只是对原始表进行运算后的计算结果,是一张逻辑表,因此只能对其进行查询,不可以写入或删除数据。
虚拟表与视图
虚拟表与视图看起来相似,但是有很多不同点:
| 属性 | 虚拟表 (Virtual Table) | 视图 (View) |
|---|---|---|
| 定义 | 虚拟表是一种动态数据结构,根据多表的列和时间戳组合规则生成逻辑表。 | 视图是一种基于 SQL 查询的虚拟化表结构,用于保存查询逻辑的定义。 |
| 数据来源 | 来自多个原始表,可以动态选择列,并通过时间戳对齐数据。 | 来自单个或多个表的查询结果,通常是一个复杂的 SQL 查询。 |
| 数据存储 | 不实际存储数据,所有数据在查询时动态生成。 | 不实际存储数据,仅保存 SQL 查询逻辑。 |
| 时间戳处理 | 通过时间戳对齐将不同表的列整合到统一的时间轴上。 | 不支持时间戳对齐,数据由查询逻辑直接决定。 |
| 更新机制 | 动态更新,原始表数据变更时,虚拟表数据实时反映变化。 | 动态更新,但依赖于视图定义的查询逻辑,不涉及对齐或数据整合。 |
| 功能特性 | 支持空值填充和插值(如 prev、next、linear)。 | 不支持内置填充和插值功能,需通过查询逻辑自行实现。 |
| 应用场景 | 时间序列对齐、跨表数据整合、多源数据对比分析等场景。 | 简化复杂查询逻辑、限制用户访问、封装业务逻辑等场景。 |
| 性能 | 由于多表对齐和空值处理,查询复杂度可能较高,尤其在数据量大时。 | 性能通常取决于视图的查询语句复杂度,与单表查询性能相似。 |
不支持虚拟表和视图之间的相互转化,如根据虚拟表建立视图或者根据视图建立虚拟表。
虚拟表的权限
权限说明
虚拟表的权限分为 READ、WRITE 两种,查询操作需要具备 READ 权限,对虚拟表本身的删除和修改操作需要具备 WRITE 权限。
语法
授权
GRANT privileges ON [db_name.]vtable_name TO user_name
privileges: {
ALL,
| priv_type [, priv_type] ...
}
priv_type: {
READ
| WRITE
}
回收权限
REVOKE privileges ON [db_name.]vtable_name FROM user_name
privileges: {
ALL,
| priv_type [, priv_type] ...
}
priv_type: {
READ
| WRITE
}
权限规则
- 虚拟表的创建者和 root 用户默认具备所有权限。
- 用户可以通过 dbname.vtbname 来为指定的虚拟表(包括虚拟超级表和虚拟普通表)授予或回收其读写权限,不支持直接对虚拟子表授予或回收权限。
- 虚拟子表和虚拟超级表不支持基于标签的授权(表级授权),虚拟子表继承虚拟超级表的权限。
- 对其他用户进行授权与回收权限可以通过 GRANT 和 REVOKE 语句进行,该操作只能由 root 用户进行。
- 具体相关权限控制细则总结如下:
| 序号 | 操作 | 权限要求 |
|---|---|---|
| 1 | CREATE VTABLE | 用户对虚拟表所属数据库有 WRITE 权限 且 用户对虚拟表数据源对应的源表或源虚拟表有 READ 权限。 |
| 2 | DROP/ALTER VTABLE | 用户对虚拟表有 WRITE 权限;若在操作中指定列引用或标签引用的数据源,需要同时对对应的源表或源虚拟表有 READ 权限。 |
| 3 | SHOW VTABLES | 无 |
| 4 | SHOW CREATE VTABLE | 无 |
| 5 | DESCRIBE VTABLE | 无 |
| 6 | 系统表查询 | 无 |
| 7 | SELECT FROM VTABLE | 操作用户对虚拟表有 READ 权限 |
| 8 | GRANT/REVOKE | 只有 root 用户有权限 |
使用场景
| SQL 查询 | SQL 写入 | STMT 查询 | STMT 写入 |
|---|---|---|---|
| 支持 | 不支持 | 后续支持 | 不支持 |








