文章

数据库1-5章复习

数据库1-5章复习

数据库1-5章复习

面对这种没有明确题型的随堂测验,最稳妥的策略是“抓大放小,理清主干,主攻查询”。数据库这门课的特点是:概念多但逻辑性强,SQL语法碎但套路固定。

通常这类测验的题型无外乎三种:

  1. 概念理解题(选择、判断、填空、简答):考查第1、2章的基本概念,以及各章中的理论部分(如视图的作用、事务的特性等)。
  2. 读码/写码题(核心大头):给出英文/中文需求,让你写出关系代数(第2章)或SQL语句(第3、4章);或者给你一段SQL,问你输出什么。
  3. 应用机制题:结合第4、5章,考查完整性约束怎么写、触发器怎么工作、JDBC防止SQL注入的原理等。

不用慌,我为你梳理了一份核心考点大纲SQL复习套路,照着这个框架复习,效率会高很多。


📚 核心考点大纲(划重点版)

第1章:引言 (Introduction) - 纯概念,重理解

  • 文件系统 vs 数据库系统:为什么要有数据库?(解决数据冗余、不一致、数据孤岛、并发异常、安全性问题)。
  • 数据抽象的三个层次物理层(怎么存)、逻辑层(存什么,有什么联系)、视图层(给特定用户看什么)。了解“物理数据独立性”。
  • 模式 (Schema) 与实例 (Instance):模式是结构(不常变),实例是某一时刻的数据(常变)。类似于编程语言中的类型与变量。
  • 数据库语言:DDL(数据定义语言,建表/约束)、DML(数据操纵语言,增删改查)。

第2章:关系模型介绍 (Relational Model) - 必考基础与关系代数

  • 基本概念:关系(表)、元组(行)、属性(列)、域(Domain,必须是原子的)。
  • 键 (Keys) 的辨析(绝对重点)
    • 超码 (Superkey):能唯一标识元组的属性集。
    • 候选码 (Candidate key):最小的超码。
    • 主码 (Primary key):被选中用来标识元组的候选码。
    • 外码 (Foreign key):用于引用完整性,参照另一张表的主码。
  • 关系代数 (Relational Algebra)(必考写表达式)
    • 基本操作:选择 $\sigma$ (where)、投影 $\Pi$ (select)、笛卡尔积 $\times$、并 $\cup$、差 $-$、更名 $\rho$。
    • 附加操作:集合交 $\cap$、自然连接 $\bowtie$、赋值 $\leftarrow$。

第3章:SQL 介绍 (Introduction to SQL) - 重中之重,占分最高

  • DDL基础create table 的基本语法,主键、外键、not null的声明。
  • 基本查询结构select ... from ... where ...(注意 select distinct 去重)。
  • 字符串操作like 的通配符(% 匹配任意子串,_ 匹配单个字符),转义 escape
  • 集合操作union, intersect, except(默认去重,带 all 不去重)。
  • 空值 (Null) 逻辑:任何涉及 Null 的算术运算结果为 Null;比较运算结果为 unknownwhere 子句中只接受 truefalseunknown都被过滤)。使用 is null 判断。
  • 聚集函数与分组(极易出题)
    • count, sum, avg, max, min
    • 关键规则select 语句中没有被聚集的属性,必须出现在 group by 子句中!
    • where(分组前过滤) vs having(分组后过滤)。
  • 嵌套子查询(难点)
    • 集合成员:in, not in
    • 集合比较:> some (大于其中至少一个), > all (大于所有)。
    • 空关系测试:exists, not exists(常用于表达“全部包含”这类复杂的逻辑)。
  • 数据库修改insert, delete, update(注意 update 中使用 case when ... then ... 避免顺序问题的考点)。

第4章:中级 SQL (Intermediate SQL) - 进阶查询与数据库特性

  • 连接 (Join) 表达式(必考)
    • 内连接:natural join, join ... using(...), join ... on ...。了解 natural join 的危险性(同名但不相关的列被错误匹配)。
    • 外连接:left outer join, right outer join, full outer join(保留未匹配的元组,用 Null 填充)。
  • 视图 (Views)
    • 作用:简化查询、安全性(隐藏数据)。
    • 物化视图 (Materialized Views) 的概念。
    • 视图更新的限制:通常只能在简单的视图上进行 insert/update/delete(单表、无聚集、无 distinct)。
  • 事务 (Transactions)commit(提交)与 rollback(回滚),保证原子性。
  • 完整性约束not null, unique, check(谓词),以及外键的级联操作 on delete cascade / set null
  • 授权 (Authorization)grant <权限> on <表/视图> to <用户/角色>revoke 命令。

第5章:高级 SQL (Advanced SQL, 仅限 5.1 - 5.3) - 偏向机制与应用

  • 5.1 编程语言访问 (JDBC/ODBC)
    • 为什么需要?(SQL不是图灵完备的,无法做UI等)。
    • 预备语句 (PreparedStatement) 的作用:提高效率,防止 SQL 注入 (SQL Injection)(必考概念!千万不要用字符串拼接拼SQL)。
    • 元数据 (Metadata) 的作用。
  • 5.2 函数与过程 (Functions and Procedures)
    • 了解如何使用 create functioncreate procedure 将业务逻辑写在数据库里。参数类型 (in, out)。
  • 5.3 触发器 (Triggers)
    • 组成:事件 (insert/update/delete) + 条件 (when) + 动作 (begin…end)。
    • referencing new row as / old row as 的用法。
    • 何时不该用触发器?(现在很多功能如图2、复制、统计表已经被物化视图和内置复制机制取代,触发器容易导致级联错误和意外执行)。

