TDSQL Boundless 多表关联查询

这篇档介绍如何在 TDSQL Boundless 数据库中使用 JOIN 语句进行多表连接查询,包括各种连接类型、连接算法的使用。

相关产品
TDSQL Boundless 产品页,含版本规格、价格与购买入口。

一、开始之前

在进行单表查询之前,请确保:

1. 已创建 TDSQL Boundless 实例。

2. 已导入测试数据:这篇档使用 TPC-H 基准测试数据集作为示例,详情请参考 TPC-H 基准测试数据模型。

3. 已建立数据库连接:通过 MySQL 客户端或应用程序连接到 TDSQL Boundless。

二、内连接(INNER JOIN)

内连接只返回两张表中同时满足连接条件的行。如果某一侧没有匹配的行,则该行不会出现在结果中。

查询每个订单对应的客户名称:

SELECT o.o_orderkey, o.o_orderdate, o.o_totalprice, c.c_name, c.c_mktsegmentFROM orders oINNER JOIN customer c ON o.o_custkey = c.c_custkey;

在这个查询中,只有在 customer 表中存在对应客户的订单才会被返回。

多表内连接 — 查询订单明细及其所属订单和客户信息:

SELECT c.c_name, o.o_orderkey, o.o_orderdate, l.l_partkey, l.l_quantity, l.l_extendedpriceFROM customer cINNER JOIN orders o ON c.c_custkey = o.o_custkeyINNER JOIN lineitem l ON o.o_orderkey = l.l_orderkeyWHERE o.o_orderdate BETWEEN '1995-01-01' AND '1995-03-31'ORDER BY l.l_extendedprice DESCLIMIT 20;

注意:由于使用的是 INNER JOIN,没有下过订单的客户不会出现在结果中。如果需要包含没有订单的客户,应使用 LEFT JOIN。

三、左外连接(LEFT JOIN)

左外连接返回左表的所有行。对于右表中没有匹配的行,结果中右表的列填充为 NULL。

查询所有客户及其订单数量(包括没有下过订单的客户):

SELECT c.c_custkey, c.c_name, c.c_mktsegment, COUNT(o.o_orderkey) AS order_countFROM customer cLEFT JOIN orders o ON c.c_custkey = o.o_custkeyGROUP BY c.c_custkey, c.c_name, c.c_mktsegmentORDER BY order_count ASCLIMIT 20;

与 INNER JOIN 不同,即使某个客户在 orders 表中没有任何记录,该客户仍然会出现在结果中,其 order_count 为0。

使用 LEFT JOIN 查找不匹配的行 — 查询没有下过订单的客户:

SELECT c.c_custkey, c.c_name, c.c_phoneFROM customer cLEFT JOIN orders o ON c.c_custkey = o.o_custkeyWHERE o.o_orderkey IS NULL;

这种写法等价于 NOT EXISTS 子查询,但在某些场景下执行效率更高:

— 等价写法SELECT c_custkey, c_name, c_phoneFROM customer cWHERE NOT EXISTS ( SELECT 1 FROM orders o WHERE o.o_custkey = c.c_custkey);

四、右外连接(RIGHT JOIN)

右外连接返回右表的所有行,对于左表中没有匹配的行,左表的列填充为 NULL。其逻辑与 LEFT JOIN 对称。

查询所有国家及其客户数量:

SELECT n.n_name AS nation_name, COUNT(c.c_custkey) AS customer_countFROM customer cRIGHT JOIN nation n ON c.c_nationkey = n.n_nationkeyGROUP BY n.n_nameORDER BY customer_count DESC;

实践教程:在实际开发中,建议统一使用 LEFT JOIN 代替 RIGHT JOIN,只需调换表的顺序即可。这样可以保持 SQL 风格一致,提高可读性。

上述查询等价于:

