
概述
视图(View)是 TDengine 企业版自 v3.2.1.0 起提供的一项功能,旨在简化复杂查询操作、增强查询逻辑复用能力、细化用户权限管控粒度,并便于团队间共享数据访问逻辑。
视图的本质是一条存储在数据库中、并被命名的 SQL 查询语句。视图本身不持久化任何数据(即非物化视图),每次从视图读取数据时,都会动态执行视图所定义的查询。用户可以像查询普通表一样查询视图,极大地降低了使用复杂 SQL 的门槛。
为什么需要视图
在实际的物联网、工业互联网和运维监控场景中,数据以时间序列方式持续采集,往往面临如下挑战:
1. 查询逻辑复杂、难以复用
针对超级表的聚合、降采样、多表关联等查询,SQL 语句往往非常复杂。如果每个业务方都需要单独编写和维护这些查询,既重复劳动又容易出错。
视图将这些复杂查询封装为一个命名对象,业务方只需 SELECT * FROM view_name 即可获得所需数据,无需关心底层查询的实现细节。
2. 权限控制粒度不足
在多租户或多部门的场景中,不同用户往往只应访问特定范围的数据。原始表的权限控制粒度通常是整张表,而视图可以将一个表的特定列或特定子集以独立对象的方式暴露给指定用户,实现行/列级别的逻辑隔离。
视图同时引入了有效用户(视图创建者)机制:被授予视图 READ 权限的用户,可以借助创建者的表权限来查询视图,即便该用户对底层表没有直接访问权限。
3. 跨库数据整合
TDengine 视图支持跨数据库定义,可以在一个视图中关联来自不同数据库的表,形成统一的逻辑查询入口,使上层应用无需关心数据分布在哪个库中。
4. 支持订阅消费(TMQ)
视图支持 TDengine 的消息队列订阅(TMQ)功能,可以将视图作为数据订阅的数据源,方便下游数据消费者以流式方式持续获取经过逻辑封装的数据。
适用场景
| 场景 | 说明 |
|---|---|
| 简化复杂查询 | 将多表 JOIN、聚合、窗口函数等复杂 SQL 封装为视图,业务层只需简单 SELECT |
| 封装业务逻辑 | 将带有业务含义的计算逻辑(如设备在线率、平均响应时间)固化为视图 |
| 权限隔离(数据脱敏) | 通过视图只暴露部分列或数据子集,隐藏敏感字段,控制不同用户的数据访问范围 |
| 跨库视图 | 跨越多个数据库关联数据,统一对外呈现,简化应用层的数据库选择逻辑 |
| 数据订阅(TMQ) | 以视图作为订阅 Topic 的数据源,消费经过业务逻辑过滤/转换后的数据流 |
| 嵌套视图 | 在视图之上建立新视图,逐层抽象,将复杂的多级处理逻辑拆分为可读性高的层次结构 |
| 降采样/聚合展示 | 将原始高频数据通过 INTERVAL 窗口聚合后封装为视图,上层展示平台直接查询低频聚合结果 |
快速入门
第一步:创建视图
视图绑定到一个数据库,创建时需要 USE 某数据库或显式指定 db_name。
sql
-- 使用当前数据库
USE power;
-- 创建一个对超级表做日聚合的视图
CREATE VIEW daily_avg_voltage AS
SELECT _wstart, tbname, avg(voltage) AS avg_vol
FROM meters
INTERVAL(1d);
-- 创建一个只暴露部分列的视图(用于权限隔离)
CREATE VIEW public_meters AS
SELECT ts, current, voltage
FROM meters;
-- 创建跨库视图
CREATE VIEW power.cross_view AS
SELECT a.ts, a.voltage, b.temperature
FROM power.meters a, env.sensors b
WHERE a.ts = b.ts;
第二步:查询视图
查询视图与查询普通表语法完全相同:
sql
-- 直接查询
SELECT * FROM daily_avg_voltage;
-- 加条件过滤
SELECT * FROM daily_avg_voltage WHERE _wstart > '2024-01-01';
-- 跨库查询视图
SELECT * FROM power.cross_view;
第三步:查看视图信息
sql
-- 查看当前数据库下所有视图
SHOW VIEWS;
-- 查看指定数据库的视图
SHOW power.VIEWS;
-- 查看视图的创建语句
SHOW CREATE VIEW daily_avg_voltage;
-- 查看视图的列信息
DESCRIBE daily_avg_voltage;
-- 通过系统表查询所有视图
SELECT * FROM information_schema.ins_views;
第四步:更新视图
使用 CREATE OR REPLACE VIEW 可以原地更新一个视图的定义,无需先删除再创建:
sql
CREATE OR REPLACE VIEW daily_avg_voltage AS
SELECT _wstart, tbname, avg(voltage) AS avg_vol, max(voltage) AS max_vol
FROM meters
INTERVAL(1d);
第五步:删除视图
sql
DROP VIEW IF EXISTS daily_avg_voltage;
进阶用法
嵌套视图
视图可以引用其他视图,支持多层嵌套,便于将复杂逻辑拆分为多个步骤:
sql
-- 第一层:基础数据视图
CREATE VIEW view_raw AS
SELECT ts, tbname, voltage, current
FROM meters;
-- 第二层:在上一层基础上做聚合
CREATE VIEW view_avg AS
SELECT _wstart, tbname, avg(voltage) avg_v, avg(current) avg_c
FROM view_raw
INTERVAL(1h)
PARTITION BY tbname;
-- 第三层:在聚合结果上做进一步筛选
CREATE VIEW view_anomaly AS
SELECT * FROM view_avg
WHERE avg_v > 250 OR avg_c > 100;
-- 最终用户只需查询 view_anomaly
SELECT * FROM view_anomaly;
多表关联视图
视图可以封装复杂的 JOIN 逻辑,对外暴露联合后的结果:
sql
-- 关联作业信息表与测试结果超级表
CREATE VIEW abc AS
SELECT *
FROM (
SELECT
a.job_id,
a.start_time AS job_start_time,
a.finish_time AS job_end_time,
a.job_status,
b.qps,
b.avg_delay,
b.hostname
FROM job_info a, test_results b
WHERE a.start_time = b.job_id_ts
AND a.job_status = 'finished'
) s1
INNER JOIN resource_info s2
ON s1.job_start_time = s2.job_id_ts;
-- 直接查询联合结果
SELECT * FROM abc;
视图作为数据源进行写入
可以将视图查询的结果写入另一张表:
sql
-- 创建目标表
CREATE TABLE daily_result (
ts TIMESTAMP,
tbname VARCHAR(64),
avg_vol FLOAT
);
-- 通过视图将聚合结果写入普通表
INSERT INTO daily_result
SELECT * FROM daily_avg_voltage;
注意 :不支持直接向视图
INSERT数据(如INSERT INTO view_name VALUES ...)。
视图用于订阅(TMQ)
视图可以作为 TDengine TMQ(消息队列)的订阅主题数据源,适合需要对原始数据进行预处理后再消费的场景:
sql
-- 创建视图
CREATE VIEW filtered_meters AS
SELECT ts, current, voltage
FROM meters
WHERE voltage > 200;
-- 以该视图建立订阅 Topic
CREATE TOPIC meters_topic AS SELECT * FROM filtered_meters;
权限管理
权限类型
| 权限类型 | 说明 |
|---|---|
READ |
可以查询(SELECT)视图 |
WRITE |
可以将数据写入视图关联的底层表 |
ALTER |
可以删除或替换视图定义 |
权限规则
- 视图的创建者 和
root用户默认拥有视图的所有权限。 - 权限的授予(
GRANT)和回收(REVOKE)只能由root用户执行。 - 通过
db.*对数据库整体的授权不包含该数据库内视图的权限,视图权限需独立授予。 - 视图可以嵌套,嵌套视图的权限校验也是递归进行的。
有效用户机制
视图引入了"有效用户"(视图的创建者)的概念:
- 被授予
READ权限的普通用户 B,即便对底层表没有直接权限,也可以借助视图创建者(有效用户)的表读权限来查询该视图。 - 视图被
CREATE OR REPLACE更新后,有效用户会更新为新的创建者。
示例:
sql
-- root 创建视图(视图有效用户为 root,root 对底层表有读权限)
CREATE VIEW power.public_meters AS
SELECT ts, voltage FROM meters;
-- 授予用户 u1 视图的 READ 权限(u1 本身对 meters 表没有直接权限)
GRANT SELECT ON VIEW power.public_meters TO u1;
-- u1 仍可成功查询视图,因为有效用户 root 对 meters 有读权限
-- (以 u1 身份执行)
SELECT * FROM power.public_meters;
授权语法
sql
-- 授权
GRANT privileges ON [db_name.]view_name TO user_name;
privileges := ALL | priv_type [, priv_type] ...
priv_type := READ | WRITE | ALTER
-- 回收权限
REVOKE privileges ON [db_name.]view_name FROM user_name;
创建视图的权限要求
| 操作 | 权限要求 |
|---|---|
CREATE VIEW |
对视图所属数据库有 WRITE 权限,且对视图查询中的目标库、表、视图有查询权限 |
CREATE OR REPLACE VIEW |
额外需要对旧视图有 ALTER 权限 |
DROP VIEW |
对视图有 ALTER 权限 |
SELECT FROM VIEW |
对视图有 READ 权限,且视图有效用户对底层对象有 READ 权限 |
SHOW / DESCRIBE VIEW |
无权限要求 |
GRANT / REVOKE |
仅 root 用户 |
使用限制
| 支持的操作 | 说明 |
|---|---|
| SQL 查询(SELECT) | ✅ 支持 |
| 数据订阅(TMQ) | ✅ 支持 |
| SQL 写入(INSERT INTO VIEW) | ❌ 暂不支持 |
| STMT 协议查询/写入 | ❌ 暂不支持 |
| 流计算(STREAM) | ❌ 暂不支持 |
其他注意事项:
- 视图名在同一个数据库内不能重复;视图名与表名推荐不重名(不强制),若重名则优先使用同名表。
- 视图与其所绑定的数据库关联,跨库使用时需显式指定
db_name.view_name。 - 视图不支持
GROUP BY ... INTERVAL(...)同时使用的语法(即聚合与时间窗口不能在同一 GROUP BY 子句中)。 - 不支持将虚拟表(VTABLE)作为视图的数据源,也不支持将视图作为虚拟表的数据源。
- 视图定义中推荐对涉及的表使用完整的
db_name.table_name格式,避免因USE切换数据库时产生歧义。
视图与虚拟表的区别
TDengine 同时提供了视图(View)和虚拟表(Virtual Table)两种逻辑表,它们虽然相似,但定位和使用场景有明显差异:
| 属性 | 视图(View) | 虚拟表(Virtual Table) |
|---|---|---|
| 定义方式 | 基于 SQL 查询语句 | 基于多表列的映射关系 |
| 数据来源 | 单表或多表的查询结果 | 多张原始表的列,按时间戳对齐组合 |
| 时间戳处理 | 由查询逻辑决定,不做对齐 | 自动对多源时间戳做并集对齐 |
| 空值填充 | 不内置,需在 SQL 中自行实现 | 内置支持 FILL(prev、next、linear) |
| 适用场景 | 封装业务逻辑、权限隔离、简化查询 | 多源时间序列对齐、异构数据融合 |
| 写入支持 | 不支持写入视图 | 不支持写入虚拟表 |
| 订阅支持 | ✅ 支持 TMQ | ❌ 不支持 |
| 流计算支持 | ❌ 暂不支持 | ✅ 支持 |
简单来说:视图 更适合封装查询逻辑和权限管控;虚拟表更适合多源时间序列数据的对齐融合。
典型案例
案例一:降采样聚合视图
监控系统中原始数据每秒一条,上层大屏展示每小时平均值时不希望每次都重新计算:
sql
-- 创建每小时聚合视图
CREATE VIEW hourly_summary AS
SELECT
_wstart AS ts,
tbname,
avg(current) AS avg_current,
avg(voltage) AS avg_voltage,
max(current) AS peak_current
FROM meters
PARTITION BY tbname
INTERVAL(1h);
-- 大屏直接查询,逻辑简洁
SELECT * FROM hourly_summary
WHERE ts >= NOW - 7d
ORDER BY ts;
案例二:权限隔离(数据脱敏)
某系统中 devices 表包含设备位置(longitude、latitude)等敏感信息,普通运维人员只需看到运行状态:
sql
-- root 创建视图,隐藏敏感列
CREATE VIEW ops_devices AS
SELECT ts, device_id, status, temperature, pressure
FROM devices;
-- 授予运维用户查询权限
GRANT SELECT ON VIEW ops_devices TO ops_user;
-- ops_user 只能看到运行状态,无法获取位置信息
案例三:多层嵌套视图实现复杂分析
生产线设备数据的多层分析:
sql
-- 第1层:基础过滤
CREATE VIEW active_devices AS
SELECT ts, device_id, output, temperature
FROM production_line
WHERE status = 'running';
-- 第2层:小时聚合
CREATE VIEW hourly_output AS
SELECT _wstart AS ts, device_id,
sum(output) AS total_output,
avg(temperature) AS avg_temp
FROM active_devices
PARTITION BY device_id
INTERVAL(1h);
-- 第3层:标记异常
CREATE VIEW abnormal_output AS
SELECT * FROM hourly_output
WHERE avg_temp > 85 OR total_output < 100;
-- 应用层直接查看异常设备
SELECT * FROM abnormal_output WHERE ts > NOW - 24h;
案例四:跨库数据关联
将来自不同数据库(电力数据库、环境数据库)的数据统一对外展示:
sql
-- 在 analytics 数据库中创建跨库视图
CREATE VIEW analytics.device_env AS
SELECT
p.ts,
p.device_id,
p.voltage,
p.current,
e.temperature AS env_temp,
e.humidity AS env_humidity
FROM power.meters p, environment.sensors e
WHERE p.ts = e.ts
AND p.location_id = e.location_id;
-- 统一查询
SELECT * FROM analytics.device_env
WHERE ts > NOW - 1h;
常见问题
Q:视图会缓存数据吗?
A:不会。TDengine 目前只支持非物化视图,每次查询视图时都会动态执行视图内的 SQL。
Q:可以对视图做 INSERT 写入吗?
A:暂不支持直接向视图插入数据。可以将查询视图的结果 INSERT 到另一张普通表中。
Q:删除底层表后,视图会怎样?
A:视图定义仍然保留,但查询时会报错(找不到数据源)。需要手动删除或更新视图。
Q:视图名与表名重名时如何处理?
A:优先使用同名的表。写入、查询、授权等操作均以表为准。建议避免视图与表同名。
Q:普通用户能创建视图吗?
A:普通用户需要对视图所属数据库有 WRITE 权限并被授予 CREATE VIEW 权限,才能创建视图。
Q:视图支持流计算(Stream)吗?
A:暂不支持以视图为数据源创建流计算(STREAM),目前视图主要用于查询和订阅(TMQ)场景。
关于 TDengine
TDengine 专为物联网IoT平台、工业大数据平台设计。其中,TDengine TSDB 是一款高性能、分布式的时序数据库(Time Series Database),同时它还带有内建的缓存、流式计算、数据订阅等系统功能;TDengine IDMP 是一款AI原生工业数据管理平台,它通过树状层次结构建立数据目录,对数据进行标准化、情景化,并通过 AI 提供实时分析、可视化、事件管理与报警等功能。