SQL 查询数据:表联结

在 SQL 查询中,联结(JOIN)是处理多表数据关联的核心技术,也是从 “单表查询” 迈向 “多表分析” 的关键。日常业务中,数据往往分散在不同的关系表中(比如用户表、商品表、订单表),只有通过联结才能整合这些分散的数据,实现更复杂的数据分析需求。本文将从 “为什么要用联结” 入手,系统介绍关系表、联结的创建、WHERE 子句的作用,以及最常用的内部联结和多表联结技巧。

 

关系表与联结

什么是关系表?

关系表是遵循 “数据库规范化” 设计的表结构 —— 将不同维度的数据拆分到多个表中,通过关联字段(比如product_id、user_id)建立表之间的联系,避免数据冗余。

举个电商场景的例子:

  • 商品表(products):存储商品的核心信息(ID、名称、分类 ID、价格),无需重复存储分类名称;

  • 分类表(categories):存储分类的核心信息(ID、名称),一个分类可对应多个商品。

如下图:

image.png

这种拆分方式的优势:修改分类名称时,只需改categories表的一条数据,而非修改products表中所有该分类的商品数据,极大降低维护成本。

 

为什么必须使用联结?

既然数据分散在多个关系表中,要获取 “商品名称 + 所属分类名” 这类跨表数据,就必须通过联结将多个表 “拼接” 起来。没有联结,我们只能先查商品的category_id,再手动查分类名称,效率极低。就像这样:

-- 查询找到所有商品的分类ID
> select DISTINCT category_id from product p


category_id|
-----------+
          1|
          2|
          3|
          4|

4 row(s) fetched.

-- 然后根据分类ID找到分类名称
> select * from product_category pc where pc.category_id in (
    select DISTINCT category_id from product p)

category_id|name|create_time        |update_time        |
-----------+----+-------------------+-------------------+
          1|手机  |2025-12-31 03:18:08|2025-12-31 03:18:08|
          2|冰箱  |2025-12-31 03:18:08|2025-12-31 03:18:08|
          3|电脑  |2025-12-31 03:18:08|2025-12-31 03:18:08|
          4|风扇  |2025-12-31 03:18:08|2025-12-31 03:18:08|

4 row(s) fetched.

-- 最后查询商品信息,使用服务端代码根据分类ID提取分类名称。

简单来说:联结的核心价值是在一次查询中整合多个关系表的数据,实现跨表数据的关联查询。

创建联结的核心逻辑

联结的基本语法

联结的核心是通过JOIN关键字指定要关联的表,通过ON(或WHERE)子句指定关联条件(即两个表的关联字段匹配规则)。

联结语法如下:

SELECT 字段1, 字段2,...
FROM 表1 [表1别名]
JOIN 表2 [表2别名]
ON 表1.关联字段 = 表2.关联字段;  -- 核心:指定关联条件

例如:查找所有商品的商分类名称、商品名称、价格和库存

> select pc.name as `category_name`, p.name as `product_name`, p.price, p.stock from product p
join product_category pc on p.category_id=pc.category_id

category_name|product_name |price   |stock|
-------------+-------------+--------+-----+
手机           |小米14手机       | 4999.00| 1000|
手机           |华为Mate60 Pro | 6999.00|  800|
手机           |苹果iPhone 15  | 7999.00| 1200|
手机           |vivo X100    | 3999.00| 1500|
手机           |OPPO Find X7 | 4499.00|  900|
手机           |荣耀Magic6     | 4299.00| 1100|
手机           |红米K70        | 2499.00| 2000|
手机           |一加12         | 4599.00|  850|
手机           |真我GT Neo5    | 2599.00| 1800|
手机           |三星S24 Ultra  | 8999.00|  600|
冰箱           |海尔双开门冰箱      | 3999.00|  200|
冰箱           |美的风冷无霜冰箱     | 2999.00|  150|
电脑           |联想拯救者Y9000P  | 8999.00|  300|
电脑           |苹果MacBook Pro|12999.00|  180|
风扇           |格力落地扇        |  199.00|  500|
风扇           |美的循环扇        |  299.00|  400|

16 row(s) fetched.

上述 SQL 语句中,“on p.category_id=pc.category_id” 表示使用商品表(product)的 category_id 字段和商品分类表(product_category)的 categroy_id 字段进行关联,即两张表 categroy_id 字段相等,才关联成功。不相等,关联失败,不检索出来。

 

WHERE 子句在联结中的重要性

