Mysql

关注公众号 jb51net

关闭
首页 > 数据库 > Mysql > mysql多表联合查询

MySQL 多表联合查询与数据备份恢复全攻略

作者:Sadsvit

本文系统讲解MySQL多表联合查询的五种核心方式(交叉连接、内连接、外连接、分组查询、子查询),并涵盖数据备份与恢复的完整流程,结合实战案例与效果验证,保障数据安全与高效查询,感兴趣的朋友跟随小编一起看看吧

MySQL 多表联合查询与数据备份恢复全指南

在关系型数据库中,表与表通过外键等关联字段建立联系,实际业务场景常需同时查询多个表的关联数据,这就需要多表联合查询。此外,数据安全是数据库管理的核心,定期备份与高效恢复是保障数据不丢失的关键。本文将系统讲解 MySQL 多表联合查询的5种核心方式(交叉连接、内连接、外连接、分组查询、子查询),并详细说明数据库备份与恢复的完整流程,包含实战案例与效果验证。

一、多表联合查询基础

1. 什么是多表联合查询

前面讲解的查询语句均针对单个表,但关系型数据库中表与表存在业务关联(如“学生表”与“课程表”通过“课程ID”关联),多表联合查询即同时查询两个或两个以上的表,通过关联条件提取整合数据,满足复杂业务需求(如“查询学生姓名及对应课程名称”)。

在 MySQL 中,多表查询主要分为 交叉连接、内连接、外连接、分组查询、子查询 5种,核心是通过“关联条件”消除无效数据,确保查询结果的准确性与实用性。

二、多表联合查询的5种方式

1. 交叉连接(CROSS JOIN)——笛卡尔积查询

交叉连接是最基础的多表查询方式,分为显式交叉连接隐式交叉连接,核心是返回两张表的“笛卡尔积”,但实际应用中需谨慎使用(易产生大量无效数据)。

(1)核心概念:笛卡尔积

笛卡尔积(Cartesian product)是数学中两个集合的乘积,表与表的交叉连接本质就是计算笛卡尔积:

注意:笛卡尔积不满足交换律(A×B ≠ B×A),且包含大量无业务意义的数据(如“学生A对应所有课程”),需通过 WHERE 子句筛选有效数据。

(2)语法格式
-- 显式交叉连接(官方推荐)
SELECT <字段名> FROM <表1> CROSS JOIN <表2> [WHERE 子句];
-- 隐式交叉连接(逗号分隔表名)
SELECT <字段名> FROM <表1>, <表2> [WHERE 子句];
(3)实战案例

以“学生信息表(tb_students_info)”和“课程表(tb_course)”为例,演示交叉连接的使用:

步骤1:创建并查看两张表的原始数据:

mysql> create database xuexiao;
Query OK, 1 row affected (0.00 sec)
mysql> use xuexiao;
Database changed
mysql> show tables;
Empty set (0.00 sec)
mysql> CREATE TABLE IF NOT EXISTS tb_course (
    -> id INT PRIMARY KEY AUTO_INCREMENT,
    -> course_name VARCHAR(50) NOT NULL
    -> );
Query OK, 0 rows affected (0.00 sec)
mysql> CREATE TABLE IF NOT EXISTS tb_students_info(
    -> id INT PRIMARY KEY AUTO_INCREMENT,
    -> name VARCHAR(50)  NOT NULL,
    -> age INT,
    -> sex VARCHAR(10),
    -> height INT,
    -> course_id INT,
    -> FOREIGN KEY (course_id) REFERENCES tb_course(id)
    -> );
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO tb_course (course_name) VALUES 
    -> ('Java'),('MySQL'),('Python'),('Go'),('C++');
