数据库原理 · 期末复习全攻略
基于上课课件 + 三套期末试卷 + 上机操作文档 + PTA题库全面整理
一、关系数据库基础理论
1.1 基本概念
关系是一个二维表,由行(元组/Tuple)和列(属性/Attribute)组成。每个关系有一个关系名。
例如:Restaurant(rID, rName, rAddress, City) 表示餐厅信息关系。
基本性质(选择题考点):
- 列是同质的,每一列中的分量来自同一域
- 不同的列可出自同一个域,每列有唯一属性名
- 列的顺序无所谓,行的顺序无所谓
- 任意两个元组不能完全相同(候选码保证)
- 分量必须取原子值(满足1NF)
属性:关系中的每一列,给每个属性起一个名称即属性名。
域:属性的取值范围,如 VARCHAR(10)、INT、FLOAT 等。
候选码(Candidate Key):能唯一标识元组且不含多余属性的属性组。
主码(Primary Key):从候选码中选定一个作为关系的唯一标识。
外码(Foreign Key):一个关系中的属性,它在另一个关系中是主码,用于建立关系间联系。
主属性:包含在任何一个候选码中的属性。非主属性:不包含在任何候选码中的属性。
1.2 关系模式
关系模式是对关系的描述,形式化表示为:R(U, D, DOM, F),简化为 R(U, F)
其中 R 是关系名,U 是属性名集合,D 是域,DOM 是属性向域的映象,F 是属性组上的函数依赖集合。
关系模式是"型"(Schema),静态的、稳定的;关系是"值"(Instance),动态的,随数据增删改而变化。
1.3 完整性约束必考
主码的各个属性都不能取空值(NULL)。主码唯一标识元组,若为空则无法标识。
SQL中通过 PRIMARY KEY 约束实现。
外码的值要么全部为空,要么等于被参照关系中某个元组的主码值。
SQL中通过 FOREIGN KEY ... REFERENCES 约束实现。
针对具体应用的约束条件,如 CHECK 约束、NOT NULL、UNIQUE、DEFAULT。
例如:CHECK(price >= 0.0)、CHECK(budget > 0)、CHECK(vip=0 or vip=1)。
1.4 三级模式与数据独立性选择填空
- 外模式(用户模式):数据库用户能看见和使用的局部数据的逻辑结构和特征的描述。
- 模式(逻辑模式):数据库中全体数据的逻辑结构和特征的描述,是所有用户的公共数据视图。
- 内模式(存储模式):数据物理结构和存储方式的描述。
外模式/模式映象:保证逻辑数据独立性(模式改变时,修改映象使外模式不变)。
模式/内模式映象:保证物理数据独立性(内模式改变时,修改映象使模式不变)。
二、关系代数
2.1 运算分类
传统集合运算(水平方向,行的运算):并、差、交、广义笛卡尔积。要求参与运算的关系具有相同的目n,相应属性取自同一域。
专门的关系运算(涉及行和列):选择、投影、连接、除。
2.2 传统集合运算
| 运算 | 公式 | 含义 |
|---|---|---|
| 并 R∪S | R∪S={t|t∈R∨t∈S} | 属于R或属于S的元组 |
| 差 R-S | R-S={t|t∈R∧t∉S} | 属于R不属于S的元组 |
| 交 R∩S | R∩S=R-(R-S) | 既属于R又属于S的元组 |
| 笛卡尔积 R×S | R×S={trts|tr∈R∧ts∈S} | 列数n+m,行数k1×k2 |
2.3 专门的关系运算
σF(R) = {t | t∈R ∧ F(t)='真'}
从行的角度选择满足条件F的元组。F为逻辑表达式,由比较运算符和逻辑运算符组成。
例:σSdept='IS'(Student) 查询信息系学生。
πA(R) = {t[A] | t∈R}
从列的角度选取属性A,可能取消重复行。
例:πSname,Sdept(Student) 查询学生姓名和所在系。
等值连接:θ为"="的连接,结果保留重复列。
自然连接:特殊的等值连接,比较分量必须是相同属性组,结果去掉重复列,同时从行和列角度运算。
外连接:主体表中不满足条件的元组也输出,非主体表用空值填充。
- 左外连接
LEFT JOIN:左边表全部显示 - 右外连接
RIGHT JOIN:右边表全部显示
自身连接:一个表与自己连接,需给表起别名。
R÷S = {tr[X] | tr∈R ∧ πY(S) ⊆ Yx},其中 Yx 为 x 在 R 中的象集。
象集:Zx = {t[Z] | t∈R, t[X]=x},即 X 值为 x 时,对应的所有 Z 值集合。
解题步骤:①求各X值的象集 ②求S在Y上的投影 ③找象集包含πY(S)的X值。
2.4 综合例题
例1(除法):查询出现在所有餐厅的菜品信息,显示菜品编号、名称、单价:
πdID,dName,dPrice(Dish) ÷ πrID(Sales)
例2(选择+投影+自然连接):查询菜品名称包含"花菜"的菜品销售信息:
πdName,dPrice,rID,QTY(σdName LIKE '%花菜%'(Dish ⋈ Sales))
例3(绿色或黄色船只的预订信息):
πsid,bid(σcolor='绿' OR color='黄'(Boats ⋈ Reserves))
三、SQL语言完全指南
3.1 SQL概述选择填空
DDL(数据定义):CREATE / DROP / ALTER
DQL(查询):SELECT
DML(数据操作):INSERT / UPDATE / DELETE
DCL(数据控制):GRANT / REVOKE
特点:综合统一、高度非过程化、面向集合的操作方式、语言简洁易学。
3.2 数据定义(DDL)必考
CREATE TABLE Sales ( rID VARCHAR(18), dID VARCHAR(18), QTY INT CHECK(QTY >= 0), PRIMARY KEY (rID, dID), FOREIGN KEY (rID) REFERENCES Restaurant(rID), FOREIGN KEY (dID) REFERENCES Dish(dID) );
常用约束:
PRIMARY KEY— 主码约束(唯一且非空)UNIQUE— 唯一性约束(允许空值,一表可多个)NOT NULL— 非空约束FOREIGN KEY ... REFERENCES— 外码约束CHECK(条件)— 检查约束,如CHECK(price >= 0.0)DEFAULT— 默认值
ADD:增加新列或约束(新增列一律为空值)DROP:删除约束或列MODIFY:修改列的数据类型
CREATE [UNIQUE] [CLUSTER] INDEX 索引名 ON 表名(列名 [ASC|DESC]);
UNIQUE:唯一值索引,已有重复值不能建。
CLUSTER(聚簇索引):索引项顺序与表中记录物理顺序一致;每张表只能建一个;需要120%附加空间;适用于很少增删改的表。主键约束自动创建聚集索引。
升序ASC(缺省),降序DESC。
3.3 数据查询(DQL)必考
SELECT [ALL|DISTINCT] 目标列表达式[, ...] FROM 表名或视图名[, ...] [WHERE 条件表达式] [GROUP BY 列名 [HAVING 条件表达式]] [ORDER BY 列名 [ASC|DESC]];
SELECT:指定显示列(可用表达式、函数、列别名)
WHERE:分组前筛选行;HAVING:分组后筛选组
GROUP BY:分组;使用后SELECT子句只能出现分组属性和集函数
ORDER BY:排序,ASC升序(缺省),DESC降序
SELECT DISTINCT barcode FROM tradeDetail
DISTINCT作用于所有目标列,不能写成 SELECT DISTINCT CName, DISTINCT barcode(错误),应为 SELECT DISTINCT CName, barcode。
| 查询条件 | 谓词 |
|---|---|
| 比较 | =, >, <, >=, <=, !=, <> |
| 确定范围 | BETWEEN AND, NOT BETWEEN AND |
| 确定集合 | IN, NOT IN |
| 字符匹配 | LIKE, NOT LIKE(%任意长度, _单个字符) |
| 空值 | IS NULL, IS NOT NULL(不能用=NULL) |
| 多重条件 | AND, OR(AND优先级高于OR) |
LIKE通配符:%代表任意长度字符串,_代表任意单个字符。ESCAPE转义:LIKE '100\%纯棉' ESCAPE '\'
| 函数 | 功能 |
|---|---|
| COUNT([DISTINCT|ALL]*) / COUNT(列名) | 计数 |
| SUM(列名) | 求和 |
| AVG(列名) | 平均值 |
| MAX(列名) | 最大值 |
| MIN(列名) | 最小值 |
DISTINCT取消重复值,ALL不取消(缺省)。
GROUP BY:值相等的为一组,集函数分别作用于每组。
WHERE中不能用集函数,HAVING中可以。
例:查询销售数量大于100的商品:
SELECT barcode, SUM(quantity) AS total FROM tradeDetail GROUP BY barcode HAVING SUM(quantity) > 100;
3.4 连接查询
等值连接:连接运算符为=,保留重复列。
自然连接:去掉重复属性列的等值连接。
自身连接:一个表与自己连接,需起别名。
-- 自身连接:查询每门课的先修课名称 SELECT FIRST.Cno, SECOND.Cpno FROM Course FIRST, Course SECOND WHERE FIRST.Cpno = SECOND.Cno;
外连接:主体表中不满足条件的元组也输出。
-- 左外连接 SELECT Student.Sno, Sname, Cno, Grade FROM Student LEFT JOIN SC ON Student.Sno = SC.Sno;
复合条件连接(多表):
SELECT Student.Sno, Sname, Cname, Grade FROM Student, SC, Course WHERE Student.Sno = SC.Sno AND SC.Cno = Course.Cno;
3.5 嵌套查询必考
不相关子查询:子查询条件不依赖于父查询,由里向外逐层处理。
相关子查询:子查询条件依赖于父查询,外层取元组→处理内层→判断。
子查询不能使用ORDER BY。
-- 查询与刘晨同系的学生 SELECT Sno, Sname, Sdept FROM Student WHERE Sdept IN (SELECT Sdept FROM Student WHERE Sname = '刘晨');
| 谓词 | 含义 | 集函数等价 |
|---|---|---|
| > ANY | 大于子查询结果中某个值 | > MIN |
| > ALL | 大于子查询结果中所有值 | > MAX |
| < ANY | 小于某个值 | < MAX |
| < ALL | 小于所有值 | < MIN |
| = ANY | 等于某个值 | IN |
| <> ALL | 不等于任何值 | NOT IN |
EXISTS代表存在量词,子查询不返回数据,只返回逻辑真/假。内层查询结果非空→真值。
双重NOT EXISTS实现全称量词(必考题型):
SQL没有全称量词∀,转换公式:(∀x)P ≡ ¬(∃x(¬P))
例:查询选修了全部课程的学生:
SELECT Sname FROM Student WHERE NOT EXISTS ( SELECT * FROM Course WHERE NOT EXISTS ( SELECT * FROM SC WHERE Sno = Student.Sno AND Cno = Course.Cno ) );
逻辑蕴函:p→q ≡ ¬p∨q,进一步 (∀y)p→q ≡ ¬∃y(p∧¬q)
例:查询至少选修了95002学生选修的全部课程的学生——"不存在这样的课程y,95002选修了y而x没有选"。
3.6 集合查询
| 操作 | 说明 | 等价改写 |
|---|---|---|
| UNION(并) | 列数相同,对应类型相同;自动去重 | OR |
| INTERSECT(交) | MySQL不支持,用AND/IN间接实现 | AND |
| MINUS(差) | MySQL不支持,用NOT IN间接实现 | NOT IN |
ORDER BY只能出现在最后,可用数字指定排序属性:ORDER BY 1
3.7 数据更新(DML)必考
-- 插入单个元组 INSERT INTO 表名[(列名...)] VALUES(常量...); -- 插入子查询结果 INSERT INTO 表名[(列名...)] 子查询;
真题示例:INSERT INTO project VALUES('01', 'A');
UPDATE 表名 SET 列名=表达式[, ...] [WHERE 条件];
真题示例:将没有销售记录的菜品单价调整为原来的30%:
UPDATE Dish SET dPrice = dPrice * 0.3 WHERE dID NOT IN (SELECT DISTINCT dID FROM Sales);
真题示例:将年龄大于40的船员级别上调一级(不超过9级):
UPDATE Sailors SET rating = rating + 1 WHERE age > 40 AND rating + 1 <= 9;
真题示例:采购数量总和大于1000的商品VIP改为1:
UPDATE Product SET Vip = 1 WHERE Pno IN (SELECT Pno FROM SPBuy GROUP BY Pno HAVING SUM(inum) > 1000);
DELETE FROM 表名 [WHERE 条件];
真题示例:删除没有被预订过的船只信息:
DELETE FROM Boats WHERE bid NOT IN (SELECT DISTINCT bid FROM Reserves);
真题示例:删除单价超过500的菜品信息:
DELETE FROM Dish WHERE dPrice > 500;
3.8 视图必考
CREATE VIEW 视图名[(列名...)] AS 子查询 [WITH CHECK OPTION];
视图是虚表,只存放定义,不存数据。基表数据变化,视图查询结果随之改变。
WITH CHECK OPTION:通过视图增删改时不得破坏视图定义中的谓词条件。
真题示例:创建水手级别统计视图:
CREATE VIEW VWS AS SELECT rating, COUNT(*) AS sCount, AVG(age) AS avgAge FROM Sailors GROUP BY rating;
真题示例:创建销售总数量在100到500之间的餐厅数量视图:
CREATE VIEW VWS AS SELECT City, COUNT(*) AS rCount FROM Restaurant r JOIN Sales s ON r.rID = s.rID GROUP BY City, r.rID HAVING SUM(QTY) > 100 AND SUM(QTY) < 500;
不可更新视图:字段来自集函数、含GROUP BY、含DISTINCT、含嵌套查询涉及基表等。
- 简化用户操作
- 使用户以多种角度看待同一数据
- 对重构数据库提供一定程度的逻辑独立性
- 对机密数据提供安全保护
3.9 数据控制(DCL)必考
GRANT 权限[, 权限]... [ON 对象类型 对象名] TO 用户[, 用户]... [WITH GRANT OPTION];
权限:SELECT、INSERT、UPDATE、DELETE、ALL PRIVILEGES等
WITH GRANT OPTION:允许获得权限的用户再授权给其他用户(权限传播)。
真题示例:将餐厅信息表的查询权限赋给所有用户:
GRANT SELECT ON Restaurant TO PUBLIC;
真题示例:将水手信息表的age属性修改权限赋给用户U2:
GRANT UPDATE(age) ON Sailors TO U2;
REVOKE 权限[, 权限]... [ON 对象类型 对象名] FROM 用户[, 用户]...;
真题示例:将对表S操作的所有权限从用户UA中收回:
REVOKE ALL PRIVILEGES ON TABLE S FROM UA;
四、函数依赖与范式理论
4.1 函数依赖必考
设 R(U) 是属性集 U 上的关系模式,X 和 Y 是 U 的子集。若对于 R(U) 的任意关系 r,r 中不可能存在两个元组在 X 上的属性值相等而在 Y 上的属性值不等,则称 Y 函数依赖于 X,记作 X → Y。
简言之:若 X 的值确定,则 Y 的值也唯一确定。
平凡/非平凡函数依赖:Y⊆X为平凡(必然成立),Y⊈X为非平凡(通常讨论的)。
完全函数依赖 X ─F→ Y:X→Y 且 X 的任何真子集 X' 都不能决定 Y。
部分函数依赖 X ─P→ Y:X→Y 但存在 X 的真子集也能决定 Y。
传递函数依赖:X→Y,Y→Z(且Y↛X),则 Z 传递依赖于 X。若 Y→X(即 X↔Y),则 Z 直接依赖于 X。
"每个工程的地址、动工日期、竣工日期唯一" → 工程号 → 工程名, 动工日期, 竣工日期
"每种材料可应用于若干工程" → 材料号 → 材料名称
"每个工程使用若干材料" → (工程号, 材料号) → 使用数量
4.2 码的求解方法必考
第一步:只在函数依赖集左部出现的属性 → 必属于候选码(L类)
第二步:只在右部出现的属性 → 必不属于任何候选码(R类)
第三步:左右都出现的(LR类)和两边都没出现的(N类)→ 可能属于候选码
第四步:从 L 类出发,逐步加入 LR 类或 N 类属性,求闭包。若闭包=U,则为候选码。
属性集 X 的闭包 X⁺ 是通过 F 能从 X 推导出的所有属性集合。
若 X⁺ = U,则 X 是超码;若 X 的任何真子集的闭包都不等于 U,则 X 是候选码。
例:R(A, B, C, D, E),F = {AB→C, B→D, C→E}
L类:A(只在左部);R类:E(只在右部);LR类:B, C;N类:D
求 (AB)⁺:AB→C→E,B→D,所以 (AB)⁺ = {A,B,C,D,E} = U
去掉A:B⁺ = {B,D} ≠ U;去掉B:A⁺ = {A} ≠ U
因此 主码 = AB。
4.3 范式理论核心必考
5NF ⊃ 4NF ⊃ BCNF ⊃ 3NF ⊃ 2NF ⊃ 1NF
关系中每个属性都是不可再分的原子值。所有关系模式默认满足1NF。
R∈1NF,且每个非主属性都完全函数依赖于码(无非主属性对码的部分函数依赖)。
判断:若存在非主属性对码的部分依赖 → 不属于2NF。
例:码=AB,B→D是部分依赖(B是AB的子集),不属于2NF。
R∈2NF,且不存在非主属性对码的传递函数依赖。
等价定义:若 X→Y(非平凡),则 X 必须是超码,或 Y 是主属性。
例:AB→C, C→E,E 传递依赖于 AB,不属于3NF。
R∈1NF,且对于每个函数依赖 X→Y(Y⊈X),X 必含候选码(每个决定因素都包含码)。
BCNF 是 3NF 的加强版,消除了主属性对码的部分和传递依赖。
关系:BCNF → 3NF → 2NF → 1NF(反方向不成立)。若 R∈3NF 且只有一个候选码,则 R∈BCNF。
- 确认满足 1NF(通常默认满足)
- 检查是否存在部分函数依赖(非主属性依赖于码的子集)→ 若有,最高 1NF
- 检查是否存在传递函数依赖(非主属性通过中间属性依赖于码)→ 若有,最高 2NF
- 若无以上问题 → 属于 3NF
- 检查主属性是否有部分/传递依赖 → 若无,属于 BCNF
4.4 规范化分解必考
原则:找出非平凡函数依赖 X→Y(X不是超码),将 Y 及其依赖属性分离,与 X 组成新关系。
例:R(A,B,C,D,E),F = {AB→C, B→D, C→E},码=AB
分解结果:
R1(A, B, C)— 主码:ABR2(B, D)— 主码:BR3(C, E)— 主码:C
原模式:R(工程号, 工程名, 动工日期, 竣工日期, 材料号, 材料名称, 使用数量)
函数依赖:工程号→工程名,动工日期,竣工日期;材料号→材料名称;(工程号,材料号)→使用数量
主码:(工程号, 材料号) 最高范式:1NF(存在部分函数依赖)
分解到3NF:
R1(工程号, 工程名, 动工日期, 竣工日期)— 主码:工程号R2(材料号, 材料名称)— 主码:材料号R3(工程号, 材料号, 使用数量)— 主码:(工程号,材料号),外码:工程号、材料号
无损连接性:分解后自然连接结果与分解前相同(不丢信息)。
保持函数依赖:分解后函数依赖仍保持。两者可同时满足或分别满足。
4.5 不好的关系模式的问题
- 数据冗余:相同数据重复存储
- 更新异常:更新时维护完整性代价大
- 插入异常:该插的数据插不进去(如新系无学生)
- 删除异常:不该删的数据被删(如学生毕业删除系信息)
原因:不合适的数据依赖引起。解决:分解关系模式(规范化)。
4.6 PTA易错判断与选择题精选新增
- 任何二目关系(两个属性)一定属于BCNF(也满足3NF)—— 正确
- 范式的主要目的是提高查询效率 —— 错误,目的是消除数据冗余和异常
- 若 R.A→R.B,R.B→R.C,则 R.A→R.C(传递律)—— 正确
- 函数依赖集F的最小函数依赖集Fm不唯一 —— 正确
- 若X→Y和Y→Z,且X、Y、Z为互不相同的单属性,则不存在X到Z的传递函数依赖 —— 正确
- 规范化的主要目的:维护数据的一致性(消除插入异常、删除异常、数据冗余)
- 包含两个属性的关系模式一定满足BCNF
- 部门关系中"部门成员"属性可能使它不满足1NF(因部门成员可再分)
- 平凡函数依赖:AB→A(Y是X的子集)
- 若所有属性都是主属性,则R至少属于3NF
- R(A,B,C,D)无函数依赖时,所有属性都是候选码(都是主属性)
- 候选码求解:R(A,B,C,D,E,F,G),F={A→B, C→D, C→F, (A,D)→E, (E,F)→G},候选码为 (A,C)
4.7 PTA关系模式分解大题精选新增
R(工程号, 材料, 数量, 价格, 开工日期, 完工日期)
函数依赖:(工程号,材料)→(数量,价格);工程号→(开工日期,完工日期)
候选码:(工程号,材料) 范式:1NF(部分依赖:工程号→开工日期)
3NF分解:R1(工程号,开工日期,完工日期);R2(工程号,材料,数量,价格)
R(学号,姓名,性别,专业,年级,课号,课名,学分,学时,工资号,教师,成绩)
函数依赖:学号→(姓名,性别,专业,年级);课号→(课名,学分,学时,工资号);(学号,课号)→成绩;工资号→教师
候选码:(学号,课号) 范式:1NF
3NF分解:学生(学号,姓名,性别,专业,年级);课程(课号,课名,学分,学时,工资号);教师(工资号,教师);成绩(学号,课号,成绩)
R(职工号,日期,日营业额,部门名,部门经理)
函数依赖:(职工号,日期)→日营业额;职工号→部门名;部门名→部门经理
候选码:(职工号,日期) 范式:1NF(传递依赖:职工号→部门名→部门经理)
3NF分解:R1(职工号,部门名);R2(部门名,部门经理);R3(职工号,日期,日营业额)
R(读者号,姓名,性别,住址,年龄,图书号,书名,作者,出版社,借出日期,归还日期)
函数依赖:读者号→(姓名,性别,住址,年龄);图书号→(书名,作者,出版社);(读者号,图书号)→(借出日期,归还日期)
候选码:(读者号,图书号) 范式:1NF
3NF分解:读者(读者号,姓名,性别,住址,年龄);图书(图书号,书名,作者,出版社);借阅(读者号,图书号,借出日期,归还日期)
R(竞赛编号,竞赛名称,竞赛组织者,竞赛开始日期,学号,学生姓名,获奖等级)
函数依赖:竞赛编号→(竞赛名称,竞赛组织者,竞赛开始日期);学号→学生姓名;(竞赛编号,学号)→获奖等级
候选码:(竞赛编号,学号) 范式:1NF
3NF分解:竞赛(竞赛编号,竞赛名称,竞赛组织者,竞赛开始日期);学生(学号,学生姓名);参赛信息(竞赛编号,学号,获奖等级)
R(教师号,姓名,部门号,部门名称,科研项目编号,项目名称,项目经费,担任工作,完成时间)
函数依赖:教师号→(姓名,部门号);部门号→部门名称;科研项目编号→(项目名称,项目经费);(教师号,科研项目编号)→(担任工作,完成时间)
候选码:(教师号,科研项目编号) 范式:1NF
3NF分解:教师(教师号,姓名,部门号);部门(部门号,部门名称);科研项目(科研项目编号,项目名称,项目经费);教师科研情况(教师号,科研项目编号,担任工作,完成时间)
R(商店编号,商品编号,数量,部门编号,负责人)
函数依赖:(商店编号,商品编号)→部门编号;(商店编号,部门编号)→负责人;(商店编号,商品编号)→数量
候选码:(商店编号,商品编号) 范式:2NF(无部分依赖,有传递依赖)
3NF分解:R1(商店编号,商品编号,数量,部门编号);R2(商店编号,部门编号,负责人)
R(SNO, CNO, GRADE, TeaNO, TAddress)
函数依赖:(SNO,CNO)→GRADE;CNO→TeaNO;TeaNO→TAddress
候选码:(SNO,CNO) 范式:1NF(部分依赖+传递依赖)
3NF分解:R1(TeaNO,TAddress);R2(CNO,TeaNO);R3(SNO,CNO,GRADE)
R(司机编号,汽车牌照,行驶公里,车队编号,车队主管)
函数依赖:司机编号→车队编号;车队编号→车队主管;(司机编号,汽车牌照)→行驶公里
候选码:(司机编号,汽车牌照) 范式:1NF
3NF分解:R1(司机编号,车队编号);R2(车队编号,车队主管);R3(司机编号,汽车牌照,行驶公里)
- 求函数依赖集F的最小覆盖
- 求候选码(属性分类法:L类只在左部、R类只在右部、N类两边都没出现)
- 判断范式等级:检查部分依赖和传递依赖
- 3NF分解:将F中每个函数依赖 X→A 组成一个关系模式 (X,A),合并左部相同的关系
五、数据库设计(E-R模型)
5.1 E-R模型基本要素必考
实体(Entity):客观存在并可相互区别的事物。用矩形表示。
属性(Attribute):实体所具有的特性。用椭圆表示。
联系(Relationship):实体之间的关联。用菱形表示。
| 类型 | 含义 | 转换规则 |
|---|---|---|
| 1:1 | 一对一 | 任一方加入对方主码为外码 |
| 1:N | 一对多 | N端加入1端主码为外码 |
| M:N | 多对多 | 新建关系,主码为两端主码组合 |
5.2 E-R图转关系模式必考
- 实体 → 一个关系模式,实体的属性即为关系属性,实体的码即为关系码
- 1:1联系 → 任一方加入对方主码为外码,并加入联系属性
- 1:N联系 → N端加入1端主码为外码,并加入联系属性
- M:N联系 → 新建关系,主码为两端实体主码的组合,并加入联系属性
运动队(队名, 主教练) — 运动员(编号, 姓名, 性别, 年龄) — 项目(编号, 项目名, 类别)
联系:运动队1:N运动员;运动员M:N项目(记录名次、成绩、日期)
转换结果:
运动队(队名, 主教练)— 主码:队名运动员(编号, 姓名, 性别, 年龄, 队名)— 主码:编号,外码:队名项目(编号, 项目名, 类别)— 主码:编号参赛(编号, 项目编号, 名次, 成绩, 日期)— 主码:(编号,项目编号),外码:编号、项目编号
5.3 PTA选择题高频考点新增
- E-R图图形含义:实体=矩形,属性=椭圆形,联系=菱形
- E-R图用于建立概念模型,不依赖于计算机硬件和DBMS
- 概念结构设计最常采用的策略是自底向上的方法(非自顶向下)
- 描述概念模型的有力工具是E-R图(不是数据字典)
- 合并E-R图属于概念结构设计阶段
- 合并局部E-R图的冲突:属性冲突、命名冲突、结构冲突(无"语法冲突")
- 物理设计阶段形成的是内模式(不是外模式)
- 建立索引属于物理设计阶段
- 逻辑结构设计判断是否合理的依据是规范化理论
- 职员到部门是多对一联系
- 机票与座位号是一对一联系
- 学生选课是多对多联系
- M:N联系转换时,码是M端与N端实体码的组合
- 1:N联系转换为关系模式时,码是N端实体的码
- 两个不同实体集+多对多联系→最少3个关系模式
- 3个实体型+3个M:N联系→转换为6个关系(3实体+3联系)
PTA平台包含以下E-R图设计大题(每题5-18分),需要掌握画图方法:
| 系统名称 | 核心实体 | 关键联系 |
|---|---|---|
| 车辆管理系统 | 部门、车队、汽车、司机 | 多对多+一对多混合 |
| 人事管理系统 | 部门、岗位、职工 | 培训、考核(M:N) |
| 医院病房管理系统 | 科室、病房、医生、病人 | 1科室对多病房/多医生 |
| 旅行社管理系统 | 景点、线路、导游、团队 | 多对多+一对多 |
| 产品-零件-材料系统 | 产品、零件、材料 | 产品与零件M:N,零件与材料N:1 |
| 商业集团销售系统 | 商店、商品、职工 | 销售为M:N,聘用为1:N |
| 企业集团生产系统 | 工厂、产品、职工 | 生产为M:N,聘用为1:N |
| 图书借阅系统 | 图书、出版社、读者 | 出版社1:N图书,读者M:N图书 |
| 班级-运动员-比赛项目 | 班级、运动员、项目 | 班级1:N运动员,运动员M:N项目 |
5.4 数据库设计步骤
- 需求分析:调查用户需求,产出数据字典、数据流图
- 概念结构设计:设计E-R模型(独立于DBMS)
- 逻辑结构设计:E-R图转关系模式,规范化
- 物理结构设计:选择存储结构和存取方法
- 数据库实施:建库、建表、录入数据
- 数据库运行和维护
六、查询优化
6.1 查询优化必要性选择/简答
不同的查询执行策略效率差异巨大。例如查询销售单1的商品名称和数量:
- 策略1(先笛卡尔积):1000×10000=10⁷中间结果 → 选择 → 投影
- 策略2(先选择):先选择orderId=1(10条)→ 自然连接 → 投影
策略2快得多。核心思想:减少中间结果。
6.2 查询优化准则必考
- 选择运算尽可能先做(最重要,减小中间关系)
- 投影运算和选择运算同时做(避免重复扫描)
- 将投影运算与前面或后面的双目运算结合(减少扫描遍数)
- 执行连接操作前对关系适当预处理(按连接属性排序/建索引)
6.3 语法树优化必考
- 把查询转换成内部表示(语法树)
- 代数优化:把语法树转换成标准(优化)形式
- 物理优化:选择低层存取路径
- 生成查询计划,选择代价最小的
口诀:选择下推、投影提前、连接最后。
关系代数表达式:π水手编号,船只编号(σcolor='绿' OR color='黄'(Boats ⋈ Reserves))
优化前语法树:投影在顶部 → 选择 → 连接(Boats × Reserves)
优化后:将选择条件下推到Boats表上 → 先选择再连接 → 最后投影
优化后:πsid,bid( (σcolor='绿' OR color='黄'(Boats)) ⋈ Reserves )
6.4 关系系统分类选择
- 表式系统:仅支持表结构
- (最小)关系系统:表 + 选择、投影、连接
- 关系完备系统:表 + 所有关系代数操作
- 全关系系统:支持关系模型所有特征
6.5 PTA索引与查询优化考点新增
- 建立索引是为了提高检索速度 —— 正确
- 连接运算尽可能先做是查询优化最重要最基本的策略 —— 正确
- 查询优化策略由DBMS自动选择,不是由用户确定 —— 错误,用户也可以指定
- 索引不是越多越好 —— 索引过多会影响增删改效率
- 每行索引记录包含指向表中数据页的逻辑指针 —— 正确
- 索引属于内模式
- 一个表最多1个聚集索引,可有多个非聚集索引
- 建立唯一性索引:
CREATE UNIQUE INDEX 索引名 ON 表名(属性名) CREATE UNIQUE INDEX IDX1 ON T(C1,C2):在C1和C2列的组合上建立唯一非聚集索引- 创建非聚集索引:
CREATE NONCLUSTERED INDEX IDX1 ON employees(phone) - 主码查询一般使用索引扫描
题1:查询每个学生及其选修的课程名和成绩(多表连接):
-- 写法1:传统WHERE连接 SELECT student.Sno, Sname, Cname, Grade FROM student, sc, course WHERE student.Sno = sc.Sno AND sc.Cno = course.Cno; -- 写法2:JOIN语法 SELECT student.Sno, Sname, Cname, Grade FROM student JOIN sc ON student.Sno = sc.Sno JOIN course ON sc.Cno = course.Cno;
题2:查询选修"58130540"课程且成绩在90分以上的所有学生:
SELECT student.Sno, Sname, Cno, Grade FROM student, sc WHERE student.Sno = sc.Sno AND sc.Cno = '58130540' AND sc.Grade > 90;
填空题:R有5个属性10个元组,S有3个属性100个元组,R×S有 8 个属性,1000 条元组。
填空题(象集):关系study,cno=1时grade的象集是 {88, 90}。
七、事务管理与并发控制
7.1 事务基本概念必考
事务:用户定义的数据库操作序列,要么全做要么全不做,不可分割的工作单位。事务是恢复和并发控制的基本单位。
显式定义:BEGIN TRANSACTION ... COMMIT / ROLLBACK
COMMIT:正常结束,所有更新永久生效。ROLLBACK:异常终止,回滚所有更新。
| 特性 | 英文 | 含义 |
|---|---|---|
| 原子性 | Atomicity | 事务中操作要么全做要么全不做 |
| 一致性 | Consistency | 从一个一致性状态变到另一个一致性状态 |
| 隔离性 | Isolation | 一个事务的执行不能被其他事务干扰 |
| 持续性 | Durability | 事务提交后对数据的改变是永久性的 |
7.2 并发操作的问题必考
| 问题 | 描述 |
|---|---|
| 丢失修改 | T1与T2读同一数据并修改,T2提交结果破坏T1结果 |
| 不可重复读 | T1读取后T2更新,T1无法再现前次结果(含幻影现象) |
| 读"脏"数据 | T1修改后T2读取,T1被撤销恢复原值,T2读到不正确数据 |
7.3 封锁必考
排它锁(X锁/写锁):与其他锁不兼容。
共享锁(S锁/读锁):与其他S锁兼容,与X锁不兼容。
| 协议 | X锁 | S锁 | 防止问题 |
|---|---|---|---|
| 1级 | 修改前加X锁,事务结束释放 | 读不加锁 | 防丢失修改 |
| 2级 | 同1级 | 读前加S锁,读完即释放 | 防丢失修改 + 读脏数据 |
| 3级 | 同1级 | 读前加S锁,事务结束释放 | 防丢失修改 + 读脏数据 + 不可重复读 |
7.4 活锁与死锁选择填空
事务永远等待。解决:先来先服务策略。
两事务互相等待对方释放锁。
预防:一次封锁法、顺序封锁法。
诊断:超时法(可能误判)、等待图法(有向图存在回路=死锁)。
解除:选择代价最小的事务撤消,释放其所有锁。
7.5 并发调度的可串行性
并行执行结果与某次序串行执行结果相同 → 正确调度。可串行性是并行事务正确性的唯一准则。
内容:①读写前先获得封锁 ②释放一个封锁后不再获得任何其他封锁。
两个阶段:第一阶段扩展阶段(获得封锁),第二阶段收缩阶段(释放封锁)。
7.6 PTA事务与并发控制选择题精选新增
- MySQL中COMMIT和ROLLBACK都是事务控制语句 —— 正确
- COMMIT后不能再ROLLBACK —— 正确
- 事务提交后对数据库的修改是永久的 —— 正确
- 一级封锁协议可防止丢失修改 —— 正确
- 三级封锁协议可防止丢失修改、不可重复读、读"脏"数据 —— 正确
- 两段锁协议可保证可串行化 —— 正确
- 两段锁协议可能发生死锁 —— 正确
- 死锁可以通过预防和检测来解决 —— 正确
- 活锁可以通过先来先服务策略避免 —— 正确
- 事务开始语句:
BEGIN TRANSACTION - 事务回滚语句:
ROLLBACK TRANSACTION - 事务的持久性由DBMS的恢复机制实现
- 事务的隔离性由并发控制机制实现
- 事务的原子性由UNDO操作实现
- T1持有R的X锁,T2请求R的S锁→T2等待
- T1持有R的S锁,T2请求R的X锁→T2等待
- 死锁产生的条件:循环等待资源
- 死锁检测方法:等待图(有向图存在回路=死锁)
- 两段锁协议:事务分扩展阶段(只加锁)和收缩阶段(只解锁)
八、数据库恢复技术
8.1 故障类型选择填空
| 类型 | 原因 | 特点 |
|---|---|---|
| 事务故障 | 输入数据有误、运算溢出、违反完整性限制、死锁 | 单个事务夭折 |
| 系统故障 | OS/DBMS错误、操作员失误、CPU故障、停电 | 内存数据丢失,外存未受影响 |
| 介质故障 | 磁盘损坏、磁头碰撞、强磁场 | 可能性小但破坏性最大 |
8.2 恢复原理与日志必考
冗余:利用存储在系统其它地方的冗余数据重建数据库。
实现技术:数据转储(backup)+ 登录日志文件(logging)。
- 登记次序严格按并行事务执行的时间次序
- 必须先写日志文件,后写数据库(Write-Ahead Logging, WAL)
填空题:当数据库被破坏后,如果事先保存了数据库副本和日志文件,就有可能恢复数据库。
8.3 恢复策略必考
| 故障类型 | 恢复方法 |
|---|---|
| 事务故障 | 反向扫描日志,对该事务执行逆操作(UNDO),用前像(BI)替换后像(AI) |
| 系统故障 | 正向扫描日志,分Redo队列(已提交)和Undo队列(未完成);先UNDO再REDO |
| 介质故障 | 装入最新后备副本+日志文件,重做已提交事务(REDO);需要DBA介入 |
8.4 PTA恢复、安全性与完整性约束考点新增
- 事务故障恢复用UNDO —— 正确
- 系统故障恢复需要UNDO和REDO —— 正确
- 介质故障恢复用后备副本+日志文件 —— 正确
- 静态转储不允许事务运行 —— 正确
- 动态转储允许并发运行事务 —— 正确
- 事务故障系统自动恢复,不必用户干预 —— 正确
- 撤销权限用REVOKE(不是DROP/DELETE/ALTER)
- REVOKE的CASCADE选项:级联撤销依赖该对象的权限
- GRANT授予权限允许转授用 WITH GRANT OPTION
- 创建角色:
CREATE ROLE 角色名 - 强制存取控制安全级别高于自主存取控制
- 数据库角色是被命名的一组权限集合
- 所有授予的权力都可用REVOKE收回
- 数据库加密提高安全性但降低效率
- 完整性约束分类:实体完整性(主码)、参照完整性(外码)、用户定义完整性(CHECK等)
- 实体完整性:主码唯一且非空
- 参照完整性:外码要么为空,要么引用被参照表的主码
- 外码约束作用:实现参照完整性
- DEFAULT约束:设置默认值
- CHECK约束:限制列值范围
- 添加约束语法:
ALTER TABLE 表名 ADD CONSTRAINT 约束名 ...
九、JDBC基础
9.1 JDBC概述选择填空
JDBC(Java Database Connectivity):Java语言版的ODBC,让Java程序透明地访问MySQL、Oracle、SQL Server等数据库,只需更换驱动jar包,业务代码基本不用改。
本质特征:与特定数据库无关的API,隐藏不同数据库特性,提供数据库存取的平台独立性。
| 类型 | 名称 | 特点 |
|---|---|---|
| Type 1 | JDBC-ODBC桥驱动 | 通过ODBC访问;需客户端安装ODBC;牺牲平台独立性 |
| Type 2 | 本地驱动 | 将JDBC调用转为本地调用;需安装本地代码;牺牲平台独立性 |
| Type 3 | 网络驱动 | 使用网络中间服务器;支持负载均衡、连接池;有平台独立性 |
| Type 4 | 纯Java驱动 | 主流方式;直接把JDBC调用转化为DBMS网络协议 |
java.sql(核心API,J2SE):使用 DriverManager、Connection 等。
javax.sql(可选扩展API,J2EE):包含JNDI资源、连接池、分布式事务,使用 DataSource 接口。
9.2 JDBC访问数据库流程必考
// 1. 加载数据库驱动 Class.forName("com.mysql.jdbc.Driver"); // 2. 建立数据库连接 Connection conn = DriverManager.getConnection( "jdbc:mysql://127.0.0.1:3306/db_room", "root", "123456"); // 3. 创建Statement对象 Statement stmt = conn.createStatement(); // 或 PreparedStatement pstmt = conn.prepareStatement(sql); // 4. 执行查询返回ResultSet ResultSet rs = stmt.executeQuery("SELECT * FROM tbl_person"); // 5. 处理结果 while(rs.next()) { String name = rs.getString("person_name"); } // 6. 关闭资源(顺序:rs → stmt → conn) rs.close(); stmt.close(); conn.close(); 核心类关系:DriverManager → Driver → Connection → Statement/PreparedStatement → ResultSet
jdbc:<drivertype>://<host>:<port>/<dbname>
MySQL:jdbc:mysql://127.0.0.1:3306/db_room
SQLServer:jdbc:jtds:sqlserver://127.0.0.1:1433/dbname
Oracle:jdbc:oracle:thin:@192.168.118.101:1521:nbtv
9.3 Statement与查询
Statement stmt = con.createStatement(); ResultSet rs = stmt.executeQuery(sql); while(rs.next()) { // 读取数据 } rs.close(); stmt.close(); SQL字符串构建:SQL中字符串常量用单引号,Java中用双引号。
动态内容通过拼接:sql = "select * from t where a='" + a + "'"
游标最初在第一行前面,next() 返回boolean,移动到最后一行之后返回false。
if(rs.next()) 判断是否存在数据;while(rs.next()) 循环遍历。
读取字段:getString(1)(列序号)、getString("userid")(列名)、getInt(...)。
Statement stmt = con.createStatement(int type, int concurrency);
| type取值 | 说明 |
|---|---|
TYPE_FORWARD_ONLY | 游标只能向后滚动 |
TYPE_SCROLL_INSENSITIVE | 可前后滚动,数据库变化时结果集不变 |
TYPE_SCROLL_SENSITIVE | 可滚动,数据库变化时结果集同步改变 |
9.4 PreparedStatement重点
SQL语句预编译为数据库底层内部命令,封装在PreparedStatement对象中,减轻负担、提高速度。
优势:
- 性能更好:相同SQL多次调用时只需编译一次
- 防止SQL注入:用
?占位符,不需要字符串拼接 - 支持带参数查询
String sql = "SELECT * FROM tbl_person WHERE person_no = ?"; PreparedStatement pstmt = conn.prepareStatement(sql); pstmt.setString(1, person_no); // 给第1个 ? 赋值 ResultSet rs = pstmt.executeQuery();
9.5 增删改操作
| 方法 | 用途 | 返回值 |
|---|---|---|
executeQuery() | SELECT查询 | ResultSet |
executeUpdate() | INSERT/DELETE/UPDATE | int(影响行数) |
插入示例:
String sql = "INSERT INTO tbl_contract(person_no, room_no, from_date, to_date, contract_amount) VALUES(?,?,?,?,?)"; PreparedStatement pstmt = conn.prepareStatement(sql); pstmt.setString(1, person.getPersonNo()); pstmt.setString(2, room.getRoomNo()); pstmt.setTimestamp(3, new Timestamp(from.getTime())); pstmt.setTimestamp(4, new Timestamp(to.getTime())); pstmt.setDouble(5, amount); pstmt.executeUpdate();
- 通过ResultSet删除:
rs.deleteRow() - 通过SQL语句:
DELETE FROM 表 WHERE 条件 - 软删除:通过字段标识删除(如设置 removeDate),不真正删除
- 删除控制:通过外码违约控制或代码删除前判断
9.6 事务处理必考
// 1. 关闭自动提交,开启事务 conn.setAutoCommit(false); // 2. 执行多条SQL pstmt1.executeUpdate(); pstmt2.executeUpdate(); // 3. 全部成功 → 提交 conn.commit(); // 4. 中途出错 → 回滚 conn.rollback();
Connection对象默认为auto-commit,每条SQL语句自动提交。
9.7 时间类型处理选择
| 类型 | 说明 | 精度 |
|---|---|---|
java.sql.Date | 屏蔽了时间(hh:mm:ss) | 到天 |
java.sql.Time | 屏蔽了日期(yyyy-MM-dd) | 到时分秒 |
java.sql.Timestamp | 扩充了Date,增加毫秒 | 到毫秒 |
读取:rs.getDate()、rs.getTime()、rs.getTimestamp()
写入:pstmt.setTimestamp(3, new java.sql.Timestamp(System.currentTimeMillis()))
9.8 元数据
数据库元数据:
DatabaseMetaData dmd = conn.getMetaData(); String name = dmd.getDatabaseProductName(); String ver = dmd.getDatabaseProductVersion();
结果集元数据:
ResultSetMetaData rsmd = rs.getMetaData(); int count = rsmd.getColumnCount(); // 字段数量 String label = rsmd.getColumnLabel(1); // 字段名称
十、JDBC进阶
10.1 数据封装与OR映射简答
- 持久数据:存储在数据库、文件等存储介质中
- 感官数据:用户可直接看到、听到的数据(界面显示)
- 内存数据:程序中的变量,用JavaBean表达
应用系统核心功能 = 完成持久数据和感官数据之间的转换(通过内存数据中间状态)。
关系数据库中的表、行、列映射到面向对象语言中的类、对象、属性:
- 表 SystemUser → 类 BeanSystemUser
- 表中一行 → 一个对象
- 表中一列 → 对象一个属性
手写JDBC转换是最原始的OR映射;Hibernate、MyBatis等框架可自动完成。
三部分:属性、方法、事件(本课程只用属性封装数据)。
- 可读属性:必须有
getProperty()方法 - 可写属性:必须有
setProperty(PropertyClass pc)方法 - 必须有无参构造函数
10.2 主从关系与级联
提取:先查询主表得到对象,再根据外码查询从表,将结果List set到主对象属性中。
删除:要么先删从表再删主表;要么数据库设置级联删除。否则违反外键约束。
10.3 分页查询选择
将查询结果按页大小分成多页,每次返回一页数据。
MySQL分页语法:LIMIT 偏移量, 数量
-- 第21页,每页10条(偏移量 = (页码-1) × 页大小) SELECT * FROM foo WHERE b=1 LIMIT 200, 10;
SQL Server:SELECT TOP 10 * FROM foo
totalRecordCount:总记录数pageCount:总页数pagesize:页大小pageIndex:当前页data:当前页数据列表
提取步骤:①查总记录数 ②计算总页数 ③构造分页SQL,封装结果
10.4 连接池简答
传统方式每次访问都建立/关闭连接,开销大。连接池:
- 预先建立一些数据库连接
- 需要使用时,从池中获取一个空闲连接
- 使用完成后,不关闭连接,仅标识为空闲
数据源接口:javax.sql.DataSource(属于JavaEE)
典型实现:c3p0(开源JDBC连接池),配置 ComboPooledDataSource 对象。
10.5 视图与索引(数据库层面)
虚表,从一个或几个基本表导出。只存放定义,不存数据,不会数据冗余。
CREATE VIEW 视图名 AS 子查询 [WITH CHECK OPTION];
按列数量:单列索引(一个索引含单列)、组合索引(含多列)。
按特性:
- 普通索引:最基本的索引
- 唯一索引:索引列值必须唯一,允许空值
- 聚集索引:数据行物理存储顺序与索引顺序一致,每表只能一个,主键自动创建
索引选择原则:在WHERE、JOIN、GROUP BY、ORDER BY中出现的列;只有 =, <, >, BETWEEN, IN 及不以通配符开头的LIKE才使用索引。
10.6 DAO设计模式重点
DAO = Data Access Object(数据访问对象)。将与数据库相关的所有操作(增删改查)封装在独立类中,业务逻辑通过DAO访问数据库。
核心价值:隔离业务逻辑和数据访问代码,降低耦合度,提高可重用性。换数据库或改表结构只需改DAO层。
因为事务需要跨多个DAO方法。
例如借书操作需调用两个DAO方法(插入借阅记录 + 修改图书状态),必须在同一事务中。如果DAO方法自己创建和关闭Connection,无法保证同一个连接。
10.7 批处理与ORM框架
一次执行多个SQL语句,降低IO次数。一次批量约200条性能较好。
executeBatch() 返回 int[](每条语句影响行数),批量执行时应启动事务。
利用Java反射机制,动态创建对象、设置属性值。告知DAO对象SQL和对象类型,由DAO动态装配。
Hibernate通过XML配置文件建立类属性与表列的映射关系。
十一、上机操作实战
11.1 DBUtil 数据库连接工具类必背
package cn.edu.zucc.test; import java.sql.Connection; import java.sql.DriverManager; public class DBUtil { private static final String jdbcUrl = "jdbc:mysql://127.0.0.1:3306/db_room"; private static final String dbUser = "root"; private static final String dbPwd = "123456"; static { try { Class.forName("com.mysql.jdbc.Driver"); // 加载并注册MySQL驱动 } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws java.sql.SQLException { return DriverManager.getConnection(jdbcUrl, dbUser, dbPwd); } } 11.2 单条查询(PreparedStatement + ResultSet封装)
String sql = "SELECT person_no, person_name, sex, age FROM tbl_person WHERE person_no = ?"; Connection conn = null; PreparedStatement pstmt = null; ResultSet rs = null; BeanPerson person = null; try { conn = DBUtil.getConnection(); pstmt = conn.prepareStatement(sql); pstmt.setString(1, person_no); // 给第1个 ? 赋值 rs = pstmt.executeQuery(); if (rs.next()) { // 单条用 if person = new BeanPerson(); person.setPersonNo(rs.getString("person_no")); person.setPersonName(rs.getString("person_name")); person.setSex(rs.getString("sex")); person.setAge(rs.getInt("age")); } } finally { // 关闭资源:rs → pstmt → conn try { if(rs!=null) rs.close(); } catch(SQLException e) {} try { if(pstmt!=null) pstmt.close(); } catch(SQLException e) {} try { if(conn!=null) conn.close(); } catch(SQLException e) {} } 11.3 多层嵌套查询(分组聚合 + 子查询)
-- SQL三层逻辑(从内到外): -- 最内层:按building_code分组,SUM(area)算每个建筑总面积 -- 中间层:MAX(total_area)求最大面积数值 -- 最外层:HAVING筛选"总面积=最大值"的建筑 SELECT building_code FROM tbl_room GROUP BY building_code HAVING SUM(area) = ( SELECT MAX(total_area) FROM ( SELECT SUM(area) AS total_area FROM tbl_room GROUP BY building_code ) AS temp )
while(rs.next()) 循环读取。 11.4 多表连接查询(JOIN + 分组计数)
-- 合同表只有room_no,需通过room_no关联房间表找到building_code SELECT r.building_code, COUNT(*) AS contract_count FROM tbl_contract c JOIN tbl_room r ON c.room_no = r.room_no GROUP BY r.building_code
结果存入 Map<String, Integer>(key=建筑编号,value=合同数)。
11.5 批量插入与事务处理必考
String sql = "INSERT INTO tbl_contract(person_no, room_no, from_date, to_date, contract_amount) VALUES(?,?,?,?,?)"; Connection conn = null; PreparedStatement pstmt = null; try { conn = DBUtil.getConnection(); conn.setAutoCommit(false); // 1. 关闭自动提交,开启事务 pstmt = conn.prepareStatement(sql); for (Map.Entry entry : map.entrySet()) { BeanContract c = entry.getValue(); pstmt.setString(1, c.getPersonNo()); pstmt.setString(2, c.getRoomNo()); // 日期转换:SimpleDateFormat解析 + Timestamp写入 pstmt.setTimestamp(3, new Timestamp(c.getFromDate().getTime())); pstmt.setTimestamp(4, new Timestamp(c.getToDate().getTime())); pstmt.setDouble(5, c.getAmount()); pstmt.executeUpdate(); // 2. 逐条执行 } conn.commit(); // 3. 全部成功 → 提交 } catch (Exception e) { if (conn != null) conn.rollback(); // 4. 出错 → 回滚 e.printStackTrace(); } finally { try { if(pstmt!=null) pstmt.close(); } catch(SQLException e) {} try { if(conn!=null) conn.close(); } catch(SQLException e) {} } 日期处理:
SimpleDateFormat("yyyy-MM-dd") 解析字符串,Timestamp 写入数据库。 11.6 资源关闭万能模板必背
finally { try { if(rs!=null) rs.close(); // 查询题才有rs if(pstmt!=null) pstmt.close(); if(conn!=null) conn.close(); } catch (SQLException e) { e.printStackTrace(); } } 关闭顺序:从小到大 rs → pstmt → conn。增删改题去掉第一行 rs 即可。
11.7 上机操作技能总结
| 技能 | 关键点 |
|---|---|
| 1. 编写DBUtil工具类 | 加载驱动、获取连接 |
| 2. 单条查询 | PreparedStatement + ? + ResultSet封装到JavaBean |
| 3. 聚合分组查询 | GROUP BY + HAVING + 多层子查询嵌套 |
| 4. 多表连接查询 | JOIN ON 关联多表 |
| 5. 批量插入与事务 | setAutoCommit(false) + 循环 + commit/rollback |
| 6. 日期类型转换 | SimpleDateFormat解析 + Timestamp写入 |
| 7. 结果集封装 | 单条用if(rs.next())+对象,多条用while+集合 |
| 8. 资源管理 | finally块按rs→pstmt→conn顺序关闭 |
十二、期末考试高频考点与真题分析
12.1 试卷题型分布必看
| 题型 | 试卷1 | 试卷2 | 样卷 |
|---|---|---|---|
| 选择题 | - | - | 5题×2分=10分 |
| 填空题 | - | - | 5空×2分=10分 |
| 应用题(函数依赖与范式) | 12分 | 12分 | 10分 |
| 设计题(E-R图+转关系模式) | 8分 | 14分 | - |
| SQL题 | 5题×4分=20分 | 6题×4分=24分 | 20分 |
| 关系代数及查询优化 | 10分 | 10分 | - |
| 总分 | 50分 | 60分 | 50分 |
12.2 高频考点汇总必看
| 考点 | 出现情况 | 说明 |
|---|---|---|
| 函数依赖与范式判断 | 3套均考 | 给关系模式→画函数依赖图→求主码→判断范式→规范化到3NF |
| E-R图设计 | 3套均考 | 根据需求画E-R图,识别实体/联系/属性 |
| E-R图转关系模式 | 3套均考 | 标主码和外码,1:1/1:N/M:N转换规则 |
| SQL建表(含约束) | 3套均考 | CREATE TABLE,主码、外码、CHECK约束 |
| SQL视图创建 | 3套均考 | CREATE VIEW,含分组聚合 |
| SQL删除(DELETE+子查询) | 3套均考 | 删除无关联/无销售/无采购的数据 |
| SQL更新(UPDATE+子查询) | 3套均考 | 按条件修改单价/VIP/级别 |
| SQL权限控制 | 3套均考 | GRANT/REVOKE |
| 关系代数(含除法) | 3套均考 | "出现在所有X的Y"必用除法 |
| 查询语法树与优化 | 2套考 | 画语法树并优化(选择/投影下推) |
| SQL插入(INSERT) | 2套考 | 基本INSERT语句 |
| 封锁协议/死锁/恢复 | 样卷考 | 三级封锁协议、死锁解除、日志文件恢复 |
| 三级模式/数据独立性 | 样卷考 | 外模式/模式/内模式及映射 |
12.3 必背知识点速查
| 知识点 | 核心内容 |
|---|---|
| ACID特性 | 原子性、一致性、隔离性、持续性 |
| 先写日志原则 | 必须先写日志文件,后写数据库(WAL) |
| 三级封锁协议 | 1级防丢失修改,2级+防读脏,3级+防不可重复读 |
| 两段锁协议 | 可串行化的充分条件(非必要) |
| 范式层级 | 1NF→消除部分依赖→2NF→消除传递依赖→3NF→消除主属性依赖→BCNF |
| 除运算 | 查"出现在所有X的Y"类问题,求象集包含关系 |
| 双重NOT EXISTS | 实现全称量词:(∀x)P ≡ ¬(∃x(¬P)) |
| WHERE vs HAVING | WHERE分组前筛选行,HAVING分组后筛选组 |
| GRANT/REVOKE | WITH GRANT OPTION允许传播权限,REVOKE级联收回 |
| 聚簇索引 | 每表只能一个,物理顺序与索引顺序一致 |
| JDBC流程 | Class.forName→getConnection→createStatement→executeQuery→处理ResultSet→关闭 |
| PreparedStatement | 预编译、防SQL注入、?占位符 |
| JDBC事务 | setAutoCommit(false)→执行→commit/rollback |
| 资源关闭顺序 | rs → pstmt → conn(在finally中) |
| 数据独立性 | 逻辑独立性=外模式/模式映象,物理独立性=模式/内模式映象 |
12.4 解题方法论
- 画函数依赖图:从语义推导所有函数依赖关系
- 求主码:找L类属性(只在左部),求闭包,验证是否=U
- 判断范式:检查部分依赖(→1NF)和传递依赖(→2NF)
- 说明原因:指出存在的部分/传递函数依赖
- 分解到3NF:按函数依赖分组,每组属性+决定属性组成新关系
- 建表题:注意PRIMARY KEY、FOREIGN KEY、CHECK约束的完整书写
- 视图题:注意GROUP BY后SELECT只能出现分组属性和集函数
- 删除/更新题:用 NOT IN + 子查询处理"没有关联记录"的情况
- 权限题:GRANT ... TO ... [WITH GRANT OPTION] / REVOKE ... FROM ...
- 除法题:识别"出现在所有...的..."句式,用 R÷S
- 连接题:选择+投影+自然连接的组合
- 语法树优化:选择条件下推到叶子节点,投影提前,连接最后
十三、PTA练习题精选
13.1 判断题精选必练
| 题目 | 答案 |
|---|---|
| 项目的数据库设计必须完全符合范式要求 | T |
| 范式的主要目的是提高查询效率 | F(消除冗余和异常) |
| 任何二目关系(两个属性)属于3NF | T |
| 若R.A→R.B,R.B→R.C,则R.A→R.C(传递律) | T |
| 函数依赖集F的最小函数依赖集Fm不唯一 | T |
| 若X→Y和Y→Z,且X、Y、Z为互不相同的单属性,则不存在X到Z的传递函数依赖 | T |
| 题目 | 答案 |
|---|---|
| E-R图中用椭圆形表示属性 | T |
| 数据库的概念模型不依赖于计算机硬件和DBMS | T |
| E-R图用于建立数据库的概念模型 | T |
| 描述概念模型的有力工具是数据字典 | F(是E-R图) |
| 概念结构设计最常采用的策略是自顶向下的方法 | F(自底向上) |
| 物理设计阶段形成的是外模式 | F(内模式) |
| 由概念设计进入逻辑设计时,多对多联系需转换成基本表 | T |
| 主码查询一般使用索引扫描 | T |
| 题目 | 答案 |
|---|---|
| 查询优化策略由DBMS自动选择,不是由用户确定 | F |
| 建立索引是为了提高检索速度 | T |
| 连接运算尽可能先做是查询优化最重要最基本的策略 | T |
| 查询优化总目标是选择有效策略求得关系表达式的值 | T |
| 题目 | 答案 |
|---|---|
| MySQL中COMMIT和ROLLBACK都是事务控制语句 | T |
| COMMIT后不能再ROLLBACK | T |
| 事务提交后对数据库的修改是永久的 | T |
| 事务故障系统自动恢复,不必用户干预 | T |
| 一级封锁协议可防止丢失修改 | T |
| 三级封锁协议可防止丢失修改、不可重复读、读"脏"数据 | T |
| 两段锁协议可保证可串行化 | T |
| 两段锁协议可能发生死锁 | T |
| 死锁可以通过预防和检测来解决 | T |
| 活锁可以通过先来先服务策略避免 | T |
| 静态转储不允许事务运行 | T |
| 动态转储允许并发运行事务 | T |
| 事务故障恢复用UNDO | T |
| 系统故障恢复需要UNDO和REDO | T |
| 题目 | 答案 |
|---|---|
| 数据库中建立的索引不是越多越好 | T(索引越多越好是错的) |
| 每行索引记录包含指向表中数据页的逻辑指针 | T |
| 数据库加密提高安全性但降低效率 | T |
| 强制存取控制安全级别高于自主存取控制 | T |
| 数据库角色是被命名的一组权限集合 | T |
| 所有授予的权力都可用REVOKE收回 | T |
| 自主存取控制:用户可自主决定将权限授予何人 | T |
13.2 选择题精选必练
- 规范化的主要目的:维护数据的一致性
- 包含两个属性的关系模式一定满足:BCNF
- 关系模式至少是:1NF
- 消除部分函数依赖的1NF必定是:2NF
- 在2NF基础上消除传递函数依赖,必定是:3NF
- 若所有属性都是主属性,则R至少属于:3NF
- 平凡函数依赖是指:Y是X的子集(如AB→A)
- X→Y, Y→X 称为:互相函数依赖
- E-R图适用于建立:概念模型
- 层次、网状和关系模型属于:逻辑模型
- 概念设计中最常用的数据模型是:实体联系模型(E-R模型)
- 概念结构设计常用方法:自底向上、自顶向下、逐步扩张(无"从外到内")
- 3个实体型+3个M:N联系→转换为:6个关系(3实体+3联系)
- 数据库设计六阶段顺序:需求分析→概念设计→逻辑设计→物理设计→实施→运行维护
- DB并发操作导致的问题:丢失修改、不可重复读、读"脏"数据
- 事务的持久性由DBMS的恢复机制实现
- 事务的隔离性由并发控制机制实现
- 事务的原子性由UNDO操作实现
- 一级封锁协议:修改前加X锁,事务结束释放→防丢失修改
- 二级封锁协议:一级+读前加S锁,读完释放→+防读脏数据
- 三级封锁协议:一级+读前加S锁,事务结束释放→+防不可重复读
- 死锁检测方法:等待图(有向图存在回路=死锁)
13.3 填空题精选必练
| 题目 | 答案 |
|---|---|
| 产生数据冗余和异常的两个重要原因是____和____函数依赖 | 部分、传递 |
| 若所有非主属性都完全函数依赖于主码,则R至少属于第____范式 | 二(2NF) |
| R有5个属性10个元组,S有3个属性100个元组,R×S有____个属性,____条元组 | 8、1000 |
| 一个表最多____个聚集索引,____个非聚集索引 | 1、多 |
| 创建非聚集索引的SQL语句 | CREATE NONCLUSTERED INDEX IDX1 ON employees(phone) |
13.4 PTA综合大题类型汇总必看
| 题型 | 分值 | 数量 | 核心要求 |
|---|---|---|---|
| E-R图设计 | 5-18分 | 10题 | 画E-R图+转关系模式 |
| 关系模式分解 | 8分 | 9题 | 函数依赖→候选码→范式→3NF分解 |
| SQL查询 | 6分 | 多题 | 多表连接查询(WHERE和JOIN两种写法) |
PTA的关系模式分解题 → 期末考试应用题(12分)
PTA的E-R图设计题 → 期末考试设计题(8-14分)
PTA的SQL查询题 → 期末考试SQL题(每题4分)
PTA的判断/选择题 → 期末考试选择填空题(10-20分)
评论交流
欢迎留下你的想法