SELECT n.n_name AS nation_name, COUNT(c.c_custkey) AS customer_countFROM nation nLEFT JOIN customer c ON c.c_nationkey = n.n_nationkeyGROUP BY n.n_nameORDER BY customer_count DESC;

五、交叉连接(CROSS JOIN)

交叉连接返回两张表的笛卡尔积,即左表的每一行与右表的每一行组合。如果左表有 m 行、右表有 n 行,结果集将有 m * n 行。

查询所有区域和市场细分的组合:

SELECT r.r_name AS region_name, seg.segment_nameFROM region rCROSS JOIN ( SELECT DISTINCT c_mktsegment AS segment_name FROM customer) segORDER BY r.r_name, seg.segment_name;

注意:交叉连接会产生非常大的结果集,在生产环境中应谨慎使用,通常仅用于生成维度组合等特定场景。

六、左半连接(LEFT SEMI JOIN)

左半连接用于判断左表的行在右表中是否存在匹配,只返回左表的列,且每行最多返回一次。TDSQL 语法中并不直接支持 LEFT SEMI JOIN 语法,通常通过 EXISTS 或 IN 子查询来表达。

查询有过订单的客户:

SELECT c.c_custkey, c.c_name, c.c_acctbalFROM customer cWHERE EXISTS ( SELECT 1 FROM orders o WHERE o.o_custkey = c.c_custkey);

等价的 IN 写法:

SELECT c_custkey, c_name, c_acctbalFROM customerWHERE c_custkey IN ( SELECT o_custkey FROM orders);

与 INNER JOIN 不同,左半连接不会因右表存在多条匹配而产生重复行。例如一个客户有10个订单,INNER JOIN 会返回10行,而左半连接只返回1行。

七、隐式连接

不使用 JOIN 关键字,而是在 FROM 子句中列出多张表,并在 WHERE 子句中指定连接条件。隐式连接的行为等同于 INNER JOIN。

SELECT c.c_name, o.o_orderkey, o.o_totalpriceFROM customer c, orders oWHERE c.c_custkey = o.o_custkey AND o.o_orderdate >= '1995-01-01'ORDER BY o.o_totalprice DESCLIMIT 10;

实践教程:建议使用显式的 JOIN … ON 语法代替隐式连接。显式语法将连接条件与过滤条件分离,SQL 的意图更加清晰,也更易于维护。

八、JOIN 相关算法

TDSQL Boundless 优化器会基于代价评估自动选择最优的连接算法。目前支持两种核心算法:Nested Loop Join 和 Hash Join。

九、Nested Loop Join

Nested Loop Join(嵌套循环连接)是最基础的连接算法。其原理是遍历外表(驱动表)的每一行,然后在内表中查找匹配的行。

TDSQL Boundless 默认采用 Batched Key Access(BKA) 方式执行 Nested Loop Join。BKA 的核心优化思路是:将外表的多行键值批量收集后,一次性发送给内表进行查找,而不是逐行发送。这种批处理方式有两个显著优势:

1. 减少 RPC 频次:将多次单行查找合并为一次批量请求,大幅降低网络开销

2. 提升存储引擎读取效率:内表收到一批键值后,批量索引查找和回表,提升存储引擎读取效率

适用场景:

·内表的连接列上有索引

·外表数据量相对较小,或经过 WHERE 过滤后行数较少

·索引选择性好(即通过索引能快速定位少量行)

示例:

— 典型的 Nested Loop Join 场景– orders 作为外表,customer 通过主键 c_custkey 作为内表SELECT o.o_orderkey, o.o_totalprice, c.c_nameFROM orders oINNER JOIN customer c ON o.o_custkey = c.c_custkeyWHERE o.o_orderdate = '1995-03-15';

在这个查询中,orders 表经过 o_orderdate 过滤后行数较少,作为驱动表;customer 表的 c_custkey 是主键,作为被驱动表。BKA 会将过滤后的 o_custkey 值批量发送,一次性在 customer 表中查找匹配行。

