tencent cloud

TDSQL Boundless

产品动态
产品简介
产品概述
应用场景
产品架构
实例类型
兼容性说明
使用规范建议
自研内核
内核概述
内核版本更新动态
功能类特性
性能类特性
产品计费
计费概述
购买方式
价格详情
续费说明
欠费说明
退费说明
快速入门
创建实例
连接实例
用户指南
数据迁移
数据同步/订阅
实例管理
参数配置
账号管理
安全组
备份与恢复
数据库审计
标签管理
实践教程
Online DDL 的技术演进与使用实践
锁机制解析与问题排查实践
数据智能调度及相关性能优化实践
TDSQL Boundless 选型指南与实践教程
开发指南
开发指南(MySQL 兼容模式)
开发指南(HBase 兼容模式)
性能调优
性能调优概述
SQL 调优
DDL 调优
性能白皮书
性能概述
TPC-C 测试
Sysbench 测试
API 文档
History
Introduction
API Category
Making API Requests
Instance APIs
Security Group APIs
Task APIs
Backup APIs
Rollback APIs
Parameter APIs
Database APIs
Data Types
Error Codes
通用参考
系统原理
SQL 参考
数据库参数说明
TPC-H 基准测试数据模型参考
错误码信息
安全与合规
常见问题
服务协议
服务等级协议
服务条款
隐私政策
数据处理和安全协议
联系我们
词汇表

多表关联查询

PDF
聚焦模式
字号
最后更新时间: 2026-03-26 14:55:43
本文档介绍如何在 TDSQL Boundless 数据库中使用 JOIN 语句进行多表连接查询,包括各种连接类型、连接算法的使用。

开始之前

在进行单表查询之前,请确保:
1. 已创建 TDSQL Boundless 实例
2. 已导入测试数据:本文档使用 TPC-H 基准测试数据集作为示例,详情请参考 TPC-H 基准测试数据模型参考
3. 已建立数据库连接:通过 MySQL 客户端或应用程序连接到 TDSQL Boundless。

JOIN 类型

内连接(INNER JOIN)

内连接只返回两张表中同时满足连接条件的行。如果某一侧没有匹配的行,则该行不会出现在结果中。
查询每个订单对应的客户名称:
SELECT
o.o_orderkey,
o.o_orderdate,
o.o_totalprice,
c.c_name,
c.c_mktsegment
FROM orders o
INNER 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_extendedprice
FROM customer c
INNER JOIN orders o ON c.c_custkey = o.o_custkey
INNER JOIN lineitem l ON o.o_orderkey = l.l_orderkey
WHERE o.o_orderdate BETWEEN '1995-01-01' AND '1995-03-31'
ORDER BY l.l_extendedprice DESC
LIMIT 20;
注意:
由于使用的是 INNER JOIN,没有下过订单的客户不会出现在结果中。如果需要包含没有订单的客户,应使用 LEFT JOIN

左外连接(LEFT JOIN)

左外连接返回左表的所有行。对于右表中没有匹配的行,结果中右表的列填充为 NULL
查询所有客户及其订单数量(包括没有下过订单的客户):
SELECT
c.c_custkey,
c.c_name,
c.c_mktsegment,
COUNT(o.o_orderkey) AS order_count
FROM customer c
LEFT JOIN orders o ON c.c_custkey = o.o_custkey
GROUP BY c.c_custkey, c.c_name, c.c_mktsegment
ORDER BY order_count ASC
LIMIT 20;
INNER JOIN 不同,即使某个客户在 orders 表中没有任何记录,该客户仍然会出现在结果中,其 order_count 为0。
使用 LEFT JOIN 查找不匹配的行 — 查询没有下过订单的客户:
SELECT c.c_custkey, c.c_name, c.c_phone
FROM customer c
LEFT JOIN orders o ON c.c_custkey = o.o_custkey
WHERE o.o_orderkey IS NULL;
这种写法等价于 NOT EXISTS 子查询,但在某些场景下执行效率更高:
-- 等价写法
SELECT c_custkey, c_name, c_phone
FROM customer c
WHERE 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_count
FROM customer c
RIGHT JOIN nation n ON c.c_nationkey = n.n_nationkey
GROUP BY n.n_name
ORDER BY customer_count DESC;
实践教程:在实际开发中,建议统一使用 LEFT JOIN 代替 RIGHT JOIN,只需调换表的顺序即可。这样可以保持 SQL 风格一致,提高可读性。
上述查询等价于:
SELECT
n.n_name AS nation_name,
COUNT(c.c_custkey) AS customer_count
FROM nation n
LEFT JOIN customer c ON c.c_nationkey = n.n_nationkey
GROUP BY n.n_name
ORDER BY customer_count DESC;

