关联查询
Join 概念
驱动表
驱动关联查询进行的表,在 Left Join 系列中左表为驱动表,在 Right Join 系列中右表为驱动表。
连接条件
连接条件是指进行表关联所指定的条件,TDengine 支持的所有关联查询都需要指定连接条件,连接条件通常(Inner Join 和 Window Join 例外)只出现在 ON
之后。根据语义,Inner Join 中出现在 WHERE
之后的条件也可以视作连接条件,而 Window Join 是通过 WINDOW_OFFSET
来指定连接条件。
除 ASOF Join 外,TDengine 支持的所有 Join 类型都必须显式指定连接条件,ASOF Join 因为默认定义有隐式的连接条件,所以(在默认条件可以满足需求的情况下)可以不必显式指定连接条件。
除 ASOF/Window Join 外,连接条件中除了包含主连接条件外,还可以包含任意多条其他连接条件,主连接条件与其他连接条件间必须是 AND
关系,而其他连接条件之间则没有这个 限制。其他连接条件中可以包含主键列、Tag 、普通列、常量及其标量函数或运算的任意逻辑运算组合。
以智能电表为例,下面这几条 SQL 都包含合法的连接条件:
SELECT a.* FROM meters a LEFT JOIN meters b ON a.ts = b.ts AND a.ts > '2023-10-18 10:00:00.000';
SELECT a.* FROM meters a LEFT JOIN meters b ON a.ts = b.ts AND (a.ts > '2023-10-18 10:00:00.000' OR a.ts < '2023-10-17 10:00:00.000');
SELECT a.* FROM meters a LEFT JOIN meters b ON timetruncate(a.ts, 1s) = timetruncate(b.ts, 1s) AND (a.ts + 1s > '2023-10-18 10:00:00.000' OR a.groupId > 0);
SELECT a.* FROM meters a LEFT ASOF JOIN meters b ON timetruncate(a.ts, 1s) < timetruncate(b.ts, 1s) AND a.groupId = b.groupId;
主连接条件
作为一款 时序数据库,TDengine 所有的关联查询都围绕主键时戳列进行,因此要求除 ASOF/Window Join 外的所有关联查询都必须含有主键列的等值连接条件,而按照顺序首次出现在连接条件中的主键列等值连接条件将会被作为主连接条件。ASOF Join 的主连接条件可以包含非等值的连接条件,而 Window Join 的主连接条件则是通过 WINDOW_OFFSET
来指定。
除 Window Join 外,TDengine 支持在主连接条件中进行 timetruncate
函数操作,例如 ON timetruncate(a.ts, 1s) = timetruncate(b.ts, 1s)
,除此之外,暂不支持其他函数及标量运算。
分组条件
时序数据库特色的 ASOF/Window Join 支持对关联查询的输入数据进行分组,然后每个分组内进行关联操作。分组只对关联查询的输入进行,输出结果将不包含分组信息。ASOF/Window Join 中出现在 ON
之后的等值条件(ASOF 的主连接条件除外)将被作为分组条件。
主键时间线
TDengine 作为时序数据库要求每个表(子表)中必须有主键时间戳列,它将作为该表的主键时间线进行很多跟时间相关的运算,而子查询的结果或者 Join 运算的结果中也需要明确哪一列将被视作主键时间线参与后续的时间相关的运算。在子查询中,查询结果中存在的有序的第一个出现的主键列(或其运算)或等同主键列的伪列(_wstart
/_wend
)将被视作该输出表的主键时间线。Join 输出结果中主键时间线的选择遵从以下规则:
- Left/Right Join 系列中驱动表(子查询)的主键列将被作为后续查询的主键时间线;此外,在 Window Join 窗口内,因为左右表同时有序所以在窗口内可以把任意一个表的主键列做作主键时间线,优先选择本表的主键列作为主键时间线。
- Inner Join 可以把任意一个表的主键列做作主键时间线,当存在类似分组条件(Tag 列的等值条件且与主连接条件
AND
关系)时将无法产生主键时间线。 - Full Join 因为无法产生任何一个有效的主键时间序列,因此没有主键时间线,这也就意味着 Full Join 中无法进行时间线相关的运算。
语法说明
在接下来的章节中会通过共用的方式同时介绍 Left/Right Join 系列,因此后续的包括 Outer、Semi、Anti-Semi、ASOF、Window 系列介绍中都采用了类似 "left/right" 的写法来同时进行 Left/Right Join 的介绍。这里简要介绍这种写法的含义,写在 "/" 前面的表示应用于 Left Join,而写在 "/" 后面的表示应用于 Right Join。
举例说明:
"左/右表" 表示对 Left Join 来说,它指的是"左表",对 Right Join 来说,它指的是“右表”;
同理,
"右/左表" 表示对 Left Join 来说,它指的是"右表",对 Right Join 来说,它指的是“左表”;
Join 功能
Inner Join
定义
内连接 - 只有左右表中同时符合连接条件的数据才会被返回,可以视为两个表符合连接条件的数据的交集。
语法
SELECT ... FROM table_name1 [INNER] JOIN table_name2 [ON ...] [WHERE ...] [...]
或
SELECT ... FROM table_name1, table_name2 WHERE ... [...]
结果集
符合连接条件的左右表行数据的笛卡尔积集合。
适用范围
支持超级表、普通表、子表、子查询间 Inner Join。
说明
- 对于第一种语法,
INNER
关键字可选,ON
和/或WHERE
中可以指定主连接条件和其他连接条件,WHERE
中还可以指定过滤条件,ON
/WHERE
两者至少指定一个。 - 对于第二种语法,可以在
WHERE
中指定主连接条件、其他连接条件、过滤条件。 - 对超级表进行 Inner Join 时,与主连接条件
AND
关系的 Tag 列等值条件将作为类似分组条件使用,因此输出结果不能保持有序。
示例
表 d1001 和表 d1002 中同时出现电压大于 220V 的时刻及各自的电压值:
SELECT a.ts, a.voltage, b.voltage FROM d1001 a JOIN d1002 b ON a.ts = b.ts and a.voltage > 220 and b.voltage > 220
Left/Right Outer Join
定义
左/右(外)连接 - 既包含左右表同时符合连接条件的数据集合,也包括左/右表中不符合连接条件的数据集合。
语法
SELECT ... FROM table_name1 LEFT|RIGHT [OUTER] JOIN table_name2 ON ... [WHERE ...] [...]
结果集
Inner Join 的结果集 + 左/右表中不符合连接条件的行和右/左表的空数据(NULL
)组成的行数据集合。
适用范围
支持超级表、普通表、子表、子查询间 Left/Right Join。
说明
- OUTER 关键字可选。
示例
表 d1001 所有时刻的电压值以及和表 d1002 中同时出现电压大于 220V 的时刻及各自的电压值:
SELECT a.ts, a.voltage, b.voltage FROM d1001 a LEFT JOIN d1002 b ON a.ts = b.ts and a.voltage > 220 and b.voltage > 220
Left/Right Semi Join
定义
左/右半连接 - 通常表达的是 IN``/EXISTS
的含义,即对左/右表任意一条数据来说,只有当右/左表中存在任一符合连接条件的数据时才返回左/右表行数据。
语法
SELECT ... FROM table_name1 LEFT|RIGHT SEMI JOIN table_name2 ON ... [WHERE ...] [...]
结果集
左/右表中符合连接条件的行和右/左表任一符合连接条件的行组成的行数据集合。
适用范围
支持超级表、普通表、子表、子查询间 Left/Right Semi Join。
示例
表 d1001 中出现电压大于 220V 且存在其他电表同一时刻电压也大于 220V 的时间:
SELECT a.ts FROM d1001 a LEFT SEMI JOIN meters b ON a.ts = b.ts and a.voltage > 220 and b.voltage > 220 and b.tbname != 'd1001'
Left/Right Anti-Semi Join
定义
左/右反连接 - 同左/右半连接的逻辑正好相反,通常表达的是 NOT IN
/NOT EXISTS
的含义,即对左/右表任意一条数据来说,只有当右/左表中不存在任何符合连接条件的数据时才返回左/右表行数据。
语法
SELECT ... FROM table_name1 LEFT|RIGHT ANTI JOIN table_name2 ON ... [WHERE ...] [...]
结果集
左/右表中不符合连接条件的行和右/左表的空数据(NULL
)组成的行数据集合。
适用范围
支持超级表、普通表、子表、子查询间 Left/Right Anti-Semi Join。
示例
表 d1001 中出现电压大于 220V 且不存在其他电表同一时刻电压也大于 220V 的时间:
SELECT a.ts FROM d1001 a LEFT ANTI JOIN meters b ON a.ts = b.ts and b.voltage > 220 and b.tbname != 'd1001' WHERE a.voltage > 220
left/Right ASOF Join
定义
左/右不完全匹配连接 - 不同于其他传统 Join 的完全匹配模式,ASOF Join 允许以指定的匹配模式进行不完全匹配,即按照主键时间戳最接近的方式进行匹配。
语法
SELECT ... FROM table_name1 LEFT|RIGHT ASOF JOIN table_name2 [ON ...] [JLIMIT jlimit_num] [WHERE ...] [...]