💡 针对“SQL语法太碎”的复习套路

不要死记硬背SQL,在脑子里建立一个“SQL执行顺序”的模型。当你写SQL或者读SQL时,按照数据库的执行逻辑来想,就不会乱:

  1. FROM:先找数据源。我要查的数据在哪几张表里?需不需要 JOIN
  2. WHERE:过滤行。在连好的大表里,哪些行是我不要的?(注意:这里不能用聚集函数)。
  3. GROUP BY:分组。把剩下的行按什么特征分类?
  4. HAVING:过滤组。分完组之后,哪些组是我不要的?(这里通常配合 sum, count 等聚集函数)。
  5. SELECT:提取列。最后我要展示哪些列?计算什么表达式?
  6. ORDER BY:排序。最后结果按什么顺序输出?

举个应试例子: 题目:找出 2018年春季学期,教授超过一门课的教师ID和姓名。

  • 思考步骤
    • 涉及表 (FROM):教师表 (instructor) 和 授课表 (teaches),因为需要姓名和授课时间。
    • 怎么连:instructor natural join teaches
    • 初步过滤 (WHERE):year = 2018 and semester = 'Spring'
    • 怎么分组 (GROUP BY):按老师分 group by instructor.ID, name
    • 分组后过滤 (HAVING):教的课超过一门 having count(course_id) > 1
    • 展示结果 (SELECT):select ID, name

🚀 最后两天的突击策略

  1. 翻课本/PPT的例题:把你PDF里的所有SQL例题看懂。教授出题大概率就是把课件上的例题改一改表名或者条件。特别注意大学模型(student, instructor, course, takes, teaches)的表结构,这是全书的基石。
  2. 手写代数与SQL:拿一张白纸,挡住答案,自己手写几道。特别是关系代数,由于只能用纯文本符号,很容易写错角标或括号;多表查询一定要想清楚关联条件。
  3. 不要钻牛角尖:第5章的代码(比如JDBC具体的Java代码怎么写)一般不会让你默写,主要是考概念(比如为什么要用PreparedStatement)。重点拿分的还是第3章和第4章的查询语句。

深呼吸,按照大纲把知识点过一遍,理清逻辑,测验没问题的!祝你好运!

没问题!第一章虽然全是概念,但它是整个数据库体系的“世界观”。考试中这部分通常以选择题、判断题、填空题简答题的形式出现。

我们结合你课本上的例子(大学模型和银行模型),把这四个核心知识点掰开揉碎,帮你真正理解:


1. 文件系统 vs 数据库系统:为什么要有数据库?

考点:理解早期文件处理系统的7大弊端(简答题常客,也常在选择题中通过案例让你判断属于哪种弊端)。

在数据库出现之前,人们把数据存在操作系统的普通文件里(比如txt、csv),这会导致以下致命问题,而数据库系统 (DBMS) 完美解决了它们:

  1. 数据的冗余和不一致性 (Data redundancy and inconsistency)
    • 概念:同一份数据在多个文件中重复存储(冗余),一旦修改没同步,就会导致数据冲突(不一致)。
    • 课本例子:一个学生同时辅修音乐和数学,他的地址可能同时存在音乐系和数学系的文件里。如果他搬家了,只改了音乐系的文件,数学系的文件没改,系统里就有两个不同的地址了。
  2. 数据访问困难 (Difficulty in accessing data)
    • 概念:想查点新东西,必须让程序员新写一个程序。
    • 课本例子:你想查“邮编是10086的所有学生”,文件系统里没有现成的程序,你只能人工找,或者求程序员现写一个代码,极不方便。
  3. 数据孤岛 (Data isolation)
    • 概念:数据散落在不同格式的文件里,想把它们联合起来查询非常困难。
  4. 完整性问题 (Integrity problem)
    • 概念:数据必须满足某些业务规则(约束),文件系统很难强制执行。
    • 课本例子:银行账户余额不能小于0。在文件系统里,你必须在每段取钱的代码里手写判断逻辑;但在数据库里,你可以直接声明一个约束(check balance >= 0),数据库会自动把关。
  5. 原子性问题 (Atomicity problem) —— 重点!
    • 概念:一个操作要么全做,要么全都不做,不能停在中间状态。
    • 课本例子:A账户给B账户转账500元,A扣了500,突然系统断电崩溃了,B还没来得及加500。钱凭空消失了!数据库能保证这种事不发生(故障恢复)。
  6. 并发访问异常 (Concurrent-access anomaly) —— 重点!
    • 概念:多个用户同时操作同一条数据,结果算错了。
    • 课本例子:账户里有10000元。职员甲取500,职员乙取100。两人同时读取到10000,甲算出9500写回去,乙算出9900写回去。最后账户变成了9900,银行亏了400。数据库有并发控制机制来解决。
  7. 安全性问题 (Security problem)
    • 概念:不是所有人都能看所有数据。
    • 课本例子:工资管理员能看财务信息,但不能看学生成绩。

2. 数据抽象的三个层次(Levels of Abstraction)

考点:区分物理层、逻辑层、视图层,深刻理解“物理数据独立性”。

