TDengine JOIN 完整语法 — Inner/Outer/ASOF/Window 全语法详解
分类: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
-- 基本SELECTt1.ts,t1.current,t2.powerFROMelectric_meters t1JOINpower_meters t2ONt1.ts=t2.ts;-- 多条件SELECT*FROMt1JOINt2ONt1.ts=t2.tsANDt1.location=t2.location;-- 多表(注意性能)SELECT*FROMt1JOINt2ONt1.ts=t2.tsJOINt3ONt1.ts=t3.ts;语义: 保留ON条件同时满足的行 双侧 ts 都存在且相等3. Outer JOIN
-- LEFT JOINSELECTt1.ts,t1.current,t2.powerFROMelectric_meters t1LEFTJOINpower_meters t2ONt1.ts=t2.ts;-- t1 保留所有,t2 无匹配为 NULL-- RIGHT JOINSELECTt1.ts,t1.current,t2.powerFROMelectric_meters t1RIGHTJOINpower_meters t2ONt1.ts=t2.ts;-- t2 保留所有-- FULL OUTER JOINSELECTt1.ts,t1.current,t2.powerFROMelectric_meters t1FULLJOINpower_meters t2ONt1.ts=t2.ts;-- 双侧都保留4. Semi/Anti JOIN
-- SEMI JOIN: 左侧存在匹配则保留(去重)SELECT*FROMusers uLEFTSEMIJOINorders oONu.id=o.user_idANDu.ts=o.ts;-- 等价于:SELECT * FROM users WHERE id IN (SELECT user_id FROM orders)-- ANTI JOIN: 左侧无匹配则保留SELECT*FROMusers uLEFTANTIJOINorders oONu.id=o.user_idANDu.ts=o.ts;-- 等价于:SELECT * FROM users WHERE id NOT IN (...)5. ASOF JOIN(时间近邻)
-- LEFT ASOF JOIN:每个左侧 ts 找右侧近邻SELECTa.ts,a.current,b.standard_voltageFROMrealtime aLEFTASOFJOINreference bONa.ts>=b.tsANDa.location=b.location;-- 支持比较:>= > <= <-- >= 找 <= 当前的最大者-- <= 找 >= 当前的最小者-- 限制条件:必须有时间比较LEFTASOFJOIN...ONa.ts>=b.ts[ANDtag_eq_conditions]-- 多列等值(Tag 关联)LEFTASOFJOINreference bONa.ts>=b.tsANDa.factory=b.factoryANDa.line=b.line;6. WINDOW JOIN(时间窗口)
-- 找出每个事件前后窗口内的关联记录SELECTt.tsASevent_ts,h.tsASrelated_ts,t.value,h.valueFROMevents t WINDOWJOINhistory h WINDOW(1m)-- 窗口大小 ±30sONt.location=h.locationWHEREt.value>100;-- 窗口语义:-- 每个 t 的 ts 为中心,[ts - window/2, ts + window/2]-- 找出 h 中落在该窗口的所有行-- 一对多关系7. 子查询 + JOIN
-- 与聚合子查询 JOINSELECTm.ts,m.current,h.hist_avgFROMmeters mJOIN(SELECTtbname,AVG(current)AShist_avgFROMmetersWHEREts<now-1dPARTITIONBYtbname)hONm.tbname=h.tbnameWHEREm.ts>now-1h;8. JOIN 性能限制
注意: ① TDengine 推荐 JOIN 数量 ≤ 5 ② 时间窗口必须明确(不能 ts > 0) ③ 同 VGroup 子表 JOIN 最优 ④ 跨 VGroup 大表 JOIN 需 Shuffle,慎用 高效 JOIN 模式: - 两侧都有时间过滤 - 两侧都有 Tag 过滤 - 时间和 Tag 条件配对 低效 JOIN: - 无时间过滤的全表 JOIN - 多张超级表全字段 JOIN - JOIN 中含复杂表达式代码示例
综合场景
-- 同设备两类传感器对齐SELECTt.ts,t.currentASamperage,v.voltageASvoltage,t.current*v.voltageASpowerFROMcurrent_sensor tJOINvoltage_sensor vONt.ts=v.tsANDt.device_id=v.device_idWHEREt.ts>now-1h;-- 高频数据对齐低频参考SELECTm.ts,m.value,r.thresholdFROMminutely_data mLEFTASOFJOINreference_data rONm.ts>=r.tsANDm.device_type=r.device_typeWHEREm.value>r.thresholdANDm.ts>now-1d;-- 事件前后关联SELECTa.tsASalarm_ts,m.tsASmeasure_ts,m.currentFROMalarm a WINDOWJOINmeters m WINDOW(5m)ONa.device_id=m.tbnameWHEREa.severity='CRITICAL'ANDa.ts>now-1d;与子查询配合
-- TOP N + JOIN 详情SELECTtop.tbname,top.avg_c,m.ts,m.currentFROM(SELECTtbname,AVG(current)ASavg_cFROMmetersWHEREts>now-1dPARTITIONBYtbnameORDERBYavg_cDESCLIMIT10)topJOINmeters mONtop.tbname=m.tbnameWHEREm.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 最佳实践》
预聚合
- 01-《TDengine RSMA》
- 02-《TDengine TSMA — 时间维度的物化聚合视图》
- 03-《TDengine SMA 内部实现》
索引
- 01-《TDengine Tag 索引》
- 02-《TDengine SMA 索引》
SQL 语句
- 01-《TDengine DDL》
- 02-《TDengine DML SELECT》
- 03-《TDengine DML 函数完整参考》
关于 TDengine
TDengine 专为物联网IoT平台、工业大数据平台设计。其中,TDengine TSDB 是一款高性能、分布式的时序数据库(Time Series Database),同时它还带有内建的缓存、流式计算、数据订阅等系统功能;TDengine IDMP 是一款AI原生工业数据管理平台,它通过树状层次结构建立数据目录,对数据进行标准化、情景化,并通过 AI 提供实时分析、可视化、事件管理与报警等功能。