SQL 核心概念:JOIN 和 UNION 到底有什么区别?

📖 先说结论

很多初学者容易把 JOIN 和 UNION 搞混,因为它们都是"把两张表合在一起"。但合并的方向完全不同:

📄 JOIN 是把两张表左右合并,像把两张纸并排贴在一起

📄 UNION 是把两张表上下合并,像把两张纸上下拼接成一张长纸

用一张图来理解:

css
 体验AI代码助手
 代码解读
复制代码
JOIN(左右合并):                UNION(上下合并):

表AB      结果            表A       结果
─────  + ─────  = ───────────     ─────     ─────
行1      行X      行1 | 行X        行112      行Y      行2 | 行Y        行223      行Z      行3 | 行Z        ─────  =  行3B       ─────
                                  行AABB

AI写代码text
12345678910

🔗 一、JOIN —— 横向拼接

核心思想

JOIN 用于将同一业务实体的不同维度拼在一起。两张表之间必须存在可以对齐的关联关系(通常是外键),把符合条件的行左右合并成一行。

典型场景

我有一张订单表,想同时看到订单信息 + 下单患者的姓名

订单表和患者表之间存在 patient_id 关联,每一条订单对应一个患者,这就是典型的横向拼接场景。

vbnet
 体验AI代码助手
 代码解读
复制代码
-- 查询订单,同时展示患者姓名
SELECT 
    o.order_no    AS 订单号,
    o.amount      AS 金额,
    u.real_name   AS 患者姓名
FROM orders o
JOIN patient_user u ON o.patient_id = u.id
WHERE o.create_time >= '2024-01-01'

AI写代码sql
12345678

结果长这样:

arduino
 体验AI代码助手
 代码解读
复制代码
订单号          金额      患者姓名
ORDER_001      199.00   张三
ORDER_002      299.00   李四
ORDER_003      99.00    张三

AI写代码text
1234

每一行都是一条订单 + 这条订单对应的患者,数据是横向扩展的,列变多了,行数不变(或者因为匹配关系有所变化)。

JOIN 的几种类型

css
 体验AI代码助手
 代码解读
复制代码
-- INNER JOIN(内连接):只返回两边都能匹配上的行
SELECT * FROM A INNER JOIN B ON A.id = B.a_id

-- LEFT JOIN(左连接):左表全返回,右表没匹配到的填 NULL
SELECT * FROM A LEFT JOIN B ON A.id = B.a_id

-- RIGHT JOIN(右连接):右表全返回,左表没匹配到的填 NULL
SELECT * FROM A RIGHT JOIN B ON A.id = B.a_id

AI写代码sql
12345678

💡 记忆口诀:JOIN 是"找对象",两行数据牵手合并成一行,牵手的条件就是 ON 后面的关联字段。


🔀 二、UNION —— 纵向堆叠

核心思想

UNION 用于将结构相同、但来源不同的数据集纵向堆叠在一起。两个查询的列数和数据类型必须一致,把多个查询的结果上下拼成一张更长的表。

典型场景

我有体重日常记录表和 InBody 专业测量表,想在趋势图里把两边的体重数据按时间顺序展示在同一条折线上。

这两张表的记录之间没有对应关系,体重记录表有365条,InBody表有12条,它们都是独立的测量事件。我需要的是把它们纵向堆叠,按时间排列:

sql
 体验AI代码助手
 代码解读
复制代码
-- 体重趋势图:把两张表的数据纵向合并
SELECT record_time, weight, '日常测量' AS source
FROM patient_weight_record
WHERE patient_id = 1

UNION ALL

SELECT record_time, weight, 'InBody测量' AS source
FROM patient_body_composition
WHERE patient_id = 1

ORDER BY record_time

AI写代码sql
123456789101112

结果长这样:

yaml
 体验AI代码助手
 代码解读
复制代码
record_time           weight    source
2024-01-01 07:00     85.50    日常测量
2024-01-05 09:00     85.20    日常测量
2024-01-10 10:00     84.80    InBody测量    来自另一张表
2024-01-15 07:30     84.50    日常测量
2024-01-20 08:00     84.10    日常测量
2024-02-10 10:00     83.60    InBody测量    来自另一张表

AI写代码
1234567

数据是纵向扩展的,列数不变,行数变多了

UNION 和 UNION ALL 的区别

sql
 体验AI代码助手
 代码解读
复制代码
-- UNION:自动去重(性能较差,需要额外排序去重)
SELECT name FROM table_a
UNION
SELECT name FROM table_b

