左连接 LEFT JOIN|工作使用率最高,实战场景详解(避坑重点)
前言
上一篇我们学习了INNER JOIN 内连接,它只返回两张表的交集数据。但在真实企业场景里,我们经常需要:保留左表全部数据,无论右表是否匹配。比如:查询所有用户,包括没下过单的用户;查询所有商品,包括没被卖过的商品;查询所有部门,包括还没有员工的部门。
这时内连接就不够用了,必须使用LEFT JOIN 左连接—— 它是企业实际开发中使用率最高、面试必考的连接方式。本篇从原理、语法、实战、对比、避坑全维度精讲,学完直接落地工作。
一、本章知识点汇总
- LEFT JOIN 左连接核心原理
- 左连接标准语法与书写规范
- LEFT JOIN 与 INNER JOIN 的本质区别
- 左表、右表定义与数据保留规则
- 右表无匹配时 NULL 产生规律与处理
- 实战场景:查询全部用户 + 订单(含无订单用户)
- 实战场景:查询左表存在、右表不存在的数据(高频面试题)
- 左连接 + 过滤条件的书写位置(ON 与 WHERE 大坑)
- 三表左连接实战与企业规范
- 高频避坑与核心总结
二、各知识点详解
1. LEFT JOIN 核心原理(一句话秒懂)
LEFT JOIN = 左连接 = 保留左表全部记录 + 右表匹配记录
- 左表(写在 LEFT JOIN 左边的表):所有行强制保留
- 右表(写在 LEFT JOIN 右边的表):能匹配就显示,不能匹配显示NULL
- 最终结果条数≥ 左表条数
- 等价于:左表全集 ∪(左表∩右表)
通俗理解:以左表为主,右表为辅,左表一个都不能少,右表匹配不上就填空。
2. 左表与右表的定义
- 左表:FROM 后面第一张表主表
- 右表:LEFT JOIN 后面的表从表
企业原则:数据量大、必须全显示的表放左边(主表)
3. 标准语法(企业强制规范)
sql
SELECT 左表.字段, 右表.字段 FROM 左表 [AS 别名] LEFT JOIN 右表 [AS 别名] ON 左表.关联字段 = 右表.关联字段 -- 必须写 [WHERE 过滤条件];4. LEFT JOIN 与 INNER JOIN 核心区别
表格
| 对比项 | INNER JOIN | LEFT JOIN |
|---|---|---|
| 结果范围 | 只返回交集 | 左表全返回 + 右表匹配 |
| 无匹配数据 | 直接丢弃 | 左表保留,右表填 NULL |
| 企业使用率 | 中等 | 最高 |
| 典型场景 | 只看有效关联数据 | 必须看全部主数据 |
5. ON 与 WHERE 在 LEFT JOIN 中的关键区别(超级大坑)
- ON:连接时是否匹配的条件,不影响左表保留
- WHERE:连接完成后的过滤,会过滤掉左表数据
面试高频题:想保留左表全部,过滤条件必须写在ON还是WHERE? 答案:右表条件放 ON,左表条件放 WHERE。
6. 右表为 NULL 的两大用途
- 判断右表无匹配数据(如:没下单的用户)
- 用于取左表独有数据(面试高频:差集查询)
三、实战环境准备(沿用上篇案例,可直接运行)
sql
-- 用户表(左表:主表) CREATE TABLE users ( user_id INT PRIMARY KEY, user_name VARCHAR(20) NOT NULL, age INT, create_time DATE ); -- 订单表(右表:从表) CREATE TABLE orders ( order_id INT PRIMARY KEY, user_id INT, amount DECIMAL(10,2), order_time DATE ); INSERT INTO users VALUES (1,'张三',25,'2024-01-01'), (2,'李四',30,'2024-01-10'), (3,'王五',28,'2024-02-01'), (4,'赵六',32,'2024-03-01'), (5,'孙七',26,'2024-03-15'); -- 无订单 INSERT INTO orders VALUES (1001,1,299.00,'2024-06-01'), (1002,1,158.50,'2024-06-15'), (1003,2,420.00,'2024-06-20'), (1004,3,88.00,'2024-07-01'), (1005,6,350.00,'2024-07-10'); -- 无用户四、应用案例及结果分析
案例 1:基础左连接 —— 查询所有用户(含无订单用户)
需求:展示所有用户,以及他们的订单信息,没下单的用户也要显示。
sql
SELECT u.user_id, u.user_name, o.order_id, o.amount, o.order_time FROM users u -- 左表(主表) LEFT JOIN orders o -- 右表(从表) ON u.user_id = o.user_id;结果分析
- 左表 5 个用户全部保留
- 有订单的用户:订单信息正常显示
- 无订单的用户(孙七、赵六):右表字段全部为NULL
- 结果条数 ≥ 左表条数(这里有 6 条,因为张三有 2 个订单)
案例 2:左连接 + 条件 —— 查询 2024-07 之后的订单,保留所有用户
需求:所有用户都显示,只匹配 7 月后订单,没下单的依然显示。
sql
SELECT u.user_id, u.user_name, o.order_id, o.amount FROM users u LEFT JOIN orders o ON u.user_id = o.user_id AND o.order_time >= '2024-07-01'; -- 右表条件放 ON,不影响左表结果分析
- 左表依然全部保留
- 只有 7 月后订单被匹配,其余订单为 NULL
- 千万不要把条件放 WHERE,否则会把左表无匹配数据过滤掉,变成内连接效果
案例 3:面试高频 —— 查询从来没有下过单的用户(左表有、右表无)
sql
SELECT u.user_id, u.user_name FROM users u LEFT JOIN orders o ON u.user_id = o.user_id WHERE o.order_id IS NULL; -- 右表主键为NULL = 无匹配结果分析
- 直接筛选出:赵六、孙七
- 这是企业流失用户、沉默用户、未使用用户的标准查询写法
案例 4:三表左连接(企业真实场景)
需求:所有用户 + 订单 + 订单明细,无数据显示 NULL
sql
-- 先建商品表、订单明细表 CREATE TABLE goods(goods_id INT PRIMARY KEY,goods_name VARCHAR(20),price DECIMAL(10,2)); CREATE TABLE order_item(id INT PRIMARY KEY,order_id INT,goods_id INT,num INT); INSERT INTO goods VALUES(1,'笔记本',299),(2,'键盘',158.5),(3,'鼠标',88); INSERT INTO order_item VALUES(1,1001,1,1),(2,1002,2,1),(3,1003,3,1),(4,1004,3,1); -- 三表左连接 SELECT u.user_name, o.order_id, g.goods_name, oi.num FROM users u LEFT JOIN orders o ON u.user_id = o.user_id LEFT JOIN order_item oi ON o.order_id = oi.order_id LEFT JOIN goods g ON oi.goods_id = g.goods_id;结果分析
- 左表 users 全程为主表,全部保留
- 多级关联不匹配均显示 NULL
- 电商、报表、统计最常用写法
五、注意事项
- 左表右表不要搞反:必须全显示的表放左边
- 右表过滤条件优先放 ON:否则会把左表数据过滤掉,等价于内连接
- 判断无匹配用 IS NULL:不能用 = NULL
- ** 不要用 SELECT ***:多表连接字段多,影响性能
- 关联字段必须加索引:左连接大数据量下极吃索引
- 左连接结果条数 ≥ 左表条数:出现更少说明条件写错
- 禁止在左连接后用 NOT IN:容易踩 NULL 陷阱,优先用 IS NULL
- 多表连接保持顺序:主表 → 从表 1 → 从表 2,逻辑清晰
六、核心总结
- LEFT JOIN 以左表为主,左表所有记录强制保留
- 右表匹配不上,字段全部显示NULL
- 右表条件写 ON,左表条件写 WHERE
- 右表主键 IS NULL =左表独有、右表无(面试 / 工作高频)
- 企业场景:用户、商品、部门、员工等必须全量展示的优先用左连接
- 左连接是工作中最常用的连接,远超内连接、右连接
- 三表 / 多表左连接:主表一直放最左,依次关联从表
一句话记忆:左连接主表全保留,右表不匹配填 NULL;右表条件放 ON,无匹配用 IS NULL
七、课后实战练习题(附答案思路)
题目 1
用 LEFT JOIN 查询:所有用户的姓名、总订单数、总消费金额,没下过单的显示 0。 (提示:用 IFNULL/COALESCE 处理 NULL)
题目 2
查询所有商品,以及被购买的次数、总销量,没被买过的商品也要显示。
参考答案思路
题目 1:
sql
SELECT u.user_name, COUNT(o.order_id) AS order_cnt, COALESCE(SUM(o.amount),0) AS total_amount FROM users u LEFT JOIN orders o ON u.user_id = o.user_id GROUP BY u.user_id, u.user_name;题目 2:
sql
SELECT g.goods_name, COUNT(oi.id) AS buy_cnt, COALESCE(SUM(oi.num),0) AS total_num FROM goods g LEFT JOIN order_item oi ON g.goods_id = oi.goods_id GROUP BY g.goods_id, g.goods_name;