首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >MySQL多表查询详解:内连接、外连接、自连接通通搞懂【MySQL系列】

MySQL多表查询详解:内连接、外连接、自连接通通搞懂【MySQL系列】

作者头像
大熊计算机
发布2025-07-14 20:08:00
发布2025-07-14 20:08:00
58900
代码可运行
举报
文章被收录于专栏:C博文C博文
运行总次数:0
代码可运行

本文将全面深入讲解 MySQL 多表查询的核心内容,包括 INNER JOINLEFT JOINRIGHT JOINFULL OUTER JOIN(模拟实现)、自连接、UNION 等,通过大量示例与实战分析,帮助你全面掌握表与表之间的联动查询技巧。

一、前置基础:为什么需要多表查询?

1.1 多表数据结构的必要性

在数据库设计中,为了避免数据冗余和提升可维护性,通常会采用 三范式设计。这会将重复出现的数据抽离成独立的表,比如:

  • 员工信息在 employees 表中;
  • 部门信息在 departments 表中;
  • 工资记录在 salaries 表中。

这就意味着我们在查询“员工所属部门名”、“员工工资情况”时必须进行多表关联

1.2 多表查询的场景
  • 获取员工及其所属部门名称
  • 查询订单及对应的客户信息
  • 汇总不同表中的业务指标
  • 构建分页列表时拉取扩展字段

二、INNER JOIN:内连接查询

2.1 基本语法
代码语言:javascript
代码运行次数:0
运行
复制
SELECT A.*, B.*
FROM tableA A
INNER JOIN tableB B
ON A.common_field = B.common_field;

关键点:

  • 只返回“两个表中都存在匹配关系”的记录。
2.2 示例:查询员工及其部门名称
代码语言:javascript
代码运行次数:0
运行
复制
SELECT e.employee_id, e.first_name, d.department_name
FROM employees e
INNER JOIN departments d
ON e.department_id = d.department_id;
2.3 多表内连接
代码语言:javascript
代码运行次数:0
运行
复制
SELECT e.employee_id, e.first_name, d.department_name, j.job_title
FROM employees e
JOIN departments d ON e.department_id = d.department_id
JOIN jobs j ON e.job_id = j.job_id;

三、LEFT JOIN:左外连接

3.1 基本语法
代码语言:javascript
代码运行次数:0
运行
复制
SELECT A.*, B.*
FROM tableA A
LEFT JOIN tableB B
ON A.common_field = B.common_field;

关键点:

  • 返回左表全部记录;
  • 若右表无匹配,则字段为 NULL
3.2 示例:查询所有员工及其部门(包括未分配的)
代码语言:javascript
代码运行次数:0
运行
复制
SELECT e.employee_id, e.first_name, d.department_name
FROM employees e
LEFT JOIN departments d
ON e.department_id = d.department_id;

四、RIGHT JOIN:右外连接

4.1 基本语法
代码语言:javascript
代码运行次数:0
运行
复制
SELECT A.*, B.*
FROM tableA A
RIGHT JOIN tableB B
ON A.common_field = B.common_field;

关键点:

  • 返回右表全部记录;
  • 若左表无匹配,则字段为 NULL
4.2 示例:列出所有部门及对应员工(包括空部门)
代码语言:javascript
代码运行次数:0
运行
复制
SELECT e.employee_id, d.department_name
FROM employees e
RIGHT JOIN departments d
ON e.department_id = d.department_id;

五、FULL OUTER JOIN:全外连接(MySQL 模拟)

MySQL 原生不支持 FULL OUTER JOIN,但我们可以通过 UNION 手动模拟。

5.1 模拟实现
代码语言:javascript
代码运行次数:0
运行
复制
SELECT e.employee_id, d.department_name
FROM employees e
LEFT JOIN departments d ON e.department_id = d.department_id

UNION

SELECT e.employee_id, d.department_name
FROM employees e
RIGHT JOIN departments d ON e.department_id = d.department_id;
5.2 使用场景

适用于需要显示所有员工和所有部门,即便他们之间没有关系。


六、自连接(Self Join)

自连接是将同一张表看作两张表来使用,通常用于有层级结构的数据。

6.1 典型应用:员工与上级的关系
代码语言:javascript
代码运行次数:0
运行
复制
SELECT e.employee_id, e.first_name, m.first_name AS manager_name
FROM employees e
LEFT JOIN employees m
ON e.manager_id = m.employee_id;
  • e 表示普通员工;
  • m 表示其上级经理。