交叉连接(CROSS JOIN)

交叉连接返回两张表的笛卡尔积,即左表的每一行与右表的每一行组合。如果左表有 m 行、右表有 n 行,结果集将有 m * n 行。
查询所有区域和市场细分的组合:
SELECT
r.r_name AS region_name,
seg.segment_name
FROM region r
CROSS JOIN (
SELECT DISTINCT c_mktsegment AS segment_name
FROM customer
) seg
ORDER BY r.r_name, seg.segment_name;
注意:
交叉连接会产生非常大的结果集,在生产环境中应谨慎使用,通常仅用于生成维度组合等特定场景。

左半连接(LEFT SEMI JOIN)

左半连接用于判断左表的行在右表中是否存在匹配,只返回左表的列,且每行最多返回一次。TDSQL 语法中并不直接支持 LEFT SEMI JOIN 语法,通常通过 EXISTSIN 子查询来表达。
查询有过订单的客户:
SELECT c.c_custkey, c.c_name, c.c_acctbal
FROM customer c
WHERE EXISTS (
SELECT 1 FROM orders o
WHERE o.o_custkey = c.c_custkey
);
等价的 IN 写法:
SELECT c_custkey, c_name, c_acctbal
FROM customer
WHERE 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_totalprice
FROM customer c, orders o
WHERE c.c_custkey = o.o_custkey
AND o.o_orderdate >= '1995-01-01'
ORDER BY o.o_totalprice DESC
LIMIT 10;
实践教程:建议使用显式的 JOIN ... ON 语法代替隐式连接。显式语法将连接条件与过滤条件分离,SQL 的意图更加清晰,也更易于维护。

JOIN 相关算法

TDSQL Boundless 优化器会基于代价评估自动选择最优的连接算法。目前支持两种核心算法:Nested Loop JoinHash 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_name
FROM orders o
INNER JOIN customer c ON o.o_custkey = c.c_custkey
WHERE 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_type
FROM lineitem l
INNER JOIN part p ON l.l_partkey = p.p_partkey
WHERE p.p_type LIKE '%BRASS%';

算法选择

TDSQL Boundless 优化器基于代价模型自动选择连接算法,主要考虑以下因素:
因素
Nested Loop Join (BKA)
Hash Join
索引依赖
需要内表连接列上有索引
不依赖索引
数据量
适合外表较小、内表较大的场景
适合两表都较大的场景
内存消耗
较低,按批次处理
较高,需要在内存中构建哈希表
连接类型
等值和非等值连接
仅支持等值连接
通过 EXPLAIN 可以查看优化器选择的连接算法:
EXPLAIN SELECT
o.o_orderkey, c.c_name
FROM orders o
INNER JOIN customer c ON o.o_custkey = c.c_custkey
WHERE o.o_orderdate = '1995-03-15';

JOIN 顺序

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

自动优化

优化器在生成执行计划时,会综合考虑表的大小、索引情况、过滤条件的选择性等因素,自动调整连接顺序:
-- 优化器会自动决定 customer、orders、lineitem 的连接顺序
SELECT
c.c_name,
o.o_orderdate,
l.l_extendedprice
FROM customer c
INNER JOIN orders o ON c.c_custkey = o.o_custkey
INNER JOIN lineitem l ON o.o_orderkey = l.l_orderkey
WHERE 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_extendedprice
FROM customer c
INNER JOIN orders o ON c.c_custkey = o.o_custkey
INNER JOIN lineitem l ON o.o_orderkey = l.l_orderkey
WHERE 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_extendedprice
FROM customer c
INNER JOIN orders o ON c.c_custkey = o.o_custkey
INNER JOIN lineitem l ON o.o_orderkey = l.l_orderkey
WHERE 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 更灵活。

帮助和支持

本页内容是否解决了您的问题?

填写满意度调查问卷,共创更好文档体验。

文档反馈