分类4.查询引擎 |篇章08 连接算子适用版本TDengine v3.xv3.3.x / v3.4.x | 最后更新2026-06-15JOIN 是关系数据库的核心能力。TDengine 在标准 SQL JOINInner/Left/Right/Full之外针对时序场景额外提供 ASOF JOIN按时间近邻和 Window JOIN按时间窗口让两个设备时间序列对齐这类需求一行 SQL 即可表达。核心概念速查表概念说明Hash Join用哈希表的等值连接Merge Join输入已排序时的连接Inner Join仅返回两侧匹配的行Outer JoinLEFT/RIGHT/FULL 保留一侧或两侧ASOF Join时间近邻连接找最接近的时间点Window Join时间窗口连接同窗口内对齐Equi Join Condition等值连接条件必须含时间等值详细解析1. TDengine JOIN 的时间约束TDengine 的 JOIN 核心要求 ✓ JOIN 必须包含时间戳等值条件普通 JOIN 或时间相关条件ASOF / Window JOIN 示例合法 SELECT * FROM t1 JOIN t2 ON t1.ts t2.ts; SELECT * FROM t1 JOIN t2 ON t1.ts t2.ts AND t1.id t2.id; ✗ 不合法 SELECT * FROM t1 JOIN t2 ON t1.id t2.id; -- 缺少时间条件 原因 - 时序数据按时间分布 - 没有时间约束的 JOIN 是笛卡尔积级别的开销 - 强制时间条件保证可优化为窗口对齐2. Inner Join 与 Outer JoinJOIN 类型对比 数据示例 t1: t2: tsT1, v10 tsT1, v100 tsT2, v20 tsT3, v300 tsT3, v30 INNER JOIN (tsts): T1: (10, 100) T3: (30, 300) [T2 不匹配被丢弃] LEFT JOIN: T1: (10, 100) T2: (20, NULL) ← 保留左侧 T3: (30, 300) RIGHT JOIN: T1: (10, 100) T3: (30, 300) [t1 中无 T2 也无影响因为 RIGHT 保留右侧] FULL OUTER JOIN: T1: (10, 100) T2: (20, NULL) T3: (30, 300)3. ASOF JOIN时间近邻ASOF JOIN 场景 设备 A 每秒采集设备 B 每 5 秒采集 问题A 的每个时间点对应的 B 最近一次采集 数据 A: T1, T2, T3, T4, T5, T6, T7, T8 B: T1, T6 ASOF JOIN A LEFT ASOF JOIN B ON A.ts B.ts: 每个 A.ts 找到 B 中 A.ts 的最大者 A.T1 → B.T1 A.T2 → B.T1 A.T3 → B.T1 A.T4 → B.T1 A.T5 → B.T1 A.T6 → B.T6 A.T7 → B.T6 A.T8 → B.T6 语法 SELECT a.ts, a.v, b.v FROM ta a ASOF JOIN tb b ON a.ts b.ts AND a.id b.id -- Tag 等值约束可选; 支持的比较操作 , , , 4. Window JOIN时间窗口Window JOIN 场景 问题每对设备的同一分钟内的关联事件 Window JOIN tb b WINDOW(1m) ON ta.id tb.id: 每个 ta 的行找 tb 中 [ta.ts - 30s, ta.ts 30s] 内的所有行 示例 ta: T112:00:10, T212:00:50 tb: B112:00:15, B212:01:30 taT1 → tb 在 [11:59:40, 12:00:40] → B1 匹配 taT2 → tb 在 [12:00:20, 12:01:20] → 无匹配 语法 SELECT * FROM ta WINDOW JOIN tb WINDOW(1m) -- 窗口大小 ON ta.id tb.id;5. Hash Join 实现Hash Join 的两阶段 阶段 1构建Build 选择较小的表构建侧 读取所有行 → 构建哈希表Key JOIN 键 阶段 2探测Probe 扫描较大的表探测侧 对每行用 JOIN 键查找哈希表 匹配则输出 示例 SELECT * FROM big JOIN small ON big.id small.id AND big.ts small.ts Build (small): 哈希表 (id1, tsT1) → row_data (id1, tsT2) → row_data (id2, tsT1) → row_data Probe (big): 扫描 big 每行 查询哈希表是否有匹配 特点 ✓ 适合 等值连接 ✓ 大小表组合 ✗ 构建侧必须放入内存6. Merge Join 实现Merge Join输入已排序 前提两侧输入按 JOIN 键有序 算法 指针 i 指向 t1 第一行 指针 j 指向 t2 第一行 while i len(t1) and j len(t2): if t1[i].key t2[j].key: 输出 (t1[i], t2[j]) i 或 j处理重复键 elif t1[i].key t2[j].key: i else: j TDengine 中的时间 JOIN 天然适合 Merge Join - 两侧数据都按 ts 有序 - 不需要构建哈希表 - 内存占用 O(1) - 适合海量数据7. JOIN 的分布式执行跨 VGroup 的 JOIN 执行 SELECT * FROM ta JOIN tb ON ta.ts tb.ts AND ta.id tb.id ta 跨 VGroup 1, 2 tb 跨 VGroup 3, 4 执行选项 ① 广播 JOIN适合小表 - 小表如 tb拉取到所有 ta 所在节点 - 每个 ta 节点本地 Hash Join ② Shuffle JOIN适合大表 - 两侧都按 JOIN 键 Shuffle 到相同节点 - 各节点 Hash/Merge Join - 适合大表 大表 ③ 单子表 JOIN最简单 - 如果 ta 和 tb 都是子表 - 通常单 VGroup 内完成 - 无需 Shuffle8. JOIN 性能调优JOIN 性能关键点 ① 选择性优先 先过滤再 JOIN SELECT * FROM ta JOIN tb ON ta.tstb.ts WHERE ta.locationBJ AND tb.locationBJ → 过滤下推到 Scan → JOIN 输入数据量减少 ② 时间范围必须明确 SELECT * FROM ta JOIN tb ON ta.tstb.ts WHERE ta.ts now-1h → 同时限制 ta 和 tb 的时间范围 ③ 数据局部性 同 VGroup 的子表 JOIN → 无 Shuffle 跨 VGroup 的 JOIN → Shuffle 开销代码示例基础 JOIN-- 两个超级表的时间对齐SELECTa.ts,a.current,b.powerFROMelectric_meters aJOINpower_meters bONa.tsb.tsANDa.locationb.locationWHEREa.tsnow-1h;-- LEFT JOIN 保留所有 ASELECTa.ts,a.current,b.powerFROMelectric_meters aLEFTJOINpower_meters bONa.tsb.tsANDa.locationb.location;ASOF JOIN-- 高频设备对低频参考值SELECTa.ts,a.current,b.standard_voltageFROMrealtime_sensor aLEFTASOFJOINreference_sensor bONa.tsb.tsANDa.locationb.locationWHEREa.tsnow-1h;Window JOIN-- 找出每个温度异常前后 1 分钟的湿度记录SELECTt.tsAStemp_ts,h.tsAShumi_ts,t.temperature,h.humidityFROMtemperature_log t WINDOWJOINhumidity_log h WINDOW(1m)ONt.locationh.locationWHEREt.temperature40ANDt.tsnow-1d;性能考量JOIN 类型选择场景推荐 JOIN等频率采集对齐INNER JOIN ON ts不同采集频率对齐LEFT ASOF JOIN事件关联同窗口内任意点WINDOW JOIN维度表关联INNER JOIN含 Tag 等值性能优化清单WHERE 同时限制两侧的时间范围WHERE 同时过滤两侧的 Tag让数据局部化优先选具体列避免 SELECT *小表放右侧可能影响 Build/Probe 选择大基数 JOIN 考虑 QNodeFAQQ1: 为什么我的 JOIN 报missing time conditionTDengine 要求 JOIN 必须有时间相关条件。改写普通 JOINON ... AND t1.ts t2.ts时间近邻用ASOF JOIN时间窗口用WINDOW JOINQ2: ASOF JOIN 性能如何输入按 ts 有序时时序数据天然如此用 Merge 风格算法复杂度 O(NM)。生产环境处理百万级数据行毫秒~秒级。Q3: 多表 JOIN≥3支持吗支持但复杂度高每多一张表 JOIN 次数线性增加。建议拆分为多个简单查询用应用层组合或预先 ETL 到宽表Q4: JOIN 和 UNION 哪个更适合JOIN横向合并增加列UNION纵向合并增加行多设备同类数据汇总用 UNION多种数据类型对齐用 JOIN参考系统构架篇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 聚合算子》