【MySQL 笔记】多表查询(Multi-Table Query / JOIN)详解

多表查询是 MySQL 中最核心、最常用的技能之一,几乎所有真实业务场景(订单+用户、商品+分类、员工+部门等)都离不开它。

一、MySQL 多表查询核心方式对比(2026 年主流用法)

查询方式 关键字 / 写法 保留哪些行 结果行数特征 性能影响 典型业务场景 推荐指数
隐式连接(旧写法) FROM A, B WHERE A.id = B.id 只匹配的行(相当于 INNER JOIN) ≤ min(A,B) 中等 历史代码、简单查询 ★★☆☆☆
INNER JOIN INNER JOIN / JOIN 两表都匹配的行 ≤ min(A,B) 较好 必须双方都有数据的查询 ★★★★★
LEFT [OUTER] JOIN LEFT JOIN 左表全部 + 右表匹配(右缺→NULL) = 左表行数 中等 查所有商品 + 是否有销量(没卖的也显示) ★★★★★
RIGHT [OUTER] JOIN RIGHT JOIN 右表全部 + 左表匹配(左缺→NULL) = 右表行数 中等 查所有课程 + 报名情况 ★★★★☆
FULL [OUTER] JOIN FULL JOIN(MySQL 不原生支持) 两表全部(不匹配补 NULL) ≈ A + B - 交集 较高 数据对账、找出差异记录 ★★★★☆
CROSS JOIN CROSS JOIN / ,(逗号无条件) 笛卡尔积(每行×每行) = A行数 × B行数 高(爆炸) 生成测试数据、全组合 ★★☆☆☆
SELF JOIN 表自己 JOIN 自己(用别名) 同表不同角色匹配 取决于 ON 条件 中等 员工-经理、分类父子、上下级 ★★★★☆

二、经典示例表(建议自己建表练习)

-- 部门表
CREATE TABLE dept (
    deptno INT PRIMARY KEY,
    dname  VARCHAR(20),
    loc    VARCHAR(20)
);

INSERT INTO dept VALUES
(10, 'RESEARCH', 'BEIJING'),
(20, 'SALES',    'SHANGHAI'),
(30, 'ACCOUNTING','GUANGZHOU'),
(40, 'OPERATIONS','SHENZHEN');  -- 没人

-- 员工表
CREATE TABLE emp (
    empno    INT PRIMARY KEY,
    ename    VARCHAR(20),
    job      VARCHAR(20),
    mgr      INT,          -- 领导编号(自关联)
    sal      DECIMAL(10,2),
    deptno   INT,
    FOREIGN KEY (deptno) REFERENCES dept(deptno)
);

INSERT INTO emp VALUES
(7369,'SMITH','CLERK',7902,  800.00,  20),
(7499,'ALLEN','SALESMAN',7698,1600.00, 30),
(7521,'WARD', 'SALESMAN',7698,1250.00, 30),
(7566,'JONES','MANAGER',7839,2975.00, 20),
(7698,'BLAKE','MANAGER',7839,2850.00, 30),
(7782,'CLARK','MANAGER',7839,2450.00, 10),
(7839,'KING', 'PRESIDENT',NULL,5000.00,10);

三、各种 JOIN 实战写法(全部敲一遍最有感觉)

-- 1. INNER JOIN(最常用)
SELECT e.ename, e.job, d.dname, d.loc
FROM emp e
INNER JOIN dept d ON e.deptno = d.deptno;
-- 结果:只显示有部门的员工(deptno=40 的部门没人,不显示)

-- 2. LEFT JOIN(保留左表全部)
SELECT e.ename, e.job, d.dname, d.loc
FROM emp e
LEFT JOIN dept d ON e.deptno = d.deptno;
-- 结果:所有员工 + 部门信息(即使 deptno 不匹配也显示员工,dname 为 NULL)

-- 3. RIGHT JOIN(保留右表全部)
SELECT e.ename, d.dname, d.loc
FROM emp e
RIGHT JOIN dept d ON e.deptno = d.deptno;
-- 结果:所有部门 + 员工(OPERATIONS 部门没人,ename 为 NULL)

-- 4. MySQL 模拟 FULL JOIN(最常见写法)
(SELECT e.ename, d.dname FROM emp e LEFT JOIN dept d ON e.deptno = d.deptno)
UNION
(SELECT e.ename, d.dname FROM emp e RIGHT JOIN dept d ON e.deptno = d.deptno 
 WHERE e.deptno IS NULL);
-- 结果:所有员工 + 所有部门,没匹配的补 NULL

-- 5. CROSS JOIN(笛卡尔积,慎用!)
SELECT e.ename, d.dname
FROM emp e CROSS JOIN dept d;
-- 结果:7员工 × 4部门 = 28 行(爆炸)

-- 6. SELF JOIN(员工找上级)
SELECT e.ename AS 员工, m.ename AS 上级
FROM emp e
LEFT JOIN emp m ON e.mgr = m.empno;
-- 结果:显示每个员工及其直接领导(KING 没有上级 → NULL)

-- 7. 三表联查示例(部门 → 员工 → 上级)
SELECT e.ename, e.job, d.dname, m.ename AS manager_name
FROM emp e
INNER JOIN dept d ON e.deptno = d.deptno
LEFT JOIN  emp m ON e.mgr = m.empno;

四、高频业务场景总结(面试 + 实战)

场景 推荐 JOIN 类型 关键写法技巧
订单详情 + 用户信息(必须有用户) INNER JOIN 脏数据过滤
所有商品 + 最近销量(没卖出显示0) LEFT JOIN + COALESCE COALESCE(sales, 0)
所有课程 + 选课人数(没人选也显示) LEFT JOIN + GROUP BY COUNT(student_id)
找出只在一张表有的记录 FULL JOIN(模拟)或 LEFT+IS NULL WHERE 右表.id IS NULL
员工上下级关系 SELF JOIN 用别名区分角色
生成测试数据组合 CROSS JOIN + LIMIT 控制行数

五、写多表查询的实用技巧 & 2026 年注意点

  1. 永远写 ON 条件(别把连接条件写 WHERE,容易逻辑错乱)
  2. 小表驱动大表:LEFT JOIN 时把小表放左边(优化器通常会调整,但显式写好)
  3. JOIN 字段必须有索引(尤其是外键、主键、频繁 ON 的列)
  4. 用表别名(e、d、o、u 常用,清晰 + 避免歧义)
  5. NULL 判断:LEFT JOIN 后常用 WHERE xx IS NULL 找“只有左表有的”
  6. EXPLAIN 验证:写完复杂 JOIN 后先 EXPLAIN 看执行计划
  7. **避免 SELECT ***:明确列出需要的字段(减少 IO)

下一期预告建议:MySQL 子查询(Subquery)全家桶 + 相关子查询 vs 非相关 + IN / EXISTS 性能对比

有特别迷惑的多表查询写法(比如三表以上、复杂过滤、性能卡住等),欢迎留言,我们一起画图分析~
祝大家 JOIN 写得又准又快!🚀

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