MySQL logo

真实项目中数据不会只放在一张表里,JOIN 连接查询 是必须掌握的核心技能。

先准备两张示例表:

-- 用户表
CREATE TABLE users (
    id INT PRIMARY KEY,
    name VARCHAR(50),
    dept_id INT
) ENGINE=InnoDB;

INSERT INTO users VALUES
(1, '张三', 1),
(2, '李四', 1),
(3, '王五', 2),
(4, '赵六', 3),
(5, '孙七', NULL);  -- 没有部门

-- 部门表
CREATE TABLE departments (
    id INT PRIMARY KEY,
    name VARCHAR(50)
) ENGINE=InnoDB;

INSERT INTO departments VALUES
(1, '技术部'),
(2, '市场部'),
(3, '财务部'),
(4, '人事部');      -- 没有员工

一、JOIN 类型总览

-- INNER JOIN:两表都有的数据
-- LEFT JOIN:左表全部 + 右表匹配
-- RIGHT JOIN:右表全部 + 左表匹配
-- FULL JOIN:两表全部(MySQL 不支持,用 UNION 模拟)

二、INNER JOIN(内连接)

只返回两表中匹配的行。

SELECT u.name AS 员工, d.name AS 部门
FROM users u
INNER JOIN departments d ON u.dept_id = d.id;

结果:

员工部门
张三技术部
李四技术部
王五市场部
赵六财务部
× 孙七(无部门)和 人事部(无员工)不会出现在结果中

三、LEFT JOIN(左连接)

返回左表全部行,右表无匹配则为 NULL。

SELECT u.name AS 员工, d.name AS 部门
FROM users u
LEFT JOIN departments d ON u.dept_id = d.id;

结果:

员工部门
张三技术部
李四技术部
王五市场部
赵六财务部
孙七NULL ← 注意!

典型用途:找出没有部门的员工

SELECT u.name
FROM users u
LEFT JOIN departments d ON u.dept_id = d.id
WHERE d.id IS NULL;

四、RIGHT JOIN(右连接)

返回右表全部行,左表无匹配则为 NULL。

SELECT u.name AS 员工, d.name AS 部门
FROM users u
RIGHT JOIN departments d ON u.dept_id = d.id;

典型用途:找出没有员工的部门

SELECT d.name AS 空部门
FROM users u
RIGHT JOIN departments d ON u.dept_id = d.id
WHERE u.id IS NULL;

五、多表 JOIN

-- 三表连接:订单 → 客户 → 城市
SELECT o.id, c.name, ci.name AS city, o.amount
FROM orders o
JOIN customers c ON o.customer_id = c.id
JOIN cities ci ON c.city_id = ci.id
WHERE ci.name = '北京';

六、SELF JOIN(自连接)

表和自己连接,常用于层级结构。

-- 员工表(包含上级ID)
CREATE TABLE employees (
    id INT PRIMARY KEY,
    name VARCHAR(50),
    manager_id INT   -- 上级的ID
);

INSERT INTO employees VALUES
(1, '总经理', NULL),
(2, '技术总监', 1),
(3, '市场总监', 1),
(4, '前端开发', 2),
(5, '后端开发', 2);

-- 查询每个员工的上级
SELECT e.name AS 员工, m.name AS 上级
FROM employees e
LEFT JOIN employees m ON e.manager_id = m.id;

七、UNION 合并查询

-- 合并两个查询结果(去重)
SELECT name FROM users
UNION
SELECT name FROM departments;

-- 合并全部(不去重)
SELECT name FROM users
UNION ALL
SELECT name FROM departments;

八、实战案例

电商订单查询

-- 查询订单详情(含用户信息和商品信息)
SELECT 
    o.id AS 订单号,
    u.name AS 客户,
    p.name AS 商品,
    o.quantity AS 数量,
    p.price AS 单价,
    (o.quantity * p.price) AS 总价,
    o.order_date AS 日期
FROM orders o
JOIN users u ON o.user_id = u.id
JOIN products p ON o.product_id = p.id
WHERE o.order_date >= '2026-01-01'
ORDER BY 总价 DESC
LIMIT 10;

博客文章与评论

-- 查询文章及其评论数
SELECT 
    a.title,
    a.created_at,
    COUNT(c.id) AS comment_count
FROM articles a
LEFT JOIN comments c ON a.id = c.article_id
GROUP BY a.id
ORDER BY comment_count DESC;

九、性能注意事项

原则说明
JOIN 字段加索引关联字段必须有索引
小表驱动大表用小表作为驱动表
避免 SELECT *只取需要的字段
EXPLAIN 分析EXPLAIN 查看执行计划
-- 查看 JOIN 查询的执行计划
EXPLAIN SELECT u.name, d.name 
FROM users u
LEFT JOIN departments d ON u.dept_id = d.id;

本篇小结

√ 理解了 4 种 JOIN 的区别和用途
√ 学会了 INNER / LEFT / RIGHT JOIN 的使用
√ 掌握了多表 JOIN 和自连接技巧
√ 知道了 JOIN 的性能优化要点

下一篇我们将学习 索引与性能优化