SQL多表查询完全指南:JOIN的7种用法详解(附实战代码)

0 阅读5分钟

SQL多表查询完全指南:JOIN的7种用法详解(附实战代码)

数据分析工作中,80%的复杂查询都离不开多表关联。本文用7种JOIN场景,带你从入门到精通。

一、为什么多表查询这么重要?

真实业务数据从来不是一张表能搞定的。

用户信息在users表,订单数据在orders表,商品信息在products表。你要分析"每个用户的购买偏好",必须把三张表关联起来。

不会JOIN,等于你只能看到数据的碎片。

二、数据准备

先建两张测试表:

-- ============================================================
-- 表结构准备 - 员工表和部门表
-- 公众号:船长Talk(SQL实战系列,关注获取更多)
-- ============================================================

CREATE TABLE departments (
    dept_id INT PRIMARY KEY,
    dept_name VARCHAR(50),
    city VARCHAR(50)
);

CREATE TABLE employees (
    emp_id INT PRIMARY KEY,
    emp_name VARCHAR(50),
    dept_id INT,
    salary DECIMAL(10,2),
    hire_date DATE
);

-- 插入测试数据
INSERT INTO departments VALUES
(1, '技术部', '北京'),
(2, '市场部', '上海'),
(3, '财务部', '深圳'),
(4, '人事部', '广州');

INSERT INTO employees VALUES
(101, '张三', 1, 25000, '2024-03-15'),
(102, '李四', 1, 22000, '2024-06-01'),
(103, '王五', 2, 18000, '2024-01-10'),
(104, '赵六', NULL, 20000, '2024-09-20'),
(105, '孙七', 5, 16000, '2025-02-01');

注意:赵六没有部门(dept_id为NULL),孙七的部门ID是5(在departments表中不存在)。这就是后面各种JOIN产生差异的关键。

三、7种JOIN用法详解

1. INNER JOIN(内连接)—— 只取交集

-- ============================================================
-- INNER JOIN:只返回两表都匹配的记录
-- 公众号:船长Talk(最常用的JOIN类型)
-- ============================================================

SELECT 
    e.emp_name,
    e.salary,
    d.dept_name,
    d.city
FROM employees e
INNER JOIN departments d ON e.dept_id = d.dept_id;

-- 结果:张三、李四、王五(赵六和孙七不会出现)
-- 因为赵六没有dept_id,孙七的dept_id=5在departments中不存在

📌 使用场景:日常分析最常用,只要"有完整关联关系"的数据。

2. LEFT JOIN(左连接)—— 保留左表全部

-- ============================================================
-- LEFT JOIN:保留左表所有记录,右表无匹配则为NULL
-- 公众号:船长Talk(排查数据缺失必备)
-- ============================================================

SELECT 
    e.emp_name,
    e.salary,
    d.dept_name,
    d.city
FROM employees e
LEFT JOIN departments d ON e.dept_id = d.dept_id;

-- 结果:张三、李四、王五、赵六(dept_name=NULL)、孙七(dept_name=NULL)
-- 赵六和孙七都会出现,只是部门信息为NULL

📌 使用场景:排查"哪些员工没有分配部门"、"哪些用户没有下单"这类问题。90%的数据排查场景用LEFT JOIN。

3. RIGHT JOIN(右连接)—— 保留右表全部

-- ============================================================
-- RIGHT JOIN:保留右表所有记录,左表无匹配则为NULL
-- 公众号:船长Talk(反向排查用)
-- ============================================================

SELECT 
    e.emp_name,
    e.salary,
    d.dept_name,
    d.city
FROM employees e
RIGHT JOIN departments d ON e.dept_id = d.dept_id;

-- 结果:张三、李四、王五 + 人事部(没有员工)
-- 人事部会出现在结果中,员工字段为NULL

📌 使用场景:"哪些部门还没有人"——组织架构分析常用。

4. FULL OUTER JOIN(全外连接)—— 保留两表全部

-- ============================================================
-- FULL OUTER JOIN:两表所有记录都保留
-- 公众号:船长Talk(数据对账神器)
-- ============================================================

