分类:10.SQL 参考 | 篇章:04 JOIN 完整语法
适用版本:TDengine v3.x(v3.3.x / v3.4.x) | 最后更新:2026-07-17
TDengine JOIN 在标准 SQL 基础上扩展了时序专属的 ASOF 和 Window JOIN。本文是 JOIN 语法的完整参考,含每种类型的语义、条件约束、典型用例。
JOIN 类型速查表
| 类型 | 关键字 | 行为 |
|---|---|---|
| Inner | JOIN / INNER JOIN | 双侧匹配 |
| Left Outer | LEFT JOIN / LEFT OUTER JOIN | 保留左侧 |
| Right Outer | RIGHT JOIN / RIGHT OUTER JOIN | 保留右侧 |
| Full Outer | FULL JOIN / FULL OUTER JOIN | 保留双侧 |
| Semi | LEFT SEMI JOIN | 左侧存在匹配则保留 |
| Anti | LEFT ANTI JOIN | 左侧无匹配则保留 |
| ASOF | LEFT ASOF JOIN | 时间近邻 |
| Window | WINDOW JOIN | 时间窗口对齐 |
详细解析
1. 时间条件约束(重要)
所有 JOIN 必须满足时间相关性:
✓ 普通 JOIN:必须含 t1.ts = t2.ts 或时间等值
✓ ASOF JOIN:必须含 t1.ts <op> t2.ts 时间比较
✓ WINDOW JOIN:内置时间窗口
✗ 不允许的 JOIN:
SELECT * FROM t1 JOIN t2 ON t1.id = t2.id; -- 缺时间
2. Inner JOIN
-- 基本
SELECT t1.ts, t1.current, t2.power
FROM electric_meters t1
JOIN power_meters t2 ON t1.ts = t2.ts;
-- 多条件
SELECT * FROM t1 JOIN t2
ON t1.ts = t2.ts
AND t1.location = t2.location;
-- 多表(注意性能)
SELECT * FROM t1 JOIN t2 ON t1.ts = t2.ts
JOIN t3 ON t1.ts = t3.ts;
语义:
保留 ON 条件同时满足的行
双侧 ts 都存在且相等
3. Outer JOIN
-- LEFT JOIN
SELECT t1.ts, t1.current, t2.power
FROM electric_meters t1
LEFT JOIN power_meters t2 ON t1.ts = t2.ts;
-- t1 保留所有,t2 无匹配为 NULL
-- RIGHT JOIN
SELECT t1.ts, t1.current, t2.power
FROM electric_meters t1
RIGHT JOIN power_meters t2 ON t1.ts = t2.ts;
-- t2 保留所有
-- FULL OUTER JOIN
SELECT t1.ts, t1.current, t2.power
FROM electric_meters t1
FULL JOIN power_meters t2 ON t1.ts = t2.ts;
-- 双侧都保留
4. Semi/Anti JOIN
-- SEMI JOIN: 左侧存在匹配则保留(去重)
SELECT * FROM users u
LEFT SEMI JOIN orders o ON u.id = o.user_id AND u.ts = o.ts;
-- 等价于:SELECT * FROM users WHERE id IN (SELECT user_id FROM orders)
-- ANTI JOIN: 左侧无匹配则保留
SELECT * FROM users u
LEFT ANTI JOIN orders o ON u.id = o.user_id AND u.ts = o.ts;
-- 等价于:SELECT * FROM users WHERE id NOT IN (...)
5. ASOF JOIN(时间近邻)
-- LEFT ASOF JOIN:每个左侧 ts 找右侧近邻
SELECT a.ts, a.current, b.standard_voltage
FROM realtime a
LEFT ASOF JOIN reference b
ON a.ts >= b.ts
AND a.location = b.location;
-- 支持比较:>= > <= <
-- >= 找 <= 当前的最大者
-- <= 找 >= 当前的最小者
-- 限制条件:必须有时间比较
LEFT ASOF JOIN ... ON a.ts >= b.ts [AND tag_eq_conditions]
-- 多列等值(Tag 关联)
LEFT ASOF JOIN reference b
ON a.ts >= b.ts
AND a.factory = b.factory
AND a.line = b.line;
6. WINDOW JOIN(时间窗口)
-- 找出每个事件前后窗口内的关联记录
SELECT t.ts AS event_ts, h.ts AS related_ts, t.value, h.value
FROM events t
WINDOW JOIN history h
WINDOW(1m) -- 窗口大小 ±30s
ON t.location = h.location
WHERE t.value > 100;
-- 窗口语义:
-- 每个 t 的 ts 为中心,[ts - window/2, ts + window/2]
-- 找出 h 中落在该窗口的所有行
-- 一对多关系
7. 子查询 + JOIN
-- 与聚合子查询 JOIN
SELECT m.ts, m.current, h.hist_avg
FROM meters m
JOIN (
SELECT tbname, AVG(current) AS hist_avg
FROM meters
WHERE ts < now-1d
PARTITION BY tbname
) h ON m.tbname = h.tbname
WHERE m.ts > now-1h;
8. JOIN 性能限制
注意:
① TDengine 推荐 JOIN 数量 ≤ 5
② 时间窗口必须明确(不能 ts > 0)
③ 同 VGroup 子表 JOIN 最优
④ 跨 VGroup 大表 JOIN 需 Shuffle,慎用
高效 JOIN 模式:
- 两侧都有时间过滤
- 两侧都有 Tag 过滤
- 时间和 Tag 条件配对
低效 JOIN:
- 无时间过滤的全表 JOIN
- 多张超级表全字段 JOIN
- JOIN 中含复杂表达式
代码示例
综合场景
-- 同设备两类传感器对齐
SELECT
t.ts, t.current AS amperage,
v.voltage AS voltage,
t.current * v.voltage AS power
FROM current_sensor t
JOIN voltage_sensor v
ON t.ts = v.ts AND t.device_id = v.device_id
WHERE t.ts > now-1h;
-- 高频数据对齐低频参考
SELECT
m.ts, m.value, r.threshold
FROM minutely_data m
LEFT ASOF JOIN reference_data r
ON m.ts >= r.ts AND m.device_type = r.device_type
WHERE m.value > r.threshold
AND m.ts > now-1d;
-- 事件前后关联
SELECT
a.ts AS alarm_ts,
m.ts AS measure_ts,
m.current
FROM alarm a
WINDOW JOIN meters m
WINDOW(5m)
ON a.device_id = m.tbname
WHERE a.severity = 'CRITICAL'
AND a.ts > now-1d;
与子查询配合
-- TOP N + JOIN 详情
SELECT top.tbname, top.avg_c, m.ts, m.current
FROM (
SELECT tbname, AVG(current) AS avg_c
FROM meters
WHERE ts > now-1d
PARTITION BY tbname
ORDER BY avg_c DESC
LIMIT 10
) top
JOIN meters m ON top.tbname = m.tbname
WHERE m.ts > now-1h;
性能考量
JOIN 性能影响
| 因素 | 影响 |
|---|---|
| 双侧时间过滤 | 决定扫描量 |
| Tag 过滤 | 决定子表数 |
| JOIN 表数量 | 复杂度指数级 |
| 跨 VGroup | 需要 Shuffle |
| ASOF 的输入有序 | 输入有序时 O(N+M) |
优化建议
| 建议 | 原因 |
|---|---|
| 限制时间范围 | 关键 |
| 列出具体列 | 减少传输 |
| JOIN 不超过 3 表 | 避免组合爆炸 |
| 大表 + 小表用 INNER | 利于 Hash Join |
| 数据局部化 | 避免 Shuffle |
FAQ
Q1: 为什么 JOIN 必须有时间条件?
时序数据按时间分布,无时间条件的 JOIN 等同于笛卡尔积,开销极大。强制时间条件防止误用。
Q2: ASOF JOIN 性能如何?
输入按 ts 有序时(时序天然如此),用 Merge 风格 O(N+M)。生产环境百万行毫秒~秒级。
Q3: 一条 JOIN 能跨数据库吗?
支持跨数据库:FROM db1.t1 JOIN db2.t2 ON ...。但跨集群 JOIN 不支持。
Q4: JOIN 结果集很大如何处理?
- 加 LIMIT
- 加更严格的过滤条件
- 改用流计算预先 JOIN
Q5: JOIN 中的 NULL 处理?
NULL 不与 NULL 相等。Outer JOIN 中无匹配的列填 NULL。
参考
系统构架篇
- 01-《TDengine 整体架构全景》
- 02-《集群拓扑深度解析》
- 03-《MNode 内部机制深度解析》
- 04-《RPC 通信层深度解析》
- 05-《VNode 生命周期》
- 06-《RAFT 共识协议》
- 07-《端到端的消息流》
数据模型
- 01-《数据库创建与参数详解》
- 02-《超级表/子表/普通表》
- 03-《支持数据类型深度解析》
- 04-《TDengine Tag 设计哲学与 Schema 变更机制》
- 05-《TDengine 虚拟表实现原理》
存储引擎
- 01-《TDengine 存储引擎概览》
- 02-《TDengine MemTable 深度解析》
- 03-《TDengine WAL 预写日志机制》
- 04-《TDengine 数据文件格式》
- 05-《TDengine Commit 与 Flush 机制 》
- 06-《TDengine Compaction 合并策略 》
- 07-《TDengine 数据保留与 TTL》
- 08-《TDengine 压缩编码机制》
- 09-《TDengine Cache 与 Last 查询加速》
- 10-《TDengine 逻辑计划生成》
查询引擎
- 01-《TDengine 查询引擎概览》
- 02-《TDengine SQL 解析与词法分析》
- 03-《TDengine 语义分析与 AST 重写》
- 04-《TDengine 逻辑计划生成》
- 05-《TDengine 物理计划生成》
- 06-《TDengine 扫描算子》
- 07-《TDengine 聚合算子》
- 08-《TDengine 连接算子》
- 09-《TDengine 排序、填充与投影》
- 10-《TDengine 分布式查询执行》
- 11-《TDengine EXPLAIN 与查询优化》
数据写入
- 01-《TDengine SQL INSERT》
- 02-《TDengine 无模式写入》
- 03-《TDengine STMT 写入》
- 04-《TDengine 写入内部流程》
- 05-《TDengine 数据更新删除》
数据订阅
- 01-《TDengine 数据订阅》
- 02-《TDengine 订阅 vs Kafka》
- 03-《TDengine TMQ 消费流程》
- 04-《TDengine 内部机制》
- 05-《TDengine TMQ 最佳实践》
预聚合
索引
SQL 语句
关于 TDengine
TDengine 专为物联网IoT平台、工业大数据平台设计。其中,TDengine TSDB 是一款高性能、分布式的时序数据库(Time Series Database),同时它还带有内建的缓存、流式计算、数据订阅等系统功能;TDengine IDMP 是一款AI原生工业数据管理平台,它通过树状层次结构建立数据目录,对数据进行标准化、情景化,并通过 AI 提供实时分析、可视化、事件管理与报警等功能。


480

被折叠的 条评论
为什么被折叠?



