
真实项目中数据不会只放在一张表里,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 的性能优化要点
下一篇我们将学习 索引与性能优化!
还没有评论
第一条回复通常最容易开启一场有价值的讨论。