Query OK, 5 rows affected (0.00 sec)
Records: 5  Duplicates: 0  Warnings: 0
mysql> ALTER TABLE tb_students_info 
    -> MODIFY COLUMN sex VARCHAR(10) 
    -> CHARACTER SET utf8mb4 
    -> COLLATE utf8mb4_unicode_ci;
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0
mysql> INSERT INTO tb_students_info (name, age, sex, height, course_id) VALUES
    -> ('Dany', 25, '男', 160, 1),
    -> ('Green', 23, '男', 158, 2),
    -> ('Henry', 23, '女', 185, 1),
    -> ('Jane', 22, '男', 162, 3),
    -> ('Jim', 24, '女', 175, 2),
    -> ('John', 21, '女', 172, 4),
    -> ('Lily', 22, '男', 165, 4),
    -> ('Susan', 23, '男', 170, 5),
    -> ('Thomas', 22, '女', 178, 5),
    -> ('Tom', 23, '女', 165, 5);
Query OK, 10 rows affected (0.00 sec)
Records: 10  Duplicates: 0  Warnings: 0
mysql> ALTER TABLE tb_students_info 
    -> MODIFY COLUMN sex VARCHAR(10) 
    -> CHARACTER SET utf8mb4 
    -> COLLATE utf8mb4_unicode_ci;
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0
-- 查看学生信息表
mysql> SELECT * FROM tb_students_info;
+----+--------+------+------+--------+-----------+
| id | name   | age  | sex  | height | course_id |  -- course_id:关联课程表的外键
+----+--------+------+------+--------+-----------+
|  1 | Dany   |   25 | 男   |    160 |         1 |
|  2 | Green  |   23 | 男   |    158 |         2 |
|  3 | Henry  |   23 | 女   |    185 |         1 |
|  4 | Jane   |   22 | 男   |    162 |         3 |
|  5 | Jim    |   24 | 女   |    175 |         2 |
|  6 | John   |   21 | 女   |    172 |         4 |
|  7 | Lily   |   22 | 男   |    165 |         4 |
|  8 | Susan  |   23 | 男   |    170 |         5 |
|  9 | Thomas |   22 | 女   |    178 |         5 |
| 10 | Tom    |   23 | 女   |    165 |         5 |
+----+--------+------+------+--------+-----------+
10 rows in set (0.00 sec)

-- 查看课程表
mysql> SELECT * FROM tb_course;
+----+-------------+
| id | course_name |  -- id:主键,与学生表的 course_id 关联
+----+-------------+
|  1 | Java        |
|  2 | MySQL       |
|  3 | Python      |
|  4 | Go          |
|  5 | C++         |
+----+-------------+
5 rows in set (0.00 sec)

步骤2:查询完整笛卡尔积(无 WHERE 子句)

mysql> SELECT * FROM tb_course CROSS JOIN tb_students_info;
+----+-------------+----+--------+------+------+--------+-----------+
| id | course_name | id | name   | age  | sex  | height | course_id |
+----+-------------+----+--------+------+------+--------+-----------+
|  1 | Java        |  1 | Dany   |   25 | 男   |    160 |         1 |
|  2 | MySQL       |  1 | Dany   |   25 | 男   |    160 |         1 |
|  3 | Python      |  1 | Dany   |   25 | 男   |    160 |         1 |
|  4 | Go          |  1 | Dany   |   25 | 男   |    160 |         1 |
|  5 | C++         |  1 | Dany   |   25 | 男   |    160 |         1 |
                      *****省略中间 40 行******
|  5 | C++         | 10 | Tom    |   23 | 女   |    165 |         5 |
+----+-------------+----+--------+------+------+--------+-----------+
50 rows in set (0.00 sec)

结果分析:返回 10×5=50 行数据,包含大量无效关联(如“Dany 对应所有课程”),实际业务中几乎不用此方式。

步骤3:带 WHERE 子句的交叉连接(筛选有效数据)

通过 WHERE 子句匹配“课程表 id”与“学生表 course_id”,消除无效数据:

mysql> SELECT * FROM tb_course CROSS JOIN tb_students_info 
    -> WHERE tb_students_info.course_id = tb_course.id;