为了让普通用户能轻松使用数据库,而不必去学底层硬盘的读写原理,数据库把数据分成了三个抽象层次:

  1. 物理层 (Physical level):最低层。
    • 描述:数据实际上是怎么存储在硬盘上的(涉及底层的数据结构、字节、索引、B+树等)。这是数据库引擎开发者关心的。
  2. 逻辑层 (Logical level):中间层。
    • 描述:数据库中存储了什么数据,以及这些数据之间有什么联系
    • 受众:数据库管理员 (DBA) 和程序员。例如,在这里我们定义 instructor 表有 ID, name, salary 几个字段。
  3. 视图层 (View level):最高层。
    • 描述:只描述整个数据库的某一部分,隐藏了其他不相关或机密的数据。
    • 受众:终端用户。例如,教务处注册选课的系统(一个视图)只能看到学生和课程,看不到老师的工资。

💡 核心考点:物理数据独立性 (Physical Data Independence)

  • 定义应用程序依赖于逻辑模式,而不依赖于物理模式。
  • 大白话:不管底层物理层怎么变(比如把数据从机械硬盘迁移到固态硬盘,或者给表加了一个新索引加快查询速度),逻辑层的表结构没变,所以上面写的SQL应用程序一行代码都不需要改。

3. 模式 (Schema) 与实例 (Instance)

考点:类比编程语言,区分静态的结构和动态的数据。

  1. 模式 (Schema)
    • 定义:数据库的总体逻辑结构/设计
    • 特点:一旦设计好,极少改变
    • 编程类比:就像C/Java里的变量声明类的定义int a;class Student { ... })。
  2. 实例 (Instance)
    • 定义:在特定时刻,数据库中实际存储的数据集合(即表里填进去的一行行数据快照)。
    • 特点:随着增删改查,时刻都在变化
    • 编程类比:就像程序运行时,某个变量在某一时刻的具体值a = 5;)。

4. 数据库语言 (Database Languages)

考点:区分 DDL 和 DML 的作用,理解“声明式”。

数据库系统提供两种主要的语言(通常统合在 SQL 中)来操作上面的模式和实例:

  1. 数据定义语言 (DDL - Data Definition Language)
    • 作用:用来定义数据库的模式 (Schema),也就是建表、改表结构、加约束
    • 输出:DDL 执行后,会把结果生成元数据(关于数据的数据)存放在数据字典 (Data Dictionary) 中。
    • 代表操作CREATE TABLE, DROP, ALTER。约束如 NOT NULL(非空), PRIMARY KEY(主键)。
  2. 数据操纵语言 (DML - Data Manipulation Language)
    • 作用:用来操作数据库中的实例 (Instance),也就是对数据进行增、删、改、查
    • 代表操作SELECT (查), INSERT (增), UPDATE (改), DELETE (删)。
    • 分类(易考概念)
      • 过程化 DML (Procedural):用户需要指定要什么数据,还要指定怎么一步步去获取(像用C语言写一个for循环去找数据)。
      • 声明式 DML (Declarative / Non-procedural):用户只需要说明“我要什么数据”,不需要管底层怎么找。SQL 查询就是声明式的! 系统内部的“查询优化器”会自动寻找最高效的获取路径。

复习小测(检验一下):

  1. 学生搬家导致三个系统地址不一致,属于文件系统的什么弊端?(答:数据冗余和不一致性)
  2. 数据库底层从机械硬盘换成SSD,我的SQL查询代码需要重写吗?这体现了什么性质?(答:不需要,体现了物理数据独立性)
  3. CREATE TABLE 命令属于DDL还是DML?(答:DDL)
  4. SQL查询语言是过程化的还是声明式的?(答:声明式的)

理清了第一章的这些基本概念,你的数据库基础框架就搭好了!接下来要不要一起顺一顺第二章(关系代数与各种键的区分)

第二章是整个关系数据库的灵魂!SQL语句的底层逻辑全是关系代数。考试中,这一章要么出在选择判断题里考“键”的辨析,要么就是大题:“请用关系代数写出下列查询”

我们把这块“硬骨头”啃下来:


1. 基本概念:数学黑话翻译

考点:关系是“集合”,所以无序且不能有重复行;域必须是“原子的”。

数据库学者喜欢用数学名词,我们把它翻译成大白话:

  • 关系 (Relation) = 表 (Table)
  • 元组 (Tuple) = 行 (Row / Record)。表里的一条记录。
  • 属性 (Attribute) = 列 (Column / Field)。表的一个字段。
  • 关系实例 (Relation Instance) = 某一个时刻表里装的具体数据。
  • 域 (Domain) = 某个属性允许取值的集合(比如整数、字符串)。
    • 🚨 必考点:关系模型要求域必须是原子的 (Atomic)!意思就是不可再分。比如“电话号码”这个属性,里面不能存一个列表 [138xxx, 139xxx],只能存一个单一的值。

2. 键 (Keys) 的辨析(绝对重点,必考套娃逻辑)

考点:给你几个属性集,让你判断哪个是超码、候选码。一定要记住它们的“包含关系”。