早期 SQL 中,联结的关联条件常写在WHERE子句中(比如WHERE p.category_id = c.category_id),虽然现代 SQL 推荐用ON子句明确关联条件,但WHERE子句在联结中仍有两个核心作用:

  1. 过滤关联后的结果集:在表联结完成后,筛选符合条件的数据(比如只保留价格 > 5000 的商品)。

  2. 兼容旧版语法:部分老数据库或简化写法中,仍用WHERE替代ON指定关联条件(但可读性差,不推荐)。

示例:查询价格 > 5000 的商品信息,含分类名称、商品名称、价格和库存信息

> select pc.name as `category_name`, p.name as `product_name`, p.price, p.stock from product p
join product_category pc on p.category_id=pc.category_id
where p.price > 5000

category_name|product_name |price   |stock|
-------------+-------------+--------+-----+
手机           |华为Mate60 Pro | 6999.00|  800|
手机           |苹果iPhone 15  | 7999.00| 1200|
手机           |三星S24 Ultra  | 8999.00|  600|
电脑           |联想拯救者Y9000P  | 8999.00|  300|
电脑           |苹果MacBook Pro|12999.00|  180|

5 row(s) fetched.

关键提醒:如果联结时省略关联条件(ON/WHERE),会产生 “笛卡尔积”。

笛卡尔积(Cartesian Product)是集合论中的一个基本概念,指两个或多个集合中所有可能的有序元素对的集合。 即表 1 的每一行都与表 2 的每一行匹配,结果集行数 = 表 1 行数 × 表 2 行数(本例中 8×4=32 行),这是无意义的错误结果,务必避免!如下图:

image.png

上图,表 1 有 3 条数据,表 2 有 2 条数据,因此笛卡尔积总共有 3 x 2 = 6。

  

内部联结(INNER JOIN)

内部联结(也叫 “等值联结”)是最常用的联结类型,只返回两个表中满足关联条件的行(即两个表的 “交集” 数据)。

语法如下:

-- 标准写法(推荐):用INNER JOIN + ON指定关联条件
SELECT 字段
FROM 表1
INNER JOIN 表2
ON 表1.关联字段 = 表2.关联字段;

-- 或

SELECT 字段
FROM 表1
JOIN 表2
ON 表1.关联字段 = 表2.关联字段;

等价如下写法,用逗号分隔表,WHERE 指定关联条件:

SELECT 字段
FROM 表1, 表2
WHERE 表1.关联字段 = 表2.关联字段;

  

示例:查询价格 > 5000 的商品信息,含分类名称、商品名称、价格和库存信息

-- 使用 JOIN ... ON 方式
> select pc.name as `category_name`, p.name as `product_name`, p.price, p.stock from product p
join product_category pc on p.category_id=pc.category_id
where p.price > 5000

category_name|product_name |price   |stock|
-------------+-------------+--------+-----+
手机           |华为Mate60 Pro | 6999.00|  800|
手机           |苹果iPhone 15  | 7999.00| 1200|
手机           |三星S24 Ultra  | 8999.00|  600|
电脑           |联想拯救者Y9000P  | 8999.00|  300|
电脑           |苹果MacBook Pro|12999.00|  180|

5 row(s) fetched.

-- 使用 INNER JOIN ... ON 方式
> select pc.name as `category_name`, p.name as `product_name`, p.price, p.stock from product p
inner join product_category pc on p.category_id=pc.category_id
where p.price > 5000

category_name|product_name |price   |stock|
-------------+-------------+--------+-----+
手机           |华为Mate60 Pro | 6999.00|  800|
手机           |苹果iPhone 15  | 7999.00| 1200|
手机           |三星S24 Ultra  | 8999.00|  600|
电脑           |联想拯救者Y9000P  | 8999.00|  300|
电脑           |苹果MacBook Pro|12999.00|  180|

5 row(s) fetched.

-- 使用 WHERE 方式
> select pc.name as `category_name`, p.name as `product_name`, p.price, p.stock 
from product p, product_category pc
where p.category_id=pc.category_id and p.price > 5000

category_name|product_name |price   |stock|
-------------+-------------+--------+-----+
手机           |华为Mate60 Pro | 6999.00|  800|
手机           |苹果iPhone 15  | 7999.00| 1200|
手机           |三星S24 Ultra  | 8999.00|  600|
电脑           |联想拯救者Y9000P  | 8999.00|  300|
电脑           |苹果MacBook Pro|12999.00|  180|

5 row(s) fetched.

  

联结多个表(3 张及以上表)

实际业务中,常需要关联 3 张及以上表(比如商品表 + 分类表 + 订单表),核心逻辑是依次联结,逐个指定关联条件。

示例:查询每个订单的商品购买信息,包含 “订单 ID + 商品名称 + 分类名称 + 购买数量”。