+----+-------------+----+--------+------+------+--------+-----------+
| id | course_name | id | name   | age  | sex  | height | course_id |
+----+-------------+----+--------+------+------+--------+-----------+
|  1 | Java        |  1 | Dany   |   25 | 男   |    160 |         1 |
|  1 | Java        |  3 | Henry  |   23 | 女   |    185 |         1 |
|  2 | MySQL       |  2 | Green  |   23 | 男   |    158 |         2 |
|  2 | MySQL       |  5 | Jim    |   24 | 女   |    175 |         2 |
|  3 | Python      |  4 | Jane   |   22 | 男   |    162 |         3 |
|  4 | Go          |  6 | John   |   21 | 女   |    172 |         4 |
|  4 | Go          |  7 | Lily   |   22 | 男   |    165 |         4 |
|  5 | C++         |  8 | Susan  |   23 | 男   |    170 |         5 |
|  5 | C++         |  9 | Thomas |   22 | 女   |    178 |         5 |
|  5 | C++         | 10 | Tom    |   23 | 女   |    165 |         5 |
+----+-------------+----+--------+------+------+--------+-----------+
10 rows in set (0.00 sec)

结果分析:仅返回 10 行有效数据,与学生表行数一致,实现“学生-课程”的正确关联。

(4)注意事项

2. 内连接(INNER JOIN)——筛选匹配数据

内连接是最常用的多表查询方式,通过 INNER JOIN 关键字和 ON 子句设置关联条件,仅返回两张表中满足关联条件的记录,自动消除无效数据,无需手动筛选笛卡尔积。

(1)核心逻辑

内连接的本质是“交集查询”:仅保留表1和表2中“关联字段值相等”的记录,不满足条件的记录(如“无对应课程的学生”“无学生的课程”)会被过滤。

(2)语法格式
SELECT <字段名> FROM <表1> INNER JOIN <表2> ON <关联条件>;
-- 简化写法:INNER 可省略,直接用 JOIN
SELECT <字段名> FROM <表1> JOIN <表2> ON <关联条件>;
(3)实战案例:查询学生姓名及对应课程名称

需求:从 tb_students_infotb_course 中,查询学生姓名(name)及对应课程名称(course_name),关联条件为“学生表 course_id = 课程表 id”。

-- 给表设置别名(s 代表学生表,c 代表课程表),简化语法
mysql> SELECT s.name, c.course_name 
    -> FROM tb_students_info s 
    -> INNER JOIN tb_course c 
    -> ON s.course_id = c.id;
+--------+-------------+
| name   | course_name |
+--------+-------------+
| Dany   | Java        |
| Henry  | Java        |
| Green  | MySQL       |
| Jim    | MySQL       |
| Jane   | Python      |
| John   | Go          |
| Lily   | Go          |
| Susan  | C++         |
| Thomas | C++         |
| Tom    | C++         |
+--------+-------------+
10 rows in set (0.00 sec)
(4)关键说明

3. 外连接(LEFT/RIGHT JOIN)——保留部分表的全部数据

内连接仅返回“双方匹配”的记录,而外连接会以一张表为“基表”,保留基表的全部记录,另一张表(参考表)匹配不到的记录用 NULL 填充,适用于“需保留所有基表数据”的场景(如“查询所有学生,包括无课程的学生”)。

外连接分为 左外连接(LEFT JOIN)右外连接(RIGHT JOIN),核心区别是“基表的选择”。

(1)左外连接(LEFT OUTER JOIN)

实战案例:查询所有学生及对应课程(含无课程的学生)

步骤1:先添加“无课程的学生”(course_id=7,课程表无 id=7 的课程):

-- 临时禁用外键约束
ysql> SET FOREIGN_KEY_CHECKS=0;  
Query OK, 0 rows affected (0.01 sec)
mysql> INSERT INTO tb_students_info (name, age, sex, height, course_id) 
    -> VALUES ('LiMing', 22, '男', 180, 7);
Query OK, 1 row affected (0.01 sec)
-- 恢复外键约束
mysql> SET FOREIGN_KEY_CHECKS=1;
Query OK, 0 rows affected (0.00 sec)

步骤2:执行左外连接查询:

mysql> SELECT s.name, c.course_name 
    -> FROM tb_students_info s
    -> LEFT JOIN tb_course c
    -> ON s.course_id = c.id;
+--------+-------------+
| name   | course_name |
+--------+-------------+
| Dany   | Java        |
| Henry  | Java        |
| Green  | MySQL       |
| Jim    | MySQL       |
| Jane   | Python      |
| John   | Go          |
| Lily   | Go          |
| Susan  | C++         |
| Thomas | C++         |
| Tom    | C++         |
| LiMing | C++         |
| LiMing | NULL        |  # 无对应课程,course_name 为null
+--------+-------------+
12 rows in set (0.00 sec)