假设有一张学生表 Student(学号, 身份证号, 姓名, 年龄)

  1. 超码 (Superkey)
    • 定义:只要能唯一标识一行元组的属性集合,都是超码。它允许有“废话”(多余属性)。
    • 例子{学号} 是超码;{学号, 姓名} 也是超码;{身份证号, 年龄} 也是超码。只要带上学号或身份证号,就能唯一确定一个人。
  2. 候选码 (Candidate key)
    • 定义最小的超码。也就是超码里把“废话”全删掉,多一个嫌胖,少一个不行
    • 例子{学号} 是候选码,{身份证号} 也是候选码。但 {学号, 姓名} 不是候选码,因为去掉“姓名”后,剩下的 {学号} 依然能唯一标识。
  3. 主码 (Primary key)
    • 定义:数据库设计师从多个“候选码”中选定出场的一个。通常在表结构设计中底下画一条实线下划线(如 $\underline{ID}$)。
    • 例子:设计师一拍大腿,决定用 {学号} 当主码。
  4. 外码 (Foreign key)
    • 定义:本表的某个属性,它不是本表的主码,但它是另一张表的主码。用于维持引用完整性 (Referential Integrity)
    • 例子教师表(ID, 姓名, 所在系)系表(系名, 预算)。教师表里的“所在系”就是外码,引用了系表的主码“系名”。意思是:老师所在的系,必须在系表里真实存在。

💡 做题口诀主码 $\subseteq$ 候选码 $\subseteq$ 超码。候选码是极简版的超码。


3. 关系代数 (Relational Algebra) - 必考大题

考点:给你一句话,让你画出这些希腊字母的组合。把它们直接当成 SQL 语句的底层函数来记!

① 基本操作(大写六剑客)

  • 选择 $\sigma$ (Sigma) —— 对应 SQL 的 WHERE (过滤)
    • 用法:$\sigma_{条件}(表名)$
    • 例子:找出物理系的老师 $\rightarrow \sigma_{dept_name=”Physics”}(instructor)$
  • 投影 $\Pi$ (Pi) —— 对应 SQL 的 SELECT (过滤)
    • 用法:$\Pi_{列名1, 列名2}(表名)$
    • 例子:只看老师的名字和工资 $\rightarrow \Pi_{name, salary}(instructor)$
    • 🚨 易错考点:关系代数里的 $\Pi$ 会自动去重!(因为关系是集合,集合里不能有重复元素),这和 SQL 的 SELECT(默认不去重)不一样。
  • 笛卡尔积 $\times$ (Cross Product) —— 对应 SQL 的 FROM A, B (无脑硬组CP)
    • 用法:$A \times B$
    • 效果:A表有3行,B表有4行,乘出来就是 $3 \times 4 = 12$ 行。把所有可能全配对。
  • 并 $\cup$ (Union) —— 对应 SQL 的 UNION
    • 用法:$A \cup B$ (把A和B的行拼在一起)
    • 前提:相容性(两张表的列数必须一样,对应列的数据类型必须一样)。自动去重
  • 差 $-$ (Set Difference) —— 对应 SQL 的 EXCEPT / MINUS
    • 用法:$A - B$ (在A里,但不在B里)
    • 例子:2017秋季开的课 $-$ 2018春季开的课 = 只有17秋开过,18春没开的课。
  • 更名 $\rho$ (Rho) —— 对应 SQL 的 AS (起别名)
    • 用法:$\rho_{新表名}(原表)$ 或 $\rho_{新表名(新列1, 新列2)}(原表)$
    • 作用:当你想把一张表和它自己做连接时(自连接),比如“找出比爱因斯坦工资高的老师”,必须用 $\rho$ 复制出一个假分身。

② 附加操作(方便你写表达式的组合技)

  • 集合交 $\cap$ (Intersection) —— 对应 SQL 的 INTERSECT
    • 用法:$A \cap B$ (既在A里,又在B里)
    • 其实它是推导出来的:$A \cap B = A - (A - B)$。
  • 自然连接 $\bowtie$ (Natural Join) —— 对应 SQL 的 NATURAL JOIN
    • 用法:$A \bowtie B$
    • 极其重要:它是 $\times$(笛卡尔积) + $\sigma$(挑出同名列相等的行) + $\Pi$(删掉重复列)的完美结合体!
    • 效果:系统会自动去找A表和B表里名字一样的列,让它们的值相等,拼成一张大表。
    • 例子:$\Pi_{name, course_id} (student \bowtie takes)$ $\rightarrow$ 查出所有选了课的学生名字和课程号。(底层自动通过学号 ID 把学生表和选课表连起来了)。
  • 赋值 $\leftarrow$ (Assignment) —— 像写代码一样定义中间变量
    • 用法:$临时表 \leftarrow 关系代数表达式$
    • 作用:如果一道大题非常长,不要写一长串反人类的嵌套,可以用 $\leftarrow$ 分步写,卷面清晰,老师给分也爽。
    • 例子: $Physics \leftarrow \sigma_{dept_name=”Physics”}(instructor)$ $Music \leftarrow \sigma_{dept_name=”Music”}(instructor)$ $Result \leftarrow Physics \cup Music$

📝 实战组合拳(考试怎么写?)

