多表关联(JOIN)
约 1029 字大约 3 分钟
布欧-Lewyon
2026-05-15
首页 › MySQL › 高级查询(在新窗口打开) › 多表关联(JOIN)
真实业务数据很少只存在一张表中,多表关联查询是 SQL 最核心的能力之一。
准备两张示例表,你可以在下面的在线运行框中尝试各种 JOIN 查询:
▶ SQL 在线运行 加载中...
CREATE TABLE department (
id INT PRIMARY KEY,
name VARCHAR(50)
);
CREATE TABLE employee (
id INT PRIMARY KEY,
name VARCHAR(50),
dept_id INT,
salary DECIMAL(10,2)
);
INSERT INTO department VALUES
(1, '技术部'), (2, '市场部'), (3, '人事部'), (4, '财务部');
INSERT INTO employee VALUES
(1, '张三', 1, 15000),
(2, '李四', 2, 12000),
(3, '王五', 1, 18000),
(4, '赵六', 1, 22000),
(5, '孙七', 2, 11000),
(6, '周八', 3, 9000),
(7, '吴九', NULL, NULL);
INNER JOIN(内连接)
只返回两表中匹配的行:
SELECT e.name, d.name AS dept_name
FROM employee e
INNER JOIN department d ON e.dept_id = d.id;
-- 结果:张三-技术部、李四-市场部、王五-技术部、赵六-技术部、孙七-市场部、周八-人事部
-- 吴九(dept_id 为 NULL)没有匹配,不出现在结果中LEFT JOIN(左外连接)
返回左表所有行,右表无匹配时填充 NULL:
SELECT e.name, d.name AS dept_name
FROM employee e
LEFT JOIN department d ON e.dept_id = d.id;
-- 7 行:所有员工都出现,吴九的 dept_name 为 NULLRIGHT JOIN(右外连接)
返回右表所有行,左表无匹配时填充 NULL:
SELECT e.name, d.name AS dept_name
FROM employee e
RIGHT JOIN department d ON e.dept_id = d.id;
-- 7 行:所有部门都出现,财务部(无员工)的 name 为 NULL
RIGHT JOIN可以通过调换表顺序改写成LEFT JOIN,建议统一使用LEFT JOIN方便阅读。
CROSS JOIN(交叉连接/笛卡尔积)
左表每行 × 右表每行,通常需要配合 WHERE 条件使用:
SELECT e.name, d.name
FROM employee e
CROSS JOIN department d;
-- 7 × 4 = 28 行日常开发中极少直接使用
CROSS JOIN,出现时往往是无意中造成的性能问题(忘记写 JOIN 条件)。
SELF JOIN(自连接)
同一张表自己关联自己,常用于层级数据(如员工上级关系):
-- 给 employee 加一个上级字段
ALTER TABLE employee ADD COLUMN manager_id INT;
UPDATE employee SET manager_id = 3 WHERE id = 1; -- 张三上级是王五
UPDATE employee SET manager_id = 4 WHERE id = 2; -- 李四上级是赵六
UPDATE employee SET manager_id = 4 WHERE id = 3; -- 王五上级是赵六
UPDATE employee SET manager_id = 4 WHERE id = 5; -- 孙七上级是赵六
UPDATE employee SET manager_id = 6 WHERE id = 7; -- 吴九上级是周八
-- 查询每个员工及其上级
SELECT
e.name AS 员工,
m.name AS 上级
FROM employee e
LEFT JOIN employee m ON e.manager_id = m.id;
-- 张三-王五、王五-赵六、赵六-NULL(CEO)关联条件 vs 过滤条件的位置差异
ON vs WHERE 的区别
-- 写法 A:过滤条件在 WHERE
SELECT e.name, d.name AS dept_name
FROM employee e
LEFT JOIN department d ON e.dept_id = d.id
WHERE d.name = '技术部';
-- 只返回 3 行(技术部的员工),LEFT JOIN 退化为 INNER JOIN
-- 写法 B:过滤条件在 ON
SELECT e.name, d.name AS dept_name
FROM employee e
LEFT JOIN department d ON e.dept_id = d.id AND d.name = '技术部';
-- 返回 7 行,属于技术部的显示部门名,其他的部门名为 NULL规则:
ON决定如何连接——右表的行是否匹配。WHERE在连接完成后过滤结果中的行。- 对
INNER JOIN,两者等价;对OUTER JOIN(LEFT/RIGHT),行为不同。
多表关联
-- 三表关联:员工 → 部门 → 项目
SELECT e.name, d.name AS dept, p.name AS project
FROM employee e
LEFT JOIN department d ON e.dept_id = d.id
LEFT JOIN project_assignment pa ON e.id = pa.employee_id
LEFT JOIN project p ON pa.project_id = p.id;小结
INNER JOIN:只取匹配行;LEFT JOIN:保左表全量;RIGHT JOIN:保右表全量。CROSS JOIN:笛卡尔积,通常只在特殊场景下使用。SELF JOIN:自连接,适合层级结构。ON是连接条件,WHERE是结果过滤——OUTER JOIN时位置不同会导致语义差异。