-- UNION ALL:不去重,直接合并(性能更好)
SELECT name FROM table_a
UNION ALL
SELECT name FROM table_b

AI写代码sql
123456789

⚠️ 原则:如果你确定两个结果集不会有重复数据,或者你不在乎重复,优先用 UNION ALL,性能更好。只有在明确需要去重时才用 UNION。


⚖️ 三、核心对比

对比维度JOINUNION
📐 合并方向横向(列变多)纵向(行变多)
🔗 前提条件两表有关联字段两个查询列数和类型一致
📊 结果形态行数取决于匹配关系行数 = 两个查询结果之和
🎯 适用场景同一实体的不同维度同类数据的不同来源
🔑 关键字ON 指定关联条件无需关联条件
⚡ 典型用法订单 + 用户信息多个来源的同类数据合并

🎯 四、如何判断用哪个?

遇到"把两张表合在一起"的需求时,问自己两个问题: https://www.liangxunacc.com/关于北京159.1415.8529北京开具仪器设备发票‖第一财经_2021.html https://www.liangxunacc.com/关于天津159.1415.8529天津开具仪器设备发票‖第一财经_2021.html https://www.liangxunacc.com/关于上海159.1415.8529上海开具仪器设备发票‖第一财经_2021.html https://www.liangxunacc.com/关于重庆159.1415.8529重庆开具仪器设备发票‖第一财经_2021.html https://www.liangxunacc.com/关于太原159.1415.8529太原开具仪器设备发票‖第一财经_2021.html https://www.liangxunacc.com/关于石家庄159.1415.8529石家庄开具仪器设备发票‖第一财经_2021.html https://www.liangxunacc.com/关于呼和浩特159.1415.8529呼和浩特开具仪器设备发票‖第一财经_2021.html https://www.liangxunacc.com/关于沈阳159.1415.8529沈阳开具仪器设备发票‖第一财经_2021.html https://www.liangxunacc.com/关于吉林159.1415.8529吉林开具仪器设备发票‖第一财经_2021.html https://www.liangxunacc.com/关于长春159.1415.8529长春开具仪器设备发票‖第一财经_2021.html https://www.liangxunacc.com/关于哈尔滨159.1415.8529哈尔滨开具仪器设备发票‖第一财经_2021.html https://www.liangxunacc.com/关于南京159.1415.8529南京开具仪器设备发票‖第一财经_2021.html https://www.liangxunacc.com/关于苏州159.1415.8529苏州开具仪器设备发票‖第一财经_2021.html https://www.liangxunacc.com/关于杭州159.1415.8529杭州开具仪器设备发票‖第一财经_2021.html https://www.liangxunacc.com/关于合肥159.1415.8529合肥开具仪器设备发票‖第一财经_2021.html https://www.liangxunacc.com/关于厦门159.1415.8529厦门开具仪器设备发票‖第一财经_2021.html https://www.liangxunacc.com/关于福州159.1415.8529福州开具仪器设备发票‖第一财经_2021.html https://www.liangxunacc.com/关于南昌159.1415.8529南昌开具仪器设备发票‖第一财经_2021.html https://www.liangxunacc.com/关于济南159.1415.8529济南开具仪器设备发票‖第一财经_2021.html https://www.liangxunacc.com/关于青岛159.1415.8529青岛开具仪器设备发票‖第一财经_2021.html https://www.liangxunacc.com/关于郑州159.1415.8529郑州开具仪器设备发票‖第一财经_2021.html https://www.liangxunacc.com/关于武汉159.1415.8529武汉开具仪器设备发票‖第一财经_2021.html https://www.liangxunacc.com/关于长沙159.1415.8529长沙开具仪器设备发票‖第一财经_2021.html https://www.liangxunacc.com/关于深圳159.1415.8529深圳开具仪器设备发票‖第一财经_2021.html https://www.liangxunacc.com/关于广州159.1415.8529广州开具仪器设备发票‖第一财经_2021.html https://www.liangxunacc.com/关于东莞159.1415.8529东莞开具仪器设备发票‖第一财经_2021.html https://www.liangxunacc.com/关于南宁159.1415.8529南宁开具仪器设备发票‖第一财经_2021.html https://www.liangxunacc.com/关于海口159.1415.8529海口开具仪器设备发票‖第一财经_2021.html https://www.liangxunacc.com/关于三亚159.1415.8529三亚开具仪器设备发票‖第一财经_2021.html https://www.liangxunacc.com/关于成都159.1415.8529成都开具仪器设备发票‖第一财经_2021.html https://www.liangxunacc.com/关于贵阳159.1415.8529贵阳开具仪器设备发票‖第一财经_2021.html https://www.liangxunacc.com/关于昆明159.1415.8529昆明开具仪器设备发票‖第一财经_2021.html https://www.liangxunacc.com/关于拉萨159.1415.8529拉萨开具仪器设备发票‖第一财经_2021.html https://www.liangxunacc.com/关于西安159.1415.8529西安开具仪器设备发票‖第一财经_2021.html https://www.liangxunacc.com/关于兰州159.1415.8529兰州开具仪器设备发票‖第一财经_2021.html https://www.liangxunacc.com/关于西宁159.1415.8529西宁开具仪器设备发票‖第一财经_2021.html https://www.liangxunacc.com/关于银川159.1415.8529银川开具仪器设备发票‖第一财经_2021.html https://www.liangxunacc.com/关于乌鲁木齐159.1415.8529乌鲁木齐开具仪器设备发票‖第一财经_2021.html