结果分析:基表(学生表)的 11 条记录全部保留,LiMing 因无对应课程,课程名称显示 NULL

(2)右外连接(RIGHT OUTER JOIN)

实战案例:查询所有课程及对应学生(含无学生的课程)

步骤1:先添加“无学生的课程”(id=6,学生表无 course_id=6 的记录):

mysql> INSERT INTO tb_course (id, course_name) 
    -> VALUES (6, 'HTML');
Query OK, 1 row affected (0.00 sec)

步骤2:执行右外连接查询:

mysql> SELECT s.name, c.course_name 
    -> FROM tb_students_info s 
    -> RIGHT JOIN tb_course c 
    -> ON s.course_id = c.id;
+--------+-------------+
| name   | course_name |
+--------+-------------+
| Dany   | Java        |
| Henry  | Java        |
| Green  | MySQL       |
| Jim    | MySQL       |
| Jane   | Python      |
| John   | Go          |
| Lily   | Go          |
| Susan  | C++         |
| Thomas | C++         |
| Tom    | C++         |
| LiMing | C++         |
| NULL   | HTML        |
+--------+-------------+
12 rows in set (0.01 sec)

结果分析:基表(课程表)的 6 条记录全部保留,HTML 课程因无学生,学生姓名显示 NULL

(3)外连接的关键区别
连接类型基表保留数据参考表匹配不到时适用场景
左外连接LEFT 左侧表左侧表全部记录右侧表字段为 NULL保留左侧表所有数据
右外连接RIGHT 右侧表右侧表全部记录左侧表字段为 NULL保留右侧表所有数据

4. 分组查询(GROUP BY)——按字段分组统计

分组查询通过 GROUP BY 关键字,按一个或多个字段对查询结果分组,常与 聚合函数(如 COUNT()SUM())结合,实现数据统计(如“按性别统计学生人数”)。

(1)核心语法
SELECT <分组字段>, <聚合函数> 
FROM <表名> 
[JOIN <关联表> ON <条件>] 
[WHERE <筛选条件>] 
GROUP BY <分组字段> [WITH ROLLUP];
(2)实战案例

案例1:按性别分组,统计学生人数

mysql> SELECT sex, COUNT(*) AS student_count 
    -> FROM tb_students_info 
    -> GROUP BY sex;
+------+---------------+
| sex  | student_count |
+------+---------------+
| 女   |             5 |
| 男   |             6 |
+------+---------------+
2 rows in set (0.00 sec)

案例2:按性别分组,显示每组学生姓名(GROUP_CONCAT())

GROUP_CONCAT() 函数可将分组内的指定字段值拼接为字符串:

mysql> SELECT sex, GROUP_CONCAT(name) AS student_names 
    -> FROM tb_students_info 
    -> GROUP BY sex;
+------+----------------------------------------+
| sex  | student_names                           |
+------+----------------------------------------+
| 女   | Henry,Jim,John,Thomas,Tom               |
| 男   | Dany,Green,Jane,Lily,Susan,LiMing       |
+------+----------------------------------------+
2 rows in set (0.00 sec)

案例3:按“年龄+性别”多字段分组

多字段分组时,先按第一个字段分组,第一个字段值相同时再按第二个字段分组:

mysql> SELECT age, sex, GROUP_CONCAT(name) AS student_names 
    -> FROM tb_students_info 
    -> GROUP BY age, sex;
+------+------+------------------+
| age  | sex  | student_names    |
+------+------+------------------+
|   21 | 女   | John             |
|   22 | 女   | Thomas           |
|   22 | 男   | Jane,Lily,LiMing |
|   23 | 女   | Henry,Tom        |
|   23 | 男   | Green,Susan      |
|   24 | 女   | Jim              |
|   25 | 男   | Dany             |
+------+------+------------------+
7 rows in set (0.00 sec)

案例4:分组后添加总计(WITH ROLLUP)