> select o.order_id as '订单ID', p.name as '商品名称', pc.name as '分类名称', op.quantity as '购买数量' 
from `order` o
join `order_product` op ON o.order_id=op.order_id 
join `product` p on p.product_id=op.product_id
join `product_category` pc on p.category_id=pc.category_id
order by o.order_id 

订单ID|商品名称        |分类名称|购买数量|
----+------------+----+----+
   1|小米14手机      |手机  |   1|
   2|小米14手机      |手机  |   1|
   2|华为Mate60 Pro|手机  |   1|
   3|红米K70       |手机  |   1|
   4|华为Mate60 Pro|手机  |   1|
   5|OPPO Find X7|手机  |   1|
   6|三星S24 Ultra |手机  |   1|
   7|vivo X100   |手机  |   1|
   8|荣耀Magic6    |手机  |   1|
   9|苹果iPhone 15 |手机  |   1|
  10|一加12        |手机  |   1|
  11|真我GT Neo5   |手机  |   1|
  12|小米14手机      |手机  |   1|
  13|vivo X100   |手机  |   1|
  14|华为Mate60 Pro|手机  |   1|
  15|红米K70       |手机  |   1|
  16|三星S24 Ultra |手机  |   1|
  17|荣耀Magic6    |手机  |   1|
  18|OPPO Find X7|手机  |   1|
  19|真我GT Neo5   |手机  |   1|
  20|红米K70       |手机  |   1|
  20|vivo X100   |手机  |   2|
  20|苹果iPhone 15 |手机  |   1|

23 row(s) fetched.

多表联结的核心原则如下:

(1)顺序无关:先联结表 A 和表 B,再联结表 C;与先联结表 A 和表 C,再联结表 B,结果一致(但性能可能有差异)。注意:SQL 执行多表连接时,数据库会先将两张表连接生成一个中间结果集,再用这个中间结果集和下一张表连接。中间结果集的大小直接决定了后续操作的耗时:

    • 如果先连接小表 / 过滤后结果少的表,中间结果集小,后续计算快。

    • 如果先连接大表 / 过滤后结果多的表,中间结果集大,后续计算慢。

(2)别名必用:多表联结时,必须给表起别名(如oi、p、c),否则字段易产生歧义(比如多个表都有id字段,应该使用 “别名.id”)。

(3)条件清晰:每个JOIN对应一个ON子句,明确当前关联的两个表的条件,避免混乱。

  

联结 vs 子查询

很多需求既可以用联结实现,也可以用子查询实现(比如 “查询 "手机" 分类的商品”),实现如下:

> select p.product_id, p.name, p.price, p.stock from product p
join product_category pc on p.category_id=pc.category_id
where pc.name='手机'

product_id|name        |price  |stock|
----------+------------+-------+-----+
         1|小米14手机      |4999.00| 1000|
         2|华为Mate60 Pro|6999.00|  800|
         3|苹果iPhone 15 |7999.00| 1200|
         4|vivo X100   |3999.00| 1500|
         5|OPPO Find X7|4499.00|  900|
         6|荣耀Magic6    |4299.00| 1100|
         7|红米K70       |2499.00| 2000|
         8|一加12        |4599.00|  850|
         9|真我GT Neo5   |2599.00| 1800|
        10|三星S24 Ultra |8999.00|  600|

10 row(s) fetched.

或者,使用子查询实现:

> select p.product_id, p.name, p.price, p.stock from product p
where p.category_id in (select category_id from product_category where name='手机')

product_id|name        |price  |stock|
----------+------------+-------+-----+
         1|小米14手机      |4999.00| 1000|
         2|华为Mate60 Pro|6999.00|  800|
         3|苹果iPhone 15 |7999.00| 1200|
         4|vivo X100   |3999.00| 1500|
         5|OPPO Find X7|4499.00|  900|
         6|荣耀Magic6    |4299.00| 1100|
         7|红米K70       |2499.00| 2000|
         8|一加12        |4599.00|  850|
         9|真我GT Neo5   |2599.00| 1800|
        10|三星S24 Ultra |8999.00|  600|

10 row(s) fetched.

联结和子查询的核心区别如下:

特性

联结(JOIN)

子查询

核心优势

性能更优(数据库优化更好)、支持跨表显示字段

逻辑更直观(先查条件再查数据)、新手易理解

适用场景

需要显示多个表的字段、大数据量查询

仅需显示单个表的字段、简单条件过滤

可读性

多表关联时结构清晰

嵌套层级多时可读性差

实战建议:

  • 若需要整合多个表的字段(比如商品名 + 分类名 + 订单号),优先用联结。

  • 若仅需用一个表的条件过滤另一个表(且只显示单表字段),子查询或联结均可,新手可先选子查询,熟悉后用联结提升性能。

  

说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
其他应用
公众号