问题一:我想要的结果,列数是变多了,还是行数变多了?

列变多 → JOIN,行变多 → UNION。

问题二:两张表的数据是"同一件事的不同角度",还是"同类事情的不同来源"?

markdown
 体验AI代码助手
 代码解读
复制代码
同一件事的不同角度(一条订单 + 这条订单的用户名)
    → JOIN,横向拼接

同类事情的不同来源(体脂秤的体重记录 + InBody的体重记录)
    → UNION,纵向堆叠

AI写代码text
12345

实际案例判断练习

sql
 体验AI代码助手
 代码解读
复制代码
❓ 查询患者信息,同时展示主管医生的姓名
→ 患者表 JOIN 医生表(同一患者档案的不同维度)✅ JOIN

❓ 查询本月所有健康指标的预警记录(血压预警 + 血糖预警 + 体重预警分别在不同表)
→ 三张预警表纵向合并(同类事件的不同来源)✅ UNION ALL

❓ 查询运动记录,同时展示当天的饮食热量
→ 运动表 LEFT JOIN 饮食表(同一天的不同健康维度)✅ JOIN

❓ 查询某患者所有沟通记录(患者发的 + 医生发的 + 系统消息)
→ 这个其实在同一张表里,按 sender_type 区分,不需要 UNION

AI写代码text
1234567891011

🚫 五、常见误区

❌ 误区一:以为 JOIN 可以代替 UNION

sql
 体验AI代码助手
 代码解读
复制代码
-- ❌ 错误思路:用 JOIN 合并两张体重表
SELECT w.record_time, w.weight, b.weight
FROM patient_weight_record w
JOIN patient_body_composition b ON w.patient_id = b.patient_id
-- 结果是笛卡尔积:365条 × 12条 = 4380条,完全错误!

AI写代码sql
12345

两张表没有行级别的对应关系,JOIN 会产生笛卡尔积,结果毫无意义。

❌ 误区二:UNION 的列不对齐

sql
 体验AI代码助手
 代码解读
复制代码
-- ❌ 错误:两个查询列数不一致
SELECT record_time, weight FROM patient_weight_record
UNION ALL
SELECT record_time, weight, bmi FROM patient_body_composition
-- 报错:列数不匹配

AI写代码sql
12345
sql
 体验AI代码助手
 代码解读
复制代码
-- ✅ 正确:列数和类型对齐,缺少的列用 NULL 补位
SELECT record_time, weight, NULL AS bmi FROM patient_weight_record
UNION ALL
SELECT record_time, weight, bmi FROM patient_body_composition

AI写代码sql
1234

❌ 误区三:UNION 后忘记加 ORDER BY

sql
 体验AI代码助手
 代码解读
复制代码
-- ⚠️ 注意:ORDER BY 要放在最后,对整体结果排序
SELECT record_time, weight FROM patient_weight_record WHERE patient_id = 1
UNION ALL
SELECT record_time, weight FROM patient_body_composition WHERE patient_id = 1
ORDER BY record_time  -- ✅ 放在最后,对合并后的全部结果排序

AI写代码sql
12345

📌 六、一句话总结

🎯 JOIN 是找对象(两行数据牵手变成一行,列变多);UNION 是排队(两批数据排成一队,行变多)。搞清楚你要的是"更宽的表"还是"更长的表",就知道该用哪个了。

0
0
0
0
评论
未登录
暂无评论