数据库原理与应用【2025-2026-2】

赵春、毛红霞、李驰、刘福刚、曾丽

目录

  • 1 两课设计
    • 1.1 教学大纲
    • 1.2 教案
      • 1.2.1 李驰老师-教案
        • 1.2.1.1 24级数媒4班-教案
      • 1.2.2 毛红霞老师-教案
        • 1.2.2.1 24级数字媒体2班教案
        • 1.2.2.2 24级数字媒体3班教案
    • 1.3 课堂反思
      • 1.3.1 李驰老师-课堂反思
        • 1.3.1.1 24数媒4班
          • 1.3.1.1.1 3月9日(第2周第1次课)
          • 1.3.1.1.2 3月16日(第4周第1次课)
      • 1.3.2 毛红霞老师-课堂反思
        • 1.3.2.1 24级数字媒体2班
          • 1.3.2.1.1 3月12号(第2周第2次课——除运算)
          • 1.3.2.1.2 3月18号(第3周第1次课——关系代数习题课)
        • 1.3.2.2 24级数字媒体3班
          • 1.3.2.2.1 3月12日(第2周第2次课——连接、除)
          • 1.3.2.2.2 3月17日(第3周第1次课——关系代数习题课)
    • 1.4 课程反思
      • 1.4.1 李驰老师-课程反思
      • 1.4.2 毛红霞老师-课程反思
    • 1.5 高阶课堂安排
    • 1.6 教材案例配套数据表
    • 1.7 实验数据库Northwind
    • 1.8 数据库软件安装
  • 2 数据库基本概念
    • 2.1 学习内容及要求
    • 2.2 课堂设计(教案)
    • 2.3 内容要点解析
    • 2.4 电子课件
    • 2.5 参考文献
  • 3 关系数据库
    • 3.1 学习内容及要求
    • 3.2 课堂设计(教案)
    • 3.3 内容要点解析
    • 3.4 电子课件
    • 3.5 参考文献
    • 3.6 微课视频
    • 3.7 翻转教学
      • 3.7.1 学习任务单
  • 4 关系代数
    • 4.1 学习内容及要求
    • 4.2 课堂设计(教案)
    • 4.3 内容要点解析
    • 4.4 电子课件
    • 4.5 参考文献
    • 4.6 微课视频
    • 4.7 翻转教学
      • 4.7.1 学习任务单
  • 5 关系代数(习题课)
    • 5.1 学习内容及要求
    • 5.2 课堂设计(教案)
    • 5.3 内容要点解析
    • 5.4 电子课件
    • 5.5 微课视频
  • 6 SQL数据类型与数据定义语句
    • 6.1 学习内容及要求
    • 6.2 课堂设计(教案)
    • 6.3 内容要点解析
    • 6.4 电子课件
    • 6.5 参考文献
    • 6.6 微课视频
    • 6.7 上机实验
    • 6.8 翻转教学1
      • 6.8.1 学习任务单
      • 6.8.2 翻转课件
      • 6.8.3 翻转视频
      • 6.8.4 讨论问题
    • 6.9 翻转教学2
      • 6.9.1 学习任务单
  • 7 SQL单表基本查询
    • 7.1 学习内容及要求
    • 7.2 课堂设计(教案)
    • 7.3 内容要点解析
    • 7.4 电子课件
    • 7.5 参考文献
    • 7.6 微课视频
      • 7.6.1 单表查询与聚合函数
      • 7.6.2 图形化单表查询
    • 7.7 上机实验
    • 7.8 翻转教学
      • 7.8.1 学习任务单
  • 8 SQL统计查询
    • 8.1 学习内容及要求
    • 8.2 课堂设计(教案)
    • 8.3 内容要点解析
    • 8.4 电子课件
    • 8.5 参考文献
    • 8.6 微课视频
    • 8.7 上机实验
    • 8.8 翻转教学
      • 8.8.1 学习任务单
  • 9 SQL多表查询
    • 9.1 学习内容及要求
    • 9.2 课堂设计(教案)
    • 9.3 内容要点解析
    • 9.4 电子课件
    • 9.5 参考文献
    • 9.6 微课视频
    • 9.7 上机实验
    • 9.8 翻转教学
      • 9.8.1 学习任务单
  • 10 SQL嵌套子查询
    • 10.1 学习内容及要求
    • 10.2 课堂设计(教案)
    • 10.3 内容要点解析
    • 10.4 电子课件
    • 10.5 参考文献
    • 10.6 微课视频
    • 10.7 上机实验
    • 10.8 翻转教学
      • 10.8.1 翻转视频
      • 10.8.2 翻转课任务分配
      • 10.8.3 翻转课流程安排
      • 10.8.4 学习任务单
      • 10.8.5 查询语句编写秘籍
  • 11 SQL数据更新
    • 11.1 学习内容及要求
    • 11.2 课堂设计(教案)
    • 11.3 内容要点解析
    • 11.4 电子课件
    • 11.5 参考文献
    • 11.6 微课视频
    • 11.7 上机实验
    • 11.8 SQL编程综合习题课(高阶翻转)
      • 11.8.1 翻转学习任务单
      • 11.8.2 翻转视频
      • 11.8.3 翻转课件
    • 11.9 备份资料
      • 11.9.1 学习任务单
  • 12 索引和视图
    • 12.1 学习内容及要求
    • 12.2 课堂设计(教案)
    • 12.3 内容要点解析
    • 12.4 电子课件
    • 12.5 参考文献
    • 12.6 微课视频
    • 12.7 上机实验
    • 12.8 翻转教学
      • 12.8.1 学习任务单
      • 12.8.2 翻转课件
  • 13 数据库设计
    • 13.1 学习内容及要求
    • 13.2 课堂设计(教案)
    • 13.3 内容要点解析
    • 13.4 电子课件
    • 13.5 参考文献
    • 13.6 微课视频
    • 13.7 上机实验
    • 13.8 数据库设计-翻转方案I(分班级实施)
      • 13.8.1 学习任务单
      • 13.8.2 翻转视频
      • 13.8.3 翻转课件
      • 13.8.4 课前作业及讨论
    • 13.9 数据库设计-翻转方案II(分班级实施)
      • 13.9.1 学习任务单
      • 13.9.2 翻转视频
      • 13.9.3 翻转课件
      • 13.9.4 问答总结
      • 13.9.5 技巧总结
      • 13.9.6 案例练习
  • 14 规范化理论-1
    • 14.1 学习内容及要求
    • 14.2 课堂设计(教案)
    • 14.3 内容要点解析
    • 14.4 电子课件
    • 14.5 参考文献
    • 14.6 翻转教学
      • 14.6.1 学习任务单
  • 15 规范化理论-2
    • 15.1 学习内容及要求
    • 15.2 课堂设计(教案)
    • 15.3 内容要点解析
    • 15.4 电子课件
    • 15.5 参考文献
    • 15.6 翻转视频
    • 15.7 上机实验
    • 15.8 翻转教学
      • 15.8.1 学习任务单
  • 16 事务、存储过程与触发器
    • 16.1 学习内容及要求
    • 16.2 课堂设计(教案)
    • 16.3 内容要点解析
    • 16.4 电子课件
    • 16.5 参考文献
    • 16.6 微课视频
    • 16.7 翻转教学
      • 16.7.1 学习任务单
  • 17 数据库高阶挑战性项目实践Ⅰ
    • 17.1 学习内容及要求
    • 17.2 实践任务
    • 17.3 课堂设计(教案)
    • 17.4 参考文献
    • 17.5 微课视频
  • 18 数据库高阶挑战性项目实践Ⅱ
    • 18.1 学习内容及要求
    • 18.2 实践任务
    • 18.3 课堂设计(教案)
    • 18.4 电子课件
    • 18.5 参考文献
    • 18.6 微课视频
    • 18.7 数据预处理总结
  • 19 课程扩展-数据备份与导入导出
    • 19.1 微课视频
    • 19.2 参考文献
  • 20 课程设计
    • 20.1 课堂设计(教案)
    • 20.2 课程设计及项目报告模板