经典真题:找出讲授了“CS-101”这门课的所有教师的名字。 (涉及表:instructor(ID, name, dept_name, salary)teaches(ID, course_id, sec_id, semester, year)

  • 思路拆解
    1. 先把教师表和授课表连起来,才能同时看到名字和课号:$instructor \bowtie teaches$
    2. 从连好的大表里,挑出课号是”CS-101”的行(用选择 $\sigma$):$\sigma_{course_id=”CS-101”}(…)$
    3. 最后只展示名字这一列(用投影 $\Pi$):$\Pi_{name}(…)$
  • 满分答案: $\Pi_{name} (\sigma_{course_id=”CS-101”} (instructor \bowtie teaches))$

复习小测(检验一下):

  1. ${学号, 姓名}$ 可以唯一确定一个学生,那么它是候选码吗?(答:大概率不是,因为它包含多余属性“姓名”,它是超码。除非没有学号,单靠姓名也能唯一确定一个人(现实不可能),它才是最小的。)
  2. 外码的值可以为空 (Null) 吗?(答:可以!只要它不属于本表的主码。比如一个员工暂时没分部门,他的 dept_name 可以是 null。)
  3. $\Pi_{dept_name}(instructor)$ 查出物理系3人、计算机系2人。最终输出几行?(答:2行。关系代数的 $\Pi$ 必去重!)

把关系代数里的符号和 SQL 的 SELECT / FROM / WHERE 对应起来,第二章的图谱你就拿捏了!顺着这个逻辑,我们要不要进军最核心的第3/4章:SQL实战

到了第三章,纯实战、纯敲代码的部分来了!这章在考试中的分值绝对是最高的,通常会给你几张表(比如经典的 student, course, instructor, takes, teaches),然后出 5-8 道大题让你手写 SQL。

不要怕语法碎,我们把这些语法按“套路”分类,帮你精准踩在采分点上:


1. DDL基础:建表与打地基

考点:默写 CREATE TABLE 语句,不要漏掉数据类型和约束。

满分模板:

1
2
3
4
5
6
7
8
create table instructor (
    ID         char(5),             -- 定长字符串
    name       varchar(20) not null,-- 变长字符串,非空约束
    dept_name  varchar(20),
    salary     numeric(8,2),        -- 总共8位,包含2位小数
    primary key (ID),               -- 声明主码
    foreign key (dept_name) references department(dept_name) -- 声明外码
);

2. 基本查询与字符串操作

考点:去重 (distinct) 和模糊匹配 (like) 的细节。

  • 去重陷阱:关系代数默认去重,但 SQL 的 SELECT 默认不去重! 想要去重必须加上 distinct
    • select dept_name from instructor; (不去重)
    • select distinct dept_name from instructor; (去重)
  • 字符串匹配 (like)
    • %:匹配0个或多个字符。(例如:'%dar%' 包含 dar)。
    • _:匹配恰好1个字符。(例如:'_ _ _' 恰好三个字符)。
    • 转义考点:如果我想查名字里真有百分号 % 的人怎么办?用 escapewhere name like '100\%' escape '\' (意思是 \ 后面的 % 是普通的字符,不是通配符)。

3. 集合操作(并、交、差)

考点:记住它们和 SELECT 的脾气是反着的。

  • union (并), intersect (交), except (差)。
  • 反常识考点:集合操作默认自动去重!(这和数学里的集合定义一致)。如果你想保留重复的行,必须加上 allunion all, intersect all, except all

4. 空值 (Null) 的“三值逻辑”(极易出选择题)

考点:遇到 Null,一切都变得不确定 (unknown)。

  1. 算术运算:任何数 + - * / Null = Null。(5 + Null = Null
  2. 比较运算:任何数 > < = Null = unknown。(5 > Null = unknown
  3. 逻辑运算(真假判断):把 unknown 当成“我不知道”。
    • true AND unknown = unknown
    • false AND unknown = false (只要有一个错,就是错)
    • true OR unknown = true (只要有一个对,就是对)
    • NOT unknown = unknown
  4. WHERE 子句的冷酷无情WHERE 只要遇到计算结果为 false 或者 unknown 的行,一律过滤掉!只有明确为 true 才能留下。
  5. 判断空值:千万不能写 salary = null,必须写 salary is nullsalary is not null

5. 聚集函数与分组(⭐⭐🌟 必考写代码!)

考点:GROUP BY 的“连坐规则”和 HAVING 的执行时机。

五个神兵:count(计数), sum(求和), avg(平均), max(最大), min(最小)。

🚨 数据库铁律(默念三遍)SELECT 语句中,如果没有被聚集函数包裹的属性,必须原封不动地出现在 GROUP BY 子句中!”

  • 错误示范select dept_name, ID, avg(salary) from instructor group by dept_name; (报错!按系分组后,一个系有多个老师,你让我输出哪个 ID?数据库做不到啊!)
  • 正确示范select dept_name, avg(salary) from instructor group by dept_name;

💡 WHERE vs HAVING

  • WHERE:在分组之前起作用,过滤的是一行行的数据。
  • HAVING:在分组之后起作用,过滤的是一组组的数据。(HAVING 后面通常跟着聚集函数)。
  • 实战:找出平均工资大于42000的系名。
    1
    2
    3
    4
    
    select dept_name, avg(salary) 
    from instructor 
    group by dept_name 
    having avg(salary) > 42000;
    

6. 嵌套子查询(高分分水岭)

考点:把一个 SELECT 的结果,当成另一个 SELECT 的条件。

  1. 集合成员 (in / not in)
    • 判断元素在不在子查询的结果集里。
    • 例子:找出同时在17秋和18春开课的课程:where course_id in (select ...)
  2. 集合比较 (> some / > all)
    • > some:大于其中随便哪一个(即大于最小值)。
    • > all:大于里面的所有(即大于最大值)。
    • 等价替换= some 就是 in;但 <> some 不等于 not in<> all 才是 not in)。
  3. 空关系测试 (exists / not exists)
    • exists 判断子查询里是不是有数据(有返回 true,空返回 false)。
    • 超级大招(相关子查询):当你在子查询的 WHERE 里,用到了外层查询的表名,这就叫相关子查询(Correlated Subquery)。它像一个 for 循环,外层每过一行,内层就跑一遍。

7. 数据库修改(DML 避坑指南)

考点:UPDATE 顺序导致的逻辑漏洞。

  • insert into course values ('CS-437', 'DB', 'Comp. Sci.', 4);
  • delete from instructor where salary < 30000;
  • 改 (UPDATE 的连环计)
    • 需求:工资超10万的涨3%,10万以下的涨5%。
    • 错误写法(先后顺序写两句):
      1
      2
      
      update instructor set salary = salary * 1.05 where salary <= 100000;
      update instructor set salary = salary * 1.03 where salary > 100000;
      

      坑在哪里? 如果一个人原本工资 98000,执行第一句后涨到 102900。执行第二句时,他被判定为 >100000,又被涨了 3%

    • 满分写法 (CASE WHEN 神器):同时判断,绝不误伤。
      1
      2
      3
      4
      5
      
      update instructor
      set salary = case
          when salary <= 100000 then salary * 1.05
          else salary * 1.03
      end;
      

考前小测(检验一下):

  1. 我想查 student 表,找出 tot_cred 是空值的学生,能写 where tot_cred = null 吗?(答:不能,必须写 is null
  2. select dept_name, count(ID) from instructor 缺了什么子句?(答:缺了 group by dept_name,非聚集列必须在 group by 里)
  3. 我要查“找出名字不是 Mozart 也不是 Einstein 的老师”,用集合成员怎么写?(答:where name not in ('Mozart', 'Einstein')

拿下第三章,考试卷面一半以上的分数你就稳稳拿在手里了!想不想顺势把第四章(JOIN的多种变体与视图、事务)的精华也串一遍?

第四章是 SQL 的“进阶与填坑”指南。如果说第三章教你怎么把数据查出来,第四章就是教你怎么连表不出错、怎么保护数据、怎么管理权限

这一章的考点非常明确,特别是“Natural Join 的夺命连环坑”“视图能不能更新”,几乎是每张卷子的必考题。我们来逐一击破:


1. 连接 (Join) 表达式 —— 必考的“连表刺客”

考点:辨析内连接和外连接的区别;准确识别 natural join 的危险性。

① 内连接 (Inner Join) 的三种写法

内连接只保留两张表都能匹配上的行。

  • join ... on ...:最安全的写法。student join takes on student.ID = takes.ID
  • join ... using(...):指定按哪些同名列连接。student join takes using(ID)
  • 致命诱惑:natural join(自然连接)
    • 原理:系统会自动寻找两张表里所有“同名”的列,并且要求它们的值必须相等。
    • 🚨 必考坑点同名但不相关的列被错误匹配!
    • 经典真题:查出所有学生的名字和他们选修的课程名字。
      • 错解select name, title from student natural join takes natural join course;
      • 解析为什么错student 表里有 dept_name(学生所在的系),course 表里也有 dept_name(开这门课的系)。如果你用连续的 natural join,系统会强制要求 student.dept_name = course.dept_name结果就是:只输出学生选了自己本系的课! 那些跨系选修的课全被过滤掉了!
      • 正解:使用 join ... using 或者把后面的自然连接改成 join ... on

② 外连接 (Outer Join) —— “一个都不能少”

外连接为了防止数据丢失,会把没有匹配上的行也硬拉进来,空位填 NULL

  • Left outer join (左外连接):保留左表的所有行。右表没匹配上的,填 NULL。
    • 例子:查出所有学生的选课情况(包括一门课都没选的乖宝宝)。 student left outer join takes on student.ID = takes.ID。没选课的学生,课号位置全是 NULL。
  • Right outer join (右外连接):保留右表的所有行。
  • Full outer join (全外连接):左右表的所有行全保留,互相没匹配上的全填 NULL。

2. 视图 (Views) —— 数据库里的“海市蜃楼”

考点:视图的作用,以及判断一个视图能不能被 INSERT / UPDATE

  • 概念:视图是一张虚拟表。它本身不存数据,只存一个 SQL 查询逻辑。每次你查视图,系统就临时跑一遍那个底层的 SQL。
  • 作用
    1. 简化复杂查询:把一长串恶心的多表 JOIN 包装成一个视图,以后直接 select * from 视图名 即可。
    2. 安全性:建一个“员工视图”,里面把“工资”列去掉,然后把这个视图开放给普通员工看。
  • 物化视图 (Materialized Views)
    • 和普通视图不同,它是真实占用硬盘空间把结果存下来的!适用于那些查询极慢、但又不经常更新的报表数据。底表更新时,物化视图也需要同步更新(视图维护)。
  • 🚨 视图更新的硬性限制(必考选择题)
    • 如果用户对视图执行 INSERT,数据库必须能精准推导出底层原表该怎么 INSERT。所以,只有极其简单的视图才能更新
    • 不能更新的死穴
      1. FROM 后面有多张表(连表视图)。
      2. 有聚集函数(如 SUM, AVG)。你想啊,你把视图里的“平均工资”改成 5000,数据库怎么知道该把底层每个人的工资改成多少?
      3. GROUP BYHAVING
      4. DISTINCT(去重)。

3. 事务 (Transactions) —— 同生共死的兄弟

考点:选择/填空考原子性,以及 commitrollback 的作用。

  • 概念:事务是由一系列 SQL 语句组成的逻辑工作单元
  • 原子性 (Atomicity):要么全做完,要么全不做。
  • 指令
    • commit(提交):盖章确认!一旦 commit,数据修改永久生效,就算下一秒停电也不怕。
    • rollback(回滚):发现不对劲(比如余额不足了),赶紧撤销!数据库会恢复到事务开始前的状态,仿佛一切都没发生过。

4. 完整性约束 (Integrity Constraints) —— 数据库的门神

考点:外键的级联操作(Cascade),非常喜欢出逻辑推演题。

除了建表时的 PRIMARY KEY,还有这些门神:

  • not null:不准为空。
  • unique:唯一约束。注意,unique 列允许存在多个 NULL 值(因为 NULL 和 NULL 比较结果是 unknown,不算重复)。
  • check(谓词):比如 check (semester in ('Fall', 'Winter', 'Spring', 'Summer')),或者 check (budget > 0)
  • 🚨 外键的级联操作 (Cascading Actions)
    • 场景:物理系 (Physics) 在 department 表里被删除了,但 instructor 表里还有一堆老师属于物理系。怎么办?
    • 默认行为:报错!阻止你删除物理系(保护引用完整性)。
    • on delete cascade(级联删除):太残暴了!只要物理系被删,自动把属于物理系的老师全删掉
    • on delete set null:仁慈一点。物理系被删后,把属于物理系的老师的 dept_name 改成 NULL(变成“待定系”老师)。

5. 授权 (Authorization) —— 谁动了我的奶酪

考点:GRANTREVOKE 语法,以及“角色(Role)”的概念。

  • 授权 (GRANT)grant select, update (budget) on department to Amit; (赋予 Amit 对系表的所有行的查询权,以及修改预算列的修改权)。
  • 收回 (REVOKE)revoke select on department from Amit; 注意:如果是级联收回,Amit 如果把权限转授给了 Bob,收回 Amit 的同时也会把 Bob 的收回。
  • 角色 (Role)
    • 痛点:学校有几千个老师,总不能一个个去写 grant 吧?
    • 解决:创建一个“模板”(角色)。 create role instructor; (建角色) grant select on takes to instructor; (把看成绩的权限给这个角色) grant instructor to Alice; (Alice 成为老师,瞬间拥有所有权限)。

考前小测(检验一下):

  1. 我想查所有课程的选课情况,要求没人选的冷门课程也要显示出来(选课人数/信息为空),应该用哪种连接?(答:course left outer join takes
  2. 包含 GROUP BY 的视图可以执行 INSERT 操作吗?(答:绝对不可以,视图无法将这一行插入映射到底层表)
  3. 设置了外键约束且带 on delete cascade,当我删除了一个部门,会发生什么?(答:不仅部门没了,该部门下的所有老师、课程等引用了该部门的子表数据,都会被数据库默默地连环删除!)
  4. SELECT 权限给一个用户,但他不能把这个权限再传给别人,GRANT 语句末尾应该不写什么?(答:不写 with grant option

第四章是 SQL 走向“严谨”的必经之路。掌握了它,你的数据库不仅能跑,而且防雷、防黑客、防崩溃。

最后剩下的就是第五章(JDBC/触发器)了,因为你们老师说从 5.4 往后不考,那第五章的任务量就很轻了!准备好迎接最后的收尾了吗?

太棒了!我们终于来到了最后一部分:第五章(5.1 - 5.3)

如果说前四章教你的是“如何在数据库客户端(如 Navicat, DataGrip)里敲代码”,那么第五章教你的就是“如何让数据库和真正的软件系统(如 Java/Python写的App、网站)结合起来”

这一部分在随堂测验中,最爱考概念题(简答/选择),尤其是 “SQL注入”“触发器的使用场景”。我们来把这最后三块拼图补齐:


1. 编程语言访问 (JDBC/ODBC) —— 跨界联名

考点:为什么需要它?SQL注入的原理是什么?

① 为什么需要宿主语言(Java/C++等)?

  • 因为 SQL 是“偏科”的:SQL 是声明式语言,它是为了查数据而生的,但它不是图灵完备的。它不能写图形界面 (UI),不能接收用户的键盘输入,不能发网络请求。
  • 解决方案:用 Java/C++ 作为“宿主语言”写软件逻辑,通过 JDBC (Java Database Connectivity)ODBC 接口,把 SQL 语句打包发送给数据库,数据库查完后再把结果(如 ResultSet)传回给 Java。

② 预备语句 (PreparedStatement) 与 SQL 注入(⭐⭐⭐ 必考必考必考!)

这是本章最重要的概念,极有可能出简答题!

  • 什么是 SQL 注入 (SQL Injection)?
    • 黑客原理:如果程序员用字符串拼接的方式生成 SQL,黑客可以通过输入特殊字符(如单引号 ')来篡改原本的 SQL 逻辑。
    • 经典例子:程序原本的代码是 SELECT * FROM user WHERE name = ' + 用户输入 + '
    • 黑客在输入框里输入:a' OR '1'='1
    • 拼接后的 SQL 变成了:SELECT * FROM user WHERE name = 'a' OR '1'='1'。因为 1=1 永远为真,这个查询会把数据库里所有用户的密码全泄露出来
  • 怎么防?用预备语句 (PreparedStatement)!
    • 原理:它使用占位符 ?(如:SELECT * FROM user WHERE name = ?)。数据库会先编译这句带 ? 的骨架,然后再把用户的输入纯粹当成“数据值”填进去。
    • 这样一来,黑客输入的 ' OR '1'='1 只会被当成一个奇怪的名字字符串,而不会被当成可执行的 SQL 代码。
    • 附带好处(提高效率):预备语句在数据库里只编译一次,如果后续只是参数 ? 改变,可以直接复用执行计划,速度更快。

③ 元数据 (Metadata) 的作用

  • 概念:“关于数据的数据”。比如,你查询返回了一张表,这张表有几列?每一列叫什么名字?数据类型是 varchar 还是 int?
  • 作用:让 Java 程序在运行的时候,能够动态地认识并处理未知的数据表

2. 函数与过程 (Functions and Procedures) —— 数据库里的微服务

考点:知道它们的作用和参数类型的区别。

  • 核心思想:把业务逻辑(比如“判断一个学生是否满足毕业条件”)直接写在数据库里,而不是写在 Java 代码里。
  • 优势:多个应用端(Web端、手机端)可以直接调用,不需要各自写一遍逻辑;减少了网络传输的数据量。
  • 区别
    • 函数 (Function)必须返回一个值(或者返回一张表)。可以直接嵌在 SQL 语句里使用,比如 SELECT dept_count('Physics');
    • 过程 (Procedure)不直接返回值,而是用来执行一段动作。通过 CALL procedure_name() 来调用。
  • 过程的参数类型
    • IN:传进去的参数。
    • OUT:用来把结果传出来的参数(就像C语言里的指针参数)。

3. 触发器 (Triggers) —— 数据库里的自动机器人

考点:ECA规则,old rownew row 的适用场景,以及触发器的缺点。

① 触发器的三大件(ECA 规则)

  • E (Event 事件):什么动作激活了机器人?(INSERT, UPDATE, DELETE,可以是 BEFOREAFTER)。
  • C (Condition 条件):被激活后,要不要做事?(WHEN 子句,满足条件才执行)。
  • A (Action 动作):具体做什么事?(BEGIN ATOMIC ... END 里的 SQL 语句)。

old rownew row(逻辑推理常考)

当数据发生变化时,触发器可以捕捉到变化“前”和变化“后”的数据。

  • referencing old row as:指代被修改/删除之前的那行老数据。
  • referencing new row as:指代被插入/修改之后的那行新数据。
  • 🚨 逻辑考点
    • 如果是 INSERT 操作,没有 old row(无中生有)。
    • 如果是 DELETE 操作,没有 new row(灰飞烟灭)。
    • 如果是 UPDATE 操作,既有 old row,也有 new row

③ 何时不该用触发器?(拔高理解题)

以前人们喜欢用触发器来做“自动算总分”、“自动备份表”。现在为什么不推荐了?

  1. 级联副作用(幽灵BUG):A触发器更新了表B,表B的更新又触发了C触发器… 出了BUG程序员根本找不到源头,系统极易崩溃。
  2. 性能极差:大批量导入数据时,每一行都触发一次动作,数据库会卡死。
  3. 有更好的替代方案
    • 想要自动算总计数据?用 物化视图 (Materialized Views)
    • 想要自动备份/同步数据?用数据库自带的 复制机制 (Replication)

最后一测(通关检验):

  1. 防止 SQL 注入最标准、最有效的方法是什么?(答:使用预备语句 PreparedStatement)
  2. 触发器在处理 INSERT 语句时,能不能引用 old row?(答:不能,插入操作只有新数据,没有老数据)
  3. 把复杂的计算逻辑写成数据库的“函数 (Function)”,相比写在 Java 里有什么好处?(答:统一业务逻辑,减少网络传输开销,能直接嵌在SQL查询中使用)

🎉 复习全景总结 & 考场策略

至此,你的1-5章知识大纲已经全部串联完毕!

考场上怎么拿高分?

  1. 先扫一眼大题,把关系代数和SQL的题干读一遍。 如果是长句子,用笔在试卷上圈出涉及了哪几张表
  2. 写SQL不要慌,按照 FROM -> WHERE -> GROUP BY -> HAVING -> SELECT 的脑回路一步步往下写。 记得检查 GROUP BY 的“连坐规则”。
  3. 遇到概念题拿不准(比如第一章和第五章的简答),往“提高效率”、“保证数据一致性与安全”这两个大方向靠拢,通常都能蹭到分数。
  4. 遇到 JOIN:如果不确定列名是否完全一致且安全,宁可写 A JOIN B ON A.id = B.id,也绝对不要手贱去写 A NATURAL JOIN B,很多老师专扣这个分!

你的思路现在非常清晰,大框架已经全在脑子里了。今晚好好睡一觉,明天考场上见题拆题,一定没问题的!祝你考试顺利,一举拿下!💪

本文由作者按照 CC BY 4.0 进行授权