6.2 层级结构拓展:部门树结构查询
代码语言:javascript
代码运行次数:0
运行
复制
SELECT a.department_name AS child, b.department_name AS parent
FROM departments a
LEFT JOIN departments b
ON a.parent_id = b.department_id;

七、UNION 与 UNION ALL:多结果合并

当多个查询结果具有相同字段结构,可以用 UNIONUNION ALL 合并。

7.1 UNION
代码语言:javascript
代码运行次数:0
运行
复制
SELECT employee_id FROM employees WHERE department_id = 10
UNION
SELECT employee_id FROM employees WHERE department_id = 20;

特点:自动去重

7.2 UNION ALL
代码语言:javascript
代码运行次数:0
运行
复制
SELECT employee_id FROM employees WHERE department_id = 10
UNION ALL
SELECT employee_id FROM employees WHERE department_id = 20;

特点:不去重,性能更优


八、别名、过滤、排序与分页在多表查询中的运用

8.1 使用表别名提升可读性
代码语言:javascript
代码运行次数:0
运行
复制
SELECT e.first_name, d.department_name
FROM employees e
JOIN departments d ON e.department_id = d.department_id;
8.2 多表查询中的过滤(WHERE)
代码语言:javascript
代码运行次数:0
运行
复制
SELECT e.first_name, d.department_name
FROM employees e
JOIN departments d ON e.department_id = d.department_id
WHERE d.location_id = 1000;
8.3 排序与分页
代码语言:javascript
代码运行次数:0
运行
复制
SELECT e.employee_id, e.first_name, d.department_name
FROM employees e
JOIN departments d ON e.department_id = d.department_id
ORDER BY e.hire_date DESC
LIMIT 10 OFFSET 20;

九、实战案例:解决真实业务问题

9.1 案例1:列出每个部门的最新入职员工
代码语言:javascript
代码运行次数:0
运行
复制
SELECT e.*
FROM employees e
JOIN (
    SELECT department_id, MAX(hire_date) AS latest_hire
    FROM employees
    GROUP BY department_id
) t
ON e.department_id = t.department_id AND e.hire_date = t.latest_hire;
9.2 案例2:查询没有员工的部门(左连接 + NULL 过滤)
代码语言:javascript
代码运行次数:0
运行
复制
SELECT d.department_id, d.department_name
FROM departments d
LEFT JOIN employees e
ON d.department_id = e.department_id
WHERE e.employee_id IS NULL;
9.3 案例3:不同表中相同字段对比(UNION)
代码语言:javascript
代码运行次数:0
运行
复制
SELECT name, 'customer' AS source FROM customers
UNION
SELECT name, 'supplier' AS source FROM suppliers;

十、优化建议与注意事项

技巧

建议或说明

使用 EXPLAIN 分析查询计划

查看 JOIN 顺序、索引使用情况

建立适当索引

特别是在 JOIN 条件字段、过滤字段、排序字段上

尽量避免 JOIN N 多表

超过 5 表连接需评估性能与复杂度

使用 LIMIT 分页时避免全表扫描

可用延迟关联或游标分页方式加速

避免 SELECT *

精准列出所需字段,提高性能

使用 UNION ALL 替代 UNION

如果业务不要求去重,UNION ALL 性能更好


十一、总结:掌握 JOIN,关系型数据库的灵魂

  • INNER JOIN:找出两个表中匹配的部分;
  • LEFT JOIN / RIGHT JOIN:包含一边全部记录;
  • 自连接:处理树状结构或自身关系;
  • UNION / UNION ALL:合并多个结果集;
  • 多表查询是构建报表、后台管理、业务接口的基础能力;
  • 掌握它,意味着你可以从零构建数据查询引擎的核心逻辑!

本项目适用于后台管理系统、电商用户中心、SaaS 用户模块等场景,特别适合开发者进行实战演练与面试准备。


一、项目背景与需求概述

我们将构建一个基础版的用户管理系统,具备以下业务功能:

  • 用户注册与登录
  • 用户角色与权限分配
  • 日志记录与用户状态追踪
  • 多条件用户查询与分页

涉及的核心业务对象包括:用户、角色、权限、日志等。


二、数据库建模与表结构设计

2.1 实体关系图(ER图)简要说明
  • 一位用户可以拥有多个角色(多对多)
  • 一个角色可以拥有多个权限(多对多)
  • 用户与登录日志是一对多关系
2.2 用户表(users
代码语言:javascript
代码运行次数:0
运行
复制
CREATE TABLE users (
  id INT PRIMARY KEY AUTO_INCREMENT,
  username VARCHAR(50) NOT NULL UNIQUE,
  password VARCHAR(100) NOT NULL,
  email VARCHAR(100),
  status TINYINT DEFAULT 1 COMMENT '0:禁用, 1:启用',
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
2.3 角色表(roles
代码语言:javascript
代码运行次数:0
运行
复制
CREATE TABLE roles (
  id INT PRIMARY KEY AUTO_INCREMENT,
  name VARCHAR(50) NOT NULL UNIQUE,
  description VARCHAR(255)
);
2.4 权限表(permissions
代码语言:javascript
代码运行次数:0
运行
复制
CREATE TABLE permissions (
  id INT PRIMARY KEY AUTO_INCREMENT,
  name VARCHAR(50) NOT NULL UNIQUE,
  code VARCHAR(50) NOT NULL UNIQUE COMMENT '用于权限标识,如 user:view'
);
2.5 用户-角色关联表(user_role
代码语言:javascript
代码运行次数:0
运行
复制
CREATE TABLE user_role (
  user_id INT,
  role_id INT,
  PRIMARY KEY (user_id, role_id),
  FOREIGN KEY (user_id) REFERENCES users(id),
  FOREIGN KEY (role_id) REFERENCES roles(id)
);
2.6 角色-权限关联表(role_permission
代码语言:javascript
代码运行次数:0
运行
复制
CREATE TABLE role_permission (
  role_id INT,
  permission_id INT,
  PRIMARY KEY (role_id, permission_id),
  FOREIGN KEY (role_id) REFERENCES roles(id),
  FOREIGN KEY (permission_id) REFERENCES permissions(id)
);
2.7 登录日志表(login_logs
代码语言:javascript
代码运行次数:0
运行
复制
CREATE TABLE login_logs (
  id INT PRIMARY KEY AUTO_INCREMENT,
  user_id INT,
  ip_address VARCHAR(45),
  login_time DATETIME DEFAULT CURRENT_TIMESTAMP,
  FOREIGN KEY (user_id) REFERENCES users(id)
);

三、数据初始化脚本

3.1 插入初始角色与权限
代码语言:javascript
代码运行次数:0
运行
复制
INSERT INTO roles(name, description) VALUES ('admin', '系统管理员'), ('user', '普通用户');

INSERT INTO permissions(name, code) VALUES
('查看用户', 'user:view'),
('新增用户', 'user:create'),
('删除用户', 'user:delete');

-- 分配权限给角色
INSERT INTO role_permission(role_id, permission_id) VALUES
(1, 1), (1, 2), (1, 3), -- admin 拥有全部权限
(2, 1);                 -- user 仅能查看用户
3.2 插入测试用户
代码语言:javascript
代码运行次数:0
运行
复制
INSERT INTO users(username, password, email) VALUES
('alice', 'hashed_pwd1', 'alice@example.com'),
('bob', 'hashed_pwd2', 'bob@example.com');

-- 分配角色
INSERT INTO user_role(user_id, role_id) VALUES
(1, 1), -- alice 为管理员
(2, 2); -- bob 为普通用户

四、典型查询场景实现

4.1 查询所有启用用户及其角色
代码语言:javascript
代码运行次数:0
运行
复制
SELECT u.id, u.username, r.name AS role
FROM users u
JOIN user_role ur ON u.id = ur.user_id
JOIN roles r ON ur.role_id = r.id
WHERE u.status = 1;
4.2 查询某用户拥有的所有权限
代码语言:javascript
代码运行次数:0
运行
复制
SELECT p.name, p.code
FROM users u
JOIN user_role ur ON u.id = ur.user_id
JOIN role_permission rp ON ur.role_id = rp.role_id
JOIN permissions p ON rp.permission_id = p.id
WHERE u.username = 'alice';
4.3 查询最近7天登录日志
代码语言:javascript
代码运行次数:0
运行
复制
SELECT u.username, l.ip_address, l.login_time
FROM login_logs l
JOIN users u ON l.user_id = u.id
WHERE l.login_time >= NOW() - INTERVAL 7 DAY
ORDER BY l.login_time DESC;
4.4 用户分页查询(带关键字搜索)
代码语言:javascript
代码运行次数:0
运行
复制
SELECT *
FROM users
WHERE username LIKE '%bob%'
ORDER BY created_at DESC
LIMIT 0, 10;

五、事务控制与一致性保障

在角色授权或用户注册等业务流程中,可以使用事务来确保数据完整性。

5.1 注册用户 + 分配默认角色(事务)
代码语言:javascript
代码运行次数:0
运行
复制
START TRANSACTION;

INSERT INTO users(username, password, email) VALUES('charlie', 'hashed_pwd3', 'charlie@example.com');
SET @uid = LAST_INSERT_ID();
INSERT INTO user_role(user_id, role_id) VALUES(@uid, 2); -- 默认赋普通角色

COMMIT;
5.2 授权失败时回滚
代码语言:javascript
代码运行次数:0
运行
复制
START TRANSACTION;

-- 假设某权限不存在导致失败
INSERT INTO role_permission(role_id, permission_id) VALUES(1, 999);

-- 失败时回滚
ROLLBACK;

六、索引优化与执行分析

6.1 建议加索引字段
  • users.username:用于登录验证、搜索
  • login_logs.user_id:日志查询
  • user_role.user_id / role_permission.role_id:JOIN 优化
代码语言:javascript
代码运行次数:0
运行
复制
CREATE INDEX idx_username ON users(username);
CREATE INDEX idx_user_log ON login_logs(user_id);
6.2 执行计划分析
代码语言:javascript
代码运行次数:0
运行
复制
EXPLAIN SELECT u.username, r.name FROM users u JOIN user_role ur ON u.id = ur.user_id JOIN roles r ON ur.role_id = r.id;

可查看索引是否使用、JOIN 类型、Rows 扫描数量等。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-05-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、前置基础:为什么需要多表查询?
    • 1.1 多表数据结构的必要性
    • 1.2 多表查询的场景
  • 二、INNER JOIN:内连接查询
    • 2.1 基本语法
    • 2.2 示例:查询员工及其部门名称
    • 2.3 多表内连接
  • 三、LEFT JOIN:左外连接
    • 3.1 基本语法
    • 3.2 示例:查询所有员工及其部门(包括未分配的)
  • 四、RIGHT JOIN:右外连接
    • 4.1 基本语法
    • 4.2 示例:列出所有部门及对应员工(包括空部门)
  • 五、FULL OUTER JOIN:全外连接(MySQL 模拟)
    • 5.1 模拟实现
    • 5.2 使用场景
  • 六、自连接(Self Join)
    • 6.1 典型应用:员工与上级的关系
    • 6.2 层级结构拓展:部门树结构查询
  • 七、UNION 与 UNION ALL:多结果合并
    • 7.1 UNION
    • 7.2 UNION ALL
  • 八、别名、过滤、排序与分页在多表查询中的运用
    • 8.1 使用表别名提升可读性
    • 8.2 多表查询中的过滤(WHERE)
    • 8.3 排序与分页
  • 九、实战案例:解决真实业务问题
    • 9.1 案例1:列出每个部门的最新入职员工
    • 9.2 案例2:查询没有员工的部门(左连接 + NULL 过滤)
    • 9.3 案例3:不同表中相同字段对比(UNION)
  • 十、优化建议与注意事项
  • 十一、总结:掌握 JOIN,关系型数据库的灵魂
  • 一、项目背景与需求概述
  • 二、数据库建模与表结构设计
    • 2.1 实体关系图(ER图)简要说明
    • 2.2 用户表(users)
    • 2.3 角色表(roles)
    • 2.4 权限表(permissions)
    • 2.5 用户-角色关联表(user_role)
    • 2.6 角色-权限关联表(role_permission)
    • 2.7 登录日志表(login_logs)
  • 三、数据初始化脚本
    • 3.1 插入初始角色与权限
    • 3.2 插入测试用户
  • 四、典型查询场景实现
    • 4.1 查询所有启用用户及其角色
    • 4.2 查询某用户拥有的所有权限
    • 4.3 查询最近7天登录日志
    • 4.4 用户分页查询(带关键字搜索)
  • 五、事务控制与一致性保障
    • 5.1 注册用户 + 分配默认角色(事务)
    • 5.2 授权失败时回滚
  • 六、索引优化与执行分析
    • 6.1 建议加索引字段
    • 6.2 执行计划分析
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档