SELECT 
    COALESCE(e.emp_name, '无员工') AS emp_name,
    COALESCE(d.dept_name, '无部门') AS dept_name
FROM employees e
FULL OUTER JOIN departments d ON e.dept_id = d.dept_id;

-- 结果:所有员工 + 所有部门 + 两种NULL情况
-- 注意:MySQL不支持FULL OUTER JOIN,用UNION模拟

📌 MySQL替代写法

-- MySQL不支持FULL JOIN,用LEFT JOIN + UNION + RIGHT JOIN模拟
SELECT e.emp_name, d.dept_name
FROM employees e LEFT JOIN departments d ON e.dept_id = d.dept_id
UNION
SELECT e.emp_name, d.dept_name
FROM employees e RIGHT JOIN departments d ON e.dept_id = d.dept_id;

5. LEFT JOIN + IS NULL(找出不匹配的记录)

-- ============================================================
-- 找出"左表有但右表没有"的记录
-- 公众号:船长Talk(数据质量检查必备)
-- ============================================================

-- 找出没有部门的员工
SELECT e.emp_name, e.salary, e.dept_id
FROM employees e
LEFT JOIN departments d ON e.dept_id = d.dept_id
WHERE d.dept_id IS NULL;

-- 结果:赵六(dept_id=NULL)、孙七(dept_id=5但部门不存在)

📌 使用场景:数据质量检查。"孤儿记录"排查,BI报表数据异常定位。

6. 自连接(Self JOIN)—— 同一张表自己关联

-- ============================================================
-- Self JOIN:同一张表自己关联自己
-- 公众号:船长Talk(组织架构分析必备)
-- ============================================================

-- 假设有manager_id字段,查找每个员工及其上级
SELECT 
    e1.emp_name AS 员工,
    e2.emp_name AS 直属上级
FROM employees e1
LEFT JOIN employees e2 ON e1.manager_id = e2.emp_id;

-- 经典场景:找工资比自己经理高的员工
SELECT e1.emp_name AS 员工, e1.salary AS 员工薪资,
       e2.emp_name AS 经理, e2.salary AS 经理薪资
FROM employees e1
JOIN employees e2 ON e1.manager_id = e2.emp_id
WHERE e1.salary > e2.salary;

📌 使用场景:组织架构分析、查找重复数据、时间序列对比。

7. 多表JOIN + 聚合(实战场景)

-- ============================================================
-- 多表JOIN + GROUP BY:每个部门的薪资统计
-- 公众号:船长Talk(数据分析高频面试题)
-- ============================================================

SELECT 
    d.dept_name AS 部门,
    d.city AS 城市,
    COUNT(e.emp_id) AS 员工数,
    ROUND(AVG(e.salary), 2) AS 平均薪资,
    MAX(e.salary) AS 最高薪资,
    MIN(e.salary) AS 最低薪资
FROM departments d
LEFT JOIN employees e ON d.dept_id = e.dept_id
GROUP BY d.dept_id, d.dept_name, d.city
ORDER BY 平均薪资 DESC;

-- 注意:用LEFT JOIN + GROUP BY可以统计出"员工数为0"的部门
-- 如果用INNER JOIN,员工数为0的部门会被过滤掉

四、3个容易踩的坑

坑1:JOIN条件写错,产生笛卡尔积

忘写ON条件,两张表的每条记录都会互相匹配。1000条 × 1000条 = 100万条结果,直接把数据库跑挂。

坑2:LEFT JOIN后用WHERE过滤右表字段

WHERE d.dept_name = '技术部'会把LEFT JOIN变成INNER JOIN的效果。应该用AND放在ON条件里。

坑3:多表JOIN性能差

超过3张表JOIN时,先检查关联字段是否有索引。JOIN字段没有索引,数据量一大就慢得离谱。

📌 更多资源

👉 关注公众号:船长Talk,获取更多SQL实战教程

👉 回复关键词**【SQL】**获取完整SQL学习路线图

👉 加入技术交流群,与500+数据分析师一起成长