首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >MySQL 多表查询

MySQL 多表查询

作者头像
IvanCodes
发布2025-09-28 11:25:38
发布2025-09-28 11:25:38
1100
代码可运行
举报
运行总次数:0
代码可运行

核心目标: 学习如何使用 JOINUNION、自连接和子查询等技术,从一个或多个表中检索和组合复杂的数据。

前提:

  1. 熟悉单表 SELECT 查询 (SELECT, FROM, WHERE, ORDER BY, LIMIT 等)。
  2. 理解表间关联(主键-外键)。
1. 表连接 (JOIN)

作用:将来自两个或多个表中的行,基于它们之间的相关列组合起来。

(1.1) 内连接 (INNER JOINJOIN)

作用:返回两个(或多个)表中 满足连接条件 的行。不匹配的行被排除。最常用。 语法:

代码语言:javascript
代码运行次数:0
运行
复制
select column_list
from table1
inner join table2 on join_condition;

示例:

代码语言:javascript
代码运行次数:0
运行
复制
-- 查询学生及其专业名称 (只显示有匹配专业的学生)
select s.student_name, m.major_name
from students s
inner join majors m on s.major_id = m.major_id;
(1.2) 外连接 (OUTER JOIN)
  • 左连接 (LEFT JOINLEFT OUTER JOIN) 作用:返回 左表的所有行,以及右表中匹配的行。右表无匹配时,其列值为 NULL。 语法:
代码语言:javascript
代码运行次数:0
运行
复制
select column_list
from table1 -- 左表
left join table2 on join_condition;

示例:

代码语言:javascript
代码运行次数:0
运行
复制
-- 查询所有学生及其专业 (没有专业的学生也会显示)
select s.student_name, m.major_name
from students s
left join majors m on s.major_id = m.major_id;
  • 右连接 (RIGHT JOINRIGHT OUTER JOIN) 作用:返回 右表的所有行,以及左表中匹配的行。左表无匹配时,其列值为 NULL。 语法:
代码语言:javascript
代码运行次数:0
运行
复制
select column_list
from table1
right join table2 on join_condition; -- 右表

示例:

代码语言:javascript
代码运行次数:0
运行
复制
-- 查询所有专业及其学生 (没有学生的专业也会显示)
select s.student_name, m.major_name
from students s
right join majors m on s.major_id = m.major_id;
  • (了解) 全外连接 (FULL OUTER JOIN) 作用:返回左表和右表中 所有 的行。无匹配的对应列为 NULL。 语法:MySQL 不直接支持。通过 LEFT JOIN UNION RIGHT JOIN 模拟。 模拟语法:
代码语言:javascript
代码运行次数:0
运行
复制
select column_list from table1 left join table2 on join_condition
union
select column_list from table1 right join table2 on join_condition;
(1.3) (了解) 交叉连接 (CROSS JOIN)

作用:返回两表的笛卡尔积(所有可能的行组合)。谨慎使用。 语法:

代码语言:javascript
代码运行次数:0
运行
复制
select column_list from table1 cross join table2;
(1.4) 使用 USING 子句

当连接的两个表中用于匹配的列名称完全相同时,可用 USING(common_column_name) 简化 ON。 语法:

代码语言:javascript
代码运行次数:0
运行
复制
select ... from table1 join table2 using(common_column_name);
2. 自连接 (Self Join)

作用:将一个表连接到它自身,用于查询表内行与行之间的关系(如员工与经理)。必须使用表别名。 语法:

代码语言:javascript
代码运行次数:0
运行
复制
select alias1.column, alias2.column
from table_name alias1
join table_name alias2 on alias1.related_column = alias2.related_column;

示例 (查询员工及其经理):

代码语言:javascript
代码运行次数:0
运行
复制
select
e.emp_name as employee_name,
m.emp_name as manager_name
from
employees e
left join
employees m on e.manager_id = m.emp_id;
3. 联合查询 (UNION / UNION ALL)

作用:合并两个或多个 SELECT 语句的结果集。 语法:

代码语言:javascript
代码运行次数:0
运行
复制
select column_list1 from table1 ...
union | union all
select column_list2 from table2 ...
[order by ...]
[limit ...]

规则:

  • 所有 SELECT 语句列数相同。
  • 对应列数据类型兼容。
  • 列顺序相同。
  • UNION 去重,UNION ALL 不去重。

示例:

代码语言:javascript
代码运行次数:0
运行
复制
-- 合并活跃客户和潜在客户邮箱 (去重)
select email from active_customers
union
select email from potential_customers;

-- 合并不同年份销售记录 (不去重)
select product_id, sale_date, amount from sales_2023
union all
select product_id, sale_date, amount from sales_2024;
4. 子查询 (Subqueries)

作用:嵌套在其他 SQL 语句中的查询。根据返回结果的结构,可分为以下几类:

(4.1) 标量子查询 (Scalar Subquery)

定义:返回 单个值(即一行一列)的子查询。 用途:可以用在任何需要单个值的地方,如 SELECT 列表、WHERE 子句的比较条件、SET 子句等。

示例:

代码语言:javascript
代码运行次数:0
运行
复制
-- WHERE 子句:查询工资高于平均工资的员工
select emp_name, salary
from employees
where salary > (select avg(salary) from employees); -- 子查询返回平均工资(单个值)

-- SELECT 列表:查询每个学生姓名及其选修的课程总数
select
s.student_name,
(select count(*) from enrollments e where e.student_id = s.student_id) as courses_taken -- 子查询计算每个学生的选课数(单个值)
from students s;

-- SET 子句 (在 UPDATE 中): 将所有员工工资设为平均工资 (危险操作示例)
-- update employees set salary = (select avg(salary) from employees);
(4.2) 列子查询 (Column Subquery)

定义:返回 单列多行 的子查询。 用途:主要用于 WHERE 子句中与 IN, NOT IN, ANY, SOME, ALL 等操作符结合使用。

示例:

代码语言:javascript
代码运行次数:0
运行
复制
-- 使用 IN:查询选修了 'CS101' 或 'MA201' 课程的学生姓名
select student_name
from students
where student_id in (select student_id from enrollments where course_id in ('CS101', 'MA201')); -- 子查询返回选了这两门课的学生 ID 列表

-- 使用 NOT IN:查询没有下过订单的客户
select customer_name
from customers
where customer_id not in (select distinct customer_id from orders); -- 子查询返回下过单的客户 ID 列表

-- 使用 ANY/SOME:查询工资高于 IT 部门(dept_id=2)任意一个员工工资的其他部门员工
-- (注意: 不包括 IT 部门自身员工)
select emp_name, salary
from employees
where salary > any (select salary from employees where dept_id = 2)
and dept_id != 2; -- 或 dept_id is null

-- 使用 ALL:查询工资高于 IT 部门所有员工工资的员工
select emp_name, salary
from employees
where salary > all (select salary from employees where dept_id = 2);
(4.3) 行子查询 (Row Subquery)

定义:返回 单行多列 的子查询。 用途:主要用于 WHERE 子句中进行行级别的比较。需要使用括号将多个列括起来进行比较。

示例:

代码语言:javascript
代码运行次数:0
运行
复制
-- 查询与 ID 为 101 的员工 部门和经理都相同的其他员工
select emp_name
from employees
where (dept_id, manager_id) = (select dept_id, manager_id from employees where emp_id = 101)
and emp_id != 101; -- 排除员工 101 自己

-- 查找选修了与学生 'Alice' 完全相同的一组课程的学生 (这比较复杂,通常用 JOIN 或 EXISTS 更优,仅作行子查询示例)
-- (假设能查询出 Alice 选的所有课作为一个 "行" - 实际场景复杂)
-- select ... where (col1, col2, ...) = (select col1, col2, ... from ... where ...)

注意:行子查询的直接比较在某些场景下可能效率不高或不易理解,常有替代方案。

(4.4) 表子查询 (Table Subquery / Derived Table)

定义:返回 多行多列(即一个结果集)的子查询。 用途:主要用在 FROM 子句中,将子查询的结果当作一个临时的表(必须为其指定别名)来进一步查询。也可用在 JOIN 子句中。

示例:

代码语言:javascript
代码运行次数:0
运行
复制
-- FROM 子句:查询每个专业的平均 GPA,然后找出平均 GPA 大于 3.5 的专业
select major_avg.major_name, major_avg.avg_gpa
from (
select m.major_name, avg(s.gpa) as avg_gpa -- 子查询计算每个专业的平均 GPA
from students s
join majors m on s.major_id = m.major_id
group by m.major_name
) as major_avg -- 必须为派生表指定别名
where major_avg.avg_gpa > 3.5;

-- JOIN 子句:查询每个部门中工资最高的员工信息
select e.emp_name, e.salary, d.dept_name
from employees e
inner join (
select dept_id, max(salary) as max_salary -- 子查询找出每个部门的最高工资
from employees
where dept_id is not null
group by dept_id
) as dept_max_salary
on e.dept_id = dept_max_salary.dept_id and e.salary = dept_max_salary.max_salary -- 连接条件:部门ID相同且工资等于该部门最高工资
inner join departments d on e.dept_id = d.dept_id;
(4.5) EXISTS / NOT EXISTS 结合子查询

EXISTS 操作符用于检查子查询是否返回任何行。如果子查询返回至少一行,EXISTS 返回 TRUE;否则返回 FALSE。NOT EXISTS 相反。这通常用于替代 IN 进行存在性检查,效率可能更高。子查询通常是相关子查询(依赖外部查询的值)。

示例:

代码语言:javascript
代码运行次数:0
运行
复制
-- EXISTS:查询至少选修了一门课程的学生
select s.student_name
from students s
where exists (select 1 from enrollments e where e.student_id = s.student_id limit 1); -- 检查是否存在该学生的选课记录

-- NOT EXISTS:查询没有员工的部门
select d.dept_name
from departments d
where not exists (select 1 from employees e where e.dept_id = d.dept_id limit 1); -- 检查是否不存在该部门的员工记录

练习题 (Practice Exercises - Advanced Queries with Answers)

继续使用 employeesdepartments 表。

employees 表:

emp_id

emp_name

dept_id

salary

manager_id

101

Alice

1

60000

102

102

Bob

2

75000

NULL

103

Charlie

1

65000

101

104

David

NULL

50000

102

105

Eve

2

80000

102

departments 表:

dept_id

dept_name

location

1

HR

New York

2

IT

London

3

Finance

New York

(Self Join) 查询所有员工及其直接经理的姓名。如果员工没有经理,也要显示该员工。 答案:

代码语言:javascript
代码运行次数:0
运行
复制
select e.emp_name as employee_name, m.emp_name as manager_name
from employees e
left join employees m on e.manager_id = m.emp_id;

(Column Subquery with IN) 查询所有在 ‘New York’ 地点工作的员工的姓名和工资。 答案:

代码语言:javascript
代码运行次数:0
运行
复制
select emp_name, salary
from employees
where dept_id in (select dept_id from departments where location = 'New York');

(Scalar Subquery) 查询工资最高的员工的姓名和工资。 答案:

代码语言:javascript
代码运行次数:0
运行
复制
select emp_name, salary
from employees
where salary = (select max(salary) from employees);

(Scalar Subquery in SELECT) 查询每个部门的名称以及该部门的员工人数。 答案:

代码语言:javascript
代码运行次数:0
运行
复制
select
d.dept_name,
(select count(*) from employees e where e.dept_id = d.dept_id) as employee_count
from departments d;

(Table Subquery / Derived Table) 先计算每个部门的平均工资,然后找出平均工资超过 70000 的部门名称及其平均工资。 答案:

代码语言:javascript
代码运行次数:0
运行
复制
select dept_avg.dept_name, dept_avg.avg_salary
from (
select d.dept_name, avg(e.salary) as avg_salary
from employees e
join departments d on e.dept_id = d.dept_id
group by d.dept_name
) as dept_avg
where dept_avg.avg_salary > 70000;

(UNION) 假设有一个 retired_employees 表结构类似 employees,合并当前员工和退休员工的姓名列表(去除重复)。 答案: (假设 retired_employees 表存在)

代码语言:javascript
代码运行次数:0
运行
复制
select emp_name from employees
union
select emp_name from retired_employees;

(EXISTS) 查询哪些部门至少有一名员工。 答案:

代码语言:javascript
代码运行次数:0
运行
复制
select d.dept_name
from departments d
where exists (select 1 from employees e where e.dept_id = d.dept_id limit 1);

(Column Subquery with ALL) 查询工资比 HR 部门(dept_id=1)所有员工都高的员工姓名和工资。 答案:

代码语言:javascript
代码运行次数:0
运行
复制
select emp_name, salary
from employees
where salary > all (select salary from employees where dept_id = 1);

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

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 表连接 (JOIN)
    • (1.1) 内连接 (INNER JOIN 或 JOIN)
    • (1.2) 外连接 (OUTER JOIN)
    • (1.3) (了解) 交叉连接 (CROSS JOIN)
    • (1.4) 使用 USING 子句
  • 2. 自连接 (Self Join)
  • 3. 联合查询 (UNION / UNION ALL)
  • 4. 子查询 (Subqueries)
    • (4.1) 标量子查询 (Scalar Subquery)
    • (4.2) 列子查询 (Column Subquery)
    • (4.3) 行子查询 (Row Subquery)
    • (4.4) 表子查询 (Table Subquery / Derived Table)
    • (4.5) EXISTS / NOT EXISTS 结合子查询
  • 练习题 (Practice Exercises - Advanced Queries with Answers)
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档