十、Hash Join

Hash Join(哈希连接)的原理是:首先将较小的表(构建表)加载到内存中构建哈希表,然后遍历较大的表(探测表),对每一行通过哈希函数快速查找匹配行。

适用场景:

·连接列上没有合适索引

·等值连接(= 条件)

示例:

— 典型的 Hash Join 场景– 两张大表的等值连接,且连接列无索引SELECT l.l_orderkey, l.l_extendedprice, l.l_discount, p.p_name, p.p_typeFROM lineitem lINNER JOIN part p ON l.l_partkey = p.p_partkeyWHERE p.p_type LIKE '%BRASS%';

十一、算法选择

TDSQL Boundless 优化器基于代价模型自动选择连接算法,主要考虑以下因素:

因素 Nested Loop Join (BKA) Hash Join
索引依赖 需要内表连接列上有索引 不依赖索引
数据量 适合外表较小、内表较大的场景 适合两表都较大的场景
内存消耗 较低,按批次处理 较高,需要在内存中构建哈希表
连接类型 等值和非等值连接 仅支持等值连接

通过 EXPLAIN 可以查看优化器选择的连接算法:

EXPLAIN SELECT o.o_orderkey, c.c_nameFROM orders oINNER JOIN customer c ON o.o_custkey = c.c_custkeyWHERE o.o_orderdate = '1995-03-15';

十二、JOIN 顺序

在多表连接查询中,表的连接顺序对查询性能有重要影响。TDSQL Boundless 优化器会自动评估不同的连接顺序,选择代价最低的执行方案。

十三、自动优化

优化器在生成执行计划时,会综合考虑表的大小、索引情况、过滤条件的选择性等因素,自动调整连接顺序:

— 优化器会自动决定 customer、orders、lineitem 的连接顺序SELECT c.c_name, o.o_orderdate, l.l_extendedpriceFROM customer cINNER JOIN orders o ON c.c_custkey = o.o_custkeyINNER JOIN lineitem l ON o.o_orderkey = l.l_orderkeyWHERE c.c_mktsegment = 'BUILDING' AND o.o_orderdate < '1995-03-15' AND l.l_shipdate > '1995-03-15';

十四、使用 Hint 指定连接顺序

当优化器选择的连接顺序不理想时,可以通过 Hint 手动干预连接顺序。

·JOIN_ORDER:指定所有表的完整连接顺序:

SELECT /*+ JOIN_ORDER(o, c, l) */ c.c_name, o.o_orderdate, l.l_extendedpriceFROM customer cINNER JOIN orders o ON c.c_custkey = o.o_custkeyINNER JOIN lineitem l ON o.o_orderkey = l.l_orderkeyWHERE c.c_mktsegment = 'BUILDING' AND o.o_orderdate < '1995-03-15' AND l.l_shipdate > '1995-03-15';

JOIN_ORDER(o, c, l) 强制优化器严格按 orders > customer > lineitem 的顺序执行连接,忽略 SQL 中的表书写位置。

·JOIN_PREFIX:仅指定连接顺序的前几张表,其余表由优化器自动决定:

SELECT /*+ JOIN_PREFIX(o, c) */ c.c_name, o.o_orderdate, l.l_extendedpriceFROM customer cINNER JOIN orders o ON c.c_custkey = o.o_custkeyINNER JOIN lineitem l ON o.o_orderkey = l.l_orderkeyWHERE c.c_mktsegment = 'BUILDING' AND o.o_orderdate < '1995-03-15' AND l.l_shipdate > '1995-03-15';

JOIN_PREFIX(o, c) 强制前两张表的连接顺序为 orders > customer,而 lineitem 的连接位置由优化器自行选择。当只需要控制部分关键表的顺序、其余表交由优化器优化时,JOIN_PREFIX 比 JOIN_ORDER 更灵活。

赞 (0)
上一篇 4天前
下一篇 4天前