news 2026/6/20 16:30:13

TDengine 连接算子 — Inner/Outer/ASOF/Window Join 的实现与使用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
TDengine 连接算子 — Inner/Outer/ASOF/Window Join 的实现与使用

分类:4.查询引擎 |篇章:08 连接算子

适用版本:TDengine v3.x(v3.3.x / v3.4.x) | 最后更新:2026-06-15

JOIN 是关系数据库的核心能力。TDengine 在标准 SQL JOIN(Inner/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 Join

JOIN 类型对比: 数据示例: t1: t2: ts=T1, v=10 ts=T1, v=100 ts=T2, v=20 ts=T3, v=300 ts=T3, v=30 INNER JOIN (ts=ts): 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: T1=12:00:10, T2=12:00:50 tb: B1=12:00:15, B2=12:01:30 ta=T1 → tb 在 [11:59:40, 12:00:40] → B1 匹配 ta=T2 → 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): 哈希表: (id=1, ts=T1) → row_data (id=1, ts=T2) → row_data (id=2, ts=T1) → 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 内完成 - 无需 Shuffle

8. JOIN 性能调优

JOIN 性能关键点: ① 选择性优先: 先过滤再 JOIN SELECT * FROM ta JOIN tb ON ta.ts=tb.ts WHERE ta.location='BJ' AND tb.location='BJ' → 过滤下推到 Scan → JOIN 输入数据量减少 ② 时间范围必须明确: SELECT * FROM ta JOIN tb ON ta.ts=tb.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.ts=b.tsANDa.location=b.locationWHEREa.ts>now-1h;-- LEFT JOIN 保留所有 ASELECTa.ts,a.current,b.powerFROMelectric_meters aLEFTJOINpower_meters bONa.ts=b.tsANDa.location=b.location;

ASOF JOIN

-- 高频设备对低频参考值SELECTa.ts,a.current,b.standard_voltageFROMrealtime_sensor aLEFTASOFJOINreference_sensor bONa.ts>=b.tsANDa.location=b.locationWHEREa.ts>now-1h;

Window JOIN

-- 找出每个温度异常前后 1 分钟的湿度记录SELECTt.tsAStemp_ts,h.tsAShumi_ts,t.temperature,h.humidityFROMtemperature_log t WINDOWJOINhumidity_log h WINDOW(1m)ONt.location=h.locationWHEREt.temperature>40ANDt.ts>now-1d;

性能考量

JOIN 类型选择

场景推荐 JOIN
等频率采集对齐INNER JOIN ON ts
不同采集频率对齐LEFT ASOF JOIN
事件关联(同窗口内任意点)WINDOW JOIN
维度表关联INNER JOIN(含 Tag 等值)

性能优化清单

  • WHERE 同时限制两侧的时间范围
  • WHERE 同时过滤两侧的 Tag(让数据局部化)
  • 优先选具体列,避免 SELECT *
  • 小表放右侧(可能影响 Build/Probe 选择)
  • 大基数 JOIN 考虑 QNode

FAQ

Q1: 为什么我的 JOIN 报"missing time condition"?

TDengine 要求 JOIN 必须有时间相关条件。改写:

  • 普通 JOIN:ON ... AND t1.ts = t2.ts
  • 时间近邻:用ASOF JOIN
  • 时间窗口:用WINDOW JOIN

Q2: ASOF JOIN 性能如何?

输入按 ts 有序时(时序数据天然如此),用 Merge 风格算法,复杂度 O(N+M)。生产环境处理百万级数据行毫秒~秒级。

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 聚合算子》
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/17 12:19:01

面试官:什么是agent的可观测性?

可观测性是2026年Agent面试上升最快的考点。去年面试官还只问"你用过什么框架"&#xff0c;今年已经递进到"你怎么知道你的Agent跑得好不好"。 以下拆成四道高频题&#xff0c;逐题分析。 Q1&#xff1a;你的Agent上线了&#xff0c;你通过什么指标判断它…

作者头像 李华
网站建设 2026/6/17 13:06:39

21点可否战胜庄家?蒙特卡洛模拟验证基本策略与Hi-Lo计牌法

1. 项目概述&#xff1a;这不是赌桌上的玄学&#xff0c;而是可量化的概率战场“Can You Actually Beat the Dealer in Blackjack? — Simulation of Most Popular Strategies”——这个标题一上来就抛出了一个困扰无数玩家几十年的终极问题&#xff1a;在21点游戏中&#xff…

作者头像 李华
网站建设 2026/6/17 23:39:01

Python print无换行控制:从缓冲区原理到生产级实时输出

1. 项目概述&#xff1a;为什么一行代码的换行控制&#xff0c;能决定你写脚本的成败“Python print without new line”——这串关键词背后藏着的&#xff0c;不是什么高深算法&#xff0c;而是每个写过 Python 脚本的人&#xff0c;在第3小时、第17次调试、第42行输出日志时&…

作者头像 李华
网站建设 2026/6/18 3:54:23

扩散模型记忆化问题与RADS框架解决方案

1. 项目背景与问题定义文本到图像扩散模型已成为当前生成式AI领域的主流技术&#xff0c;通过逐步去噪的逆向过程实现高质量图像合成。然而在实际应用中&#xff0c;这类模型普遍存在记忆化问题&#xff08;Memorization&#xff09;——当输入特定提示词时&#xff0c;模型会直…

作者头像 李华
网站建设 2026/6/17 19:12:20

Android 开发问题:Unable to find explicit activity class

Intent intent new Intent(getActivity(), Test2Activity.class);startActivity(intent);在 Android 开发中&#xff0c;执行上述代码进行页面跳转时&#xff0c;出现如下错误信息 FATAL EXCEPTION: main Process: com.my.navigation, PID: 27544 android.content.ActivityNot…

作者头像 李华
网站建设 2026/6/17 14:20:31

Colab数据持久化实战:Drive挂载、Kaggle下载与HTTP直链避坑指南

1. 项目概述&#xff1a;为什么在 Colab 上“搞数据”是每个实践者的必修课Google Colab 是我过去三年里用得最勤的实验环境&#xff0c;没有之一。它不是什么神秘黑科技&#xff0c;就是一台随时能调用 T4 或 A100 的远程笔记本——你打开浏览器、点几下鼠标、写几行 Python&a…

作者头像 李华