mysql> SELECT sex, COUNT(*) AS student_count 
    -> FROM tb_students_info 
    -> GROUP BY sex WITH ROLLUP;
+------+---------------+
| sex  | student_count |
+------+---------------+
| 女   |             5 |
| 男   |             6 |
| NULL |            11 |  
+------+---------------+
3 rows in set (0.00 sec)

5. 子查询——嵌套查询实现复杂逻辑

子查询(嵌套查询)是将一个查询语句(子查询)嵌套在另一个查询语句(父查询)中,子查询的结果作为父查询的条件或数据源,适用于“需先获取中间结果”的复杂场景(如“查询学习 Java 课程的学生姓名”)。

(1)核心语法

子查询常位于 WHERE 子句中,格式如下:

SELECT <父查询字段> 
FROM <父查询表> 
WHERE <表达式> <操作符> (子查询);
(2)实战案例

案例1:查询学习 Java 课程的学生姓名(IN 关键字)

需求:先查询“Java 课程的 id”,再根据 id 查询对应学生姓名。

-- 子查询:获取 Java 课程的 id
mysql> SELECT id FROM tb_course WHERE course_name = 'Java';
+----+
| id |
+----+
|  1 |
+----+
1 row in set (0.00 sec)

-- 父查询:根据 id=1 查询学生姓名
mysql> SELECT name 
    -> FROM tb_students_info 
    -> WHERE course_id IN (SELECT id FROM tb_course WHERE course_name = 'Java');
+-------+
| name  |
+-------+
| Dany  |
| Henry |
+-------+
2 rows in set (0.01 sec)

案例2:查询没有学习 Java 课程的学生姓名(NOT IN 关键字)

mysql> SELECT name 
    -> FROM tb_students_info 
    -> WHERE course_id NOT IN (SELECT id FROM tb_course WHERE course_name = 'Java');
+--------+
| name   |
+--------+
| Green  |
| Jane   |
| Jim    |
| John   |
| Lily   |
| Susan  |
| Thomas |
| Tom    |
| LiMing |
+--------+
9 rows in set (0.00 sec)

案例3:判断课程是否存在,再查询学生(EXISTS 关键字)

EXISTS 仅判断子查询结果集是否为空(不为空则返回 TRUE,为空则返回 FALSE),不关心具体值:

-- 若存在 id=1 的课程(Java),则查询所有学生
mysql> SELECT * 
    -> FROM tb_students_info 
    -> WHERE EXISTS (SELECT course_name FROM tb_course WHERE id=1);
+----+--------+------+------+--------+-----------+
| id | name   | age  | sex  | height | course_id |
+----+--------+------+------+--------+-----------+
|  1 | Dany   |   25 | 男   |    160 |         1 |
|  2 | Green  |   23 | 男   |    158 |         2 |
|  3 | Henry  |   23 | 女   |    185 |         1 |
|  4 | Jane   |   22 | 男   |    162 |         3 |
|  5 | Jim    |   24 | 女   |    175 |         2 |
|  6 | John   |   21 | 女   |    172 |         4 |
|  7 | Lily   |   22 | 男   |    165 |         4 |
|  8 | Susan  |   23 | 男   |    170 |         5 |
|  9 | Thomas |   22 | 女   |    178 |         5 |
| 10 | Tom    |   23 | 女   |    165 |         5 |
| 15 | LiMing |   22 | 男   |    180 |         7 |
+----+--------+------+------+--------+-----------+
11 rows in set (0.00 sec)

案例4:多条件结合 EXISTS(查询年龄>24 的学生)

mysql> SELECT * 
    -> FROM tb_students_info 
    -> WHERE age>24 AND EXISTS (SELECT course_name FROM tb_course WHERE id=1);
+----+------+------+------+--------+-----------+
| id | name | age  | sex  | height | course_id |
+----+------+------+------+--------+-----------+
|  1 | Dany |   25 | 男   |    160 |         1 |
+----+------+------+------+--------+-----------+
1 row in set (0.01 sec)
(3)子查询 vs 表连接

到此这篇关于MySQL 多表联合查询与数据备份恢复全攻略的文章就介绍到这了,更多相关mysql多表联合查询内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文