参考文献
  • 1 子查询
  • 2 相关和不相关子查...
  • 3 子查询和表连接对比

                                          子查询

转载自:https://zhuanlan.zhihu.com/p/603197412?utm_id=0

前言

概念:

出现在其他语句内部的SELECT语句,称为子查询或内查询(其他语句:不限于SELECT语句,不过最常出现在SELECT语句内部)。与之相对地,外部的查询语句,称为外查询或主查询。

分类:

  • 按子查询出现的位置:SELECT后面、FROM后面、WHERE或HAVING后面、EXISTS后面(相关子查询)

  • 按查询结果的行列数:标量子查询(结果只有一行一列)、列子查询(结果只有一列多行,也称为多行子查询)、行子查询(结果有一行多列或多行多列)、表子查询(结果一般为多行多列)

SELECT后面:仅仅支持标量子查询

FROM后面:支持表子查询

WHERE或HAVING后面:主要支持标量子查询和列子查询,行子查询用得比较少

EXISTS后面:支持表子查询

1. WHERE或HAVING后面的子查询

特点:

(1)子查询放在小括号内

(2)子查询一般放在条件的右侧

(3)标量子查询一般搭配着单行操作符使用,比如:>、<、>=、<=、=、!=;列子查询一般搭配着多行操作符使用,比如IN、ANY/SOME、ALL

(4)子查询的执行优先于主查询执行,主查询的条件用到了子查询的结果。

1.1 标量子查询

子查询结果返回标量,即1行1列。

  • 案例1:谁的工资(salary)比 Abel 高(注:Abel是姓氏)?已知:表employees中有员工信息,包含工资字段salary。

分析:从employees中先获取姓氏为Abel的工资,然后再筛选比Abel的工资高的员工。

# ①查询Abel(姓氏)的工资
SELECT salary
FROM employees
WHERE last_name = 'Abel';

# ②查询员工的信息,满足salary>①的结果
SELECT *
FROM employees
WHERE salary > (
	SELECT salary
	FROM employees
	WHERE last_name = 'Abel'
);

因为子查询的结果只有一行一列(表中Abel姓氏的员工只有一位,对应的工资也只有1个),所以是标量子查询。

  • 案例2:返回job_id与141号(employee_id)员工相同,salary比143号员工多的员工姓名(first_name和last_name),job_id和工资(salary)。注:以上字段均在employees表中。

分析:先从employees表中找到员工id为141号的工作id,还有员工id为143号的工资,然后再筛选工作id与前者相等,工资比后者高的员工。

# ①查询141号员工的工作id
SELECT job_id
FROM employees
WHERE employee_id = 141;

# ②查询143号员工的工资
SELECT salary
FROM employees
WHERE employee_id = 143;

# ③查询员工的姓名、job_id和工资,要求job_id=①且salary>②
SELECT first_name,last_name,job_id,salary
FROM employees
WHERE job_id = (
	SELECT job_id
	FROM employees
	WHERE employee_id = 141
) AND salary > (
	SELECT salary
	FROM employees
	WHERE employee_id = 143
);

这个案例中有2个子查询,说明WHERE后面可以支持多个子查询。

  • 案例3:返回公司工资最少的员工last_name,job_id,salary。注:以上字段均在employees表中。

分析:从employees表中找到最少的工资,然后筛选出工资数目是最少工资的员工last_name,job_id,salary。

# ①查询公司的最低工资
SELECT MIN(salary)
FROM employees;

# ②查询last_name,job_id和salary,要求salary=①
SELECT last_name,job_id,salary
FROM employees
WHERE salary = (
	SELECT MIN(salary)
	FROM employees
);
  • 案例4:查询最低工资大于50号部门最低工资的部门id和其最低工资。

分析:从employees表中找到50号部门的最低工资,然后筛选部门最低工资大于前者的部门id和其最低工资。

# ①查询50号部门的最低工资
SELECT MIN(salary)
FROM employees
WHERE department_id = 50;

# ②查询部门id,最低工资,满足最低工资>①
SELECT department_id,MIN(salary) AS min_salary
FROM employees
GROUP BY department_id
HAVING MIN(salary) > (
	SELECT MIN(salary)
	FROM employees
	WHERE department_id = 50
);

这里的子查询是嵌套在HAVING后面的,因为涉及按部门id分组求最低工资。

注意:子查询的结果若不是一行一列,不能使用标量子查询,不能搭配单行操作符。

1.2 列子查询(多行子查询)

子查询结果返回1列多行。

操作符含义
(NOT) IN等于列表中的任意一个
ANY|SOME和子查询返回的某一个值比较(列表中某一个满足条件)
ALL和子查询返回的所有值比较(列表中均满足条件)

事实上,IN()与= ANY()达到的效果一样;NOT IN()与!= ALL()效果一样

  • 案例1:返回location_id是1400或1700的部门中的所有员工姓名。location_id字段在departments表中,员工姓名在employees表中,二表共有字段是department_id。

分析:从departments表中查询location_id是1400或1700的部门id(department_id),然后从employees表中找到部门id在前者中的员工姓名。

# ①查询location_id是1400或1700的部门id(在departments表中)
SELECT department_id
FROM departments
WHERE location_id IN (1400,1700);

# ②查询员工姓名,要求部门号是①列表中的一个(在employees表中)
SELECT first_name,last_name
FROM employees
WHERE department_id IN (
	SELECT department_id
	FROM departments
	WHERE location_id IN (1400,1700)
);

子查询中返回location_id是1400或1700的部门id是多行的,因此是列子查询(多行子查询)。

  • 案例2:返回其它工种中比job_id为'IT_PROG'工种任一工资低的员工的员工号(employee_id)、last_name、job_id、salary。注:以上字段均在employees表中。

分析:从employees表中找到job_id为'IT_PROG'工种的所有工资,然后查询其它工种中工资低于前者列表中任一值的员工信息,注意:其它工种就表明job_id不能等于'IT_PROG'。

# ①查询job_id为'IT_PROG'的部门工资
SELECT salary 
FROM employees
WHERE job_id = 'IT_PROG';

# ②查询employee_id、last_name、job_id、salary,要求salary<①中任一值,且job_id不是'IT_PROG'
SELECT employee_id,last_name,job_id,salary
FROM employees
WHERE salary < ANY(
	SELECT salary 
	FROM employees
	WHERE job_id = 'IT_PROG'
) AND job_id != 'IT_PROG';
  • 案例3:返回其它工种中比job_id为'IT_PROG'工种所有工资都低的员工的员工号(employee_id)、last_name、job_id、salary。注:以上字段均在employees表中。

# ①查询job_id为'IT_PROG'的部门工资
SELECT salary 
FROM employees
WHERE job_id = 'IT_PROG';

# ②查询employee_id、last_name、job_id、salary,要求salary<①中所有值,且job_id不是'IT_PROG'
SELECT employee_id,last_name,job_id,salary
FROM employees
WHERE salary < ALL(
	SELECT salary 
	FROM employees
	WHERE job_id = 'IT_PROG'
) AND job_id != 'IT_PROG';

1.3 行子查询

子查询结果返回1行多列或多行多列,重点是多列

  • 案例1:查询员工编号(employee_id)最小且工资(salary)最高的员工信息。

分析:这道题可以采用标量子查询的思想,具体做法类似1.1节中的案例2。从employees表中分别查询最小的员工编号和最高的员工工资,筛选同时符合这两个条件的员工信息。

# ①查询最小的员工编号
SELECT MIN(employee_id)
FROM employees;

# ②查询最高的员工工资
SELECT MAX(salary)
FROM employees;

# ③查询员工信息,要求employee_id=①且salary=②
SELECT *
FROM employees
WHERE employee_id = (
	SELECT MIN(employee_id)
	FROM employees
) AND salary = (
	SELECT MAX(salary)
	FROM employees
);

但是,我们这里可以换一种思路,采用行子查询,查询结果不变。

# 行子查询
SELECT *
FROM employees
WHERE (employee_id,salary) = (
	SELECT MIN(employee_id),MAX(salary)
	FROM employees
);

行子查询具有局限性,子查询的多个字段(列)需要有一样的操作符,比如这个案例中employee_id和salary都用=这个操作符进行筛选,因此可以把(employee_id,salary)看作一个虚拟的字段。

2. SELECT后面的子查询

SELECT后面仅仅支持标量子查询,即1行1列

  • 案例1:查询每个部门(department_id)的员工个数。注:不能仅从employees表中计算员工个数,因为employees表中只包含有员工的部门,存在一些部门没有员工的情况。

分析:departments表中罗列了所有的部门,有些部门有员工,员工信息出现在employees表中,可在employees表中统计个数,但有些部门没有员工,员工个数就是0。因此,主查询的表是departments,子查询的表是employees,且子查询中必须要满足这两个表格的部门id相等。具体查询语句如下,根据SQL的执行顺序得知此子查询的结果返回的是一个标量

# 子查询
SELECT *,(
	SELECT COUNT(employee_id)
	FROM employees e
	WHERE e.department_id = d.department_id) AS counts
FROM departments d;

当然,这个案例可以采用连接的方法,因为涉及两个表(employees和departments),且有公共字段(department_id),具体查询语句如下:

# 连接查询
SELECT d.*, COUNT(employee_id) AS counts
FROM departments d
LEFT JOIN employees e
ON d.`department_id` = e.`department_id`
GROUP BY d.`department_id`;

3. FROM后面的子查询

FROM后面一般是表,代表数据源,因此是把子查询的结果当作一张表,且必须给这张表起别名

  • 案例1:查询每个部门的平均工资的工资等级。

已知工资等级表(job_grades):


工资等级表


# ①查询每个部门的平均工资
SELECT department_id,AVG(salary) AS avg_salary
FROM employees
GROUP BY department_id;


每个部门的平均工资


根据平均工资在工资等级表的情况,可以判别每个部门平均工资的工资等级,这里就涉及非等值连接,判断平均工资是否介于某等级的最低工资和最高工资之间。

# ②查询工资等级,判断平均工资在哪个等级
SELECT avg_dep.*,grade_level
FROM (
	SELECT department_id,AVG(salary) AS avg_salary
	FROM employees
	GROUP BY department_id
) avg_dep
LEFT JOIN job_grades g
ON avg_salary BETWEEN lowest_sal AND highest_sal;


查询结果


4. EXISTS后面的子查询

EXISTS后面的子查询也叫做相关子查询。

语法:EXISTS(完整的查询语句),括号内的查询语句可以1行1列,也可以多行多列。

结果:1或0,当括号内有查询结果时,返回1,当括号内没有查询结果时,返回0。在逻辑判断中,1可认为是True,0可认为是False。

一般来讲,EXISTS后面的子查询都可以用IN后面的子查询代替,所以用得不是很多。

  • 案例1:查询有员工的部门名。

# 采用IN的方式
SELECT department_name
FROM departments d
WHERE d.department_id IN (
	SELECT department_id
	FROM employees
);

# 采用EXISTS的方式
SELECT department_name
FROM departments d
WHERE EXISTS(
	SELECT department_id
	FROM employees e
	WHERE e.`department_id` = d.`department_id`
);

总结

(1)WHERE或HAVING后面的子查询结果可以是1行1列(标量),多行1列,或者1行多列,这里的子查询结果是作为筛选条件的,一般可使用单行运算符,比如<、>、=、<=、>=、!=,也可使用多行运算符,比如IN、ANY、SOME、ALL,常用的是IN。

(2)SELECT后面的子查询结果必须是1个标量,即1行1列,这里的子查询结果一般是生成新的字段。

(3)FROM后面的子查询结果一般是多行多列,充当一张表,且这张表必须取别名

(4)EXISTS后面的子查询一般可以用IN后面的子查询代替