知识库

先学知识点,再做题检验。当前:数据库系统

01. 计算机系统知识 (13个) 02. 程序语言基础 (3个) 03. 操作系统 (8个) 04. 软件工程 (8个) 05. 数据结构与算法 (23个) 06. 数据库系统 (11个) 07. 计算机网络 (12个) 08. 面向对象技术 (10个) 09. 信息安全 (4个) 10. 知识产权与标准化 (4个) 11. 多媒体基础 (2个) 12. 项目管理 (4个)
关系模型与代数
关系模型核心概念 重点 难度 2/3
通俗理解
关系模型就是Excel表格的世界观——数据库里的数据都组织成一张张"表格",每张表有固定的列(属性),行(元组)就是一条条记录。比如"学生表"有列:学号、姓名、年龄、班级——每行就是一个具体学生。关系模型的"关系"就是指这张表本身。
核心公式/考点
• 基本概念:
关系(Relation)→ 表
元组(Tuple)→ 行/记录
属性(Attribute)→ 列/字段
域(Domain)→ 属性的取值范围
码(Key)→ 唯一标识元组的属性集
候选码 → 能唯一标识的最小属性集
主码 → 选中的一个候选码
外码 → 引用另一个表的主码
• 关系完整性约束:
实体完整性:主码非空且唯一
参照完整性:外码值要么为空要么是被引用表的有效主码值
用户定义完整性:自定义约束条件
表格对比
关系模型术语数学名称通俗名称例子
关系Relation表(Table)学生表
元组Tuple行(Row)张三那行
属性Attribute列(Column)姓名列
Domain数据类型+范围年龄∈[0,150]
Degree属性个数4
真题演练
例题:学生表S(学号,姓名,系别),选课表SC(学号,课程号,成绩)。
问:以下哪些是可作为主码?外码是哪些?
解:S表主码=学号(能唯一定位一个学生)
SC表候选码=(学号,课程号):一个学生选一门课只有一条成绩记录
SC表的学号是外码(引用S表的学号)
SC表的课程号如果是引用课程表C(课程号,课程名)的主码,则也是外码
注意事项
• 主码是候选码之一,用下划线标注
• 外码和被引用的主码不一定同名,但域必须相同
• 关系模型中"关系"有严格的数学定义(集合论),不是随便一张表
• 关系规范化就是让表结构更好——减少数据冗余和异常
✏️ 做这节的题(5题)
SQL语言
关系代数 重点 难度 2/3
通俗理解
关系代数就是"对表格做运算的数学"——就像算术有加减乘除,关系代数有并交差、选择投影、连接除。选择是"挑行"(选出满足条件的记录),投影是"挑列"(只看某些字段)。连接是把两张表按某个条件拼在一起。
核心公式/考点
• 基本运算:
σ(选择/Select):σ_条件(关系) → 选出满足条件的行
π(投影/Project):π_属性列表(关系) → 选出指定列
∪(并/Union):R∪S → 合并两个关系(去重)
∩(交/Intersection):R∩S → 共同的元组
−(差/Difference):R−S → 在R不在S的元组
×(笛卡尔积/Cartesian):R×S → 所有可能的组合
• 连接运算:
自然连接 ⋈:等值连接+去掉重复列
θ连接 ⋈_θ:按条件θ连接
外连接:左外⟕、右外⟖、全外⟗
• 除运算 ÷:R÷S = R中在S所有属性上都匹配的元组
表格对比
运算符号类似SQL作用于
选择σWHERE
投影πSELECT(列)
连接JOIN多表
UNION行(上下合并)
真题演练
例题:学生S(S#,SName),选课SC(S#,C#,Score)。查询"选了课程C1的学生的学号和姓名"。
解:关系代数:
π_S#,SName( σ_C#='C1'(S ⋈ SC) )
或者:
π_SName( σ_C#='C1'(S ⋈ SC) )
分步理解:
1) S ⋈ SC → 自然连接,得到学生+选课信息的大表
2) σ_C#='C1' → 只保留选了C1课程的行
3) π_S#,SName → 只要学号和姓名列
SQL对应:SELECT S.S#, S.SName FROM S JOIN SC ON S.S#=SC.S# WHERE SC.C#='C1';
注意事项
• 选择σ是行过滤,投影π是列过滤——运算顺序影响效率
• 自然连接自动匹配同名属性,θ连接需指定连接条件
• 除运算最难理解——相当于"至少包含所有..."的查询
• 关系代数运算结果是"关系"(表),可以嵌套使用
SQL-DDL建表 重点 难度 2/3
通俗理解
DDL(数据定义语言)就是"给数据库建房子"的蓝图——CREATE TABLE是画户型图(定义表结构),ALTER TABLE是装修(加柱子/拆墙),DROP TABLE是拆房子。建表时要确定:每个房间叫什么(列名)、装什么(数据类型)、能不能空着(NOT NULL)、有没有门牌号约束(主键/外键)。
核心公式/考点
• CREATE TABLE语法:
CREATE TABLE 表名 (
列名 数据类型 [约束],
...
[表级约束]
);
• 数据类型:INT, FLOAT, CHAR(n), VARCHAR(n), TEXT, DATE, ENUM(列表)
• 约束类型:
PRIMARY KEY — 主键(非空且唯一)
FOREIGN KEY REFERENCES — 外键引用
NOT NULL — 非空
UNIQUE — 唯一
DEFAULT 默认值 — 默认
CHECK(条件) — 检查约束
• ALTER TABLE常用操作:
ADD COLUMN 列名 类型 — 加列
DROP COLUMN 列名 — 删列
MODIFY COLUMN 列名 新类型 — 改列类型
ADD CONSTRAINT — 加约束
表格对比
约束类型作用表级/列级
NOT NULL不能为空列级
UNIQUE值唯一两者均可
PRIMARY KEY主键(非空+唯一)两者均可
FOREIGN KEY引用其他表主键表级
CHECK满足条件两者均可
DEFAULT默认值列级
真题演练
例题:创建学生表Student(Sno,Sname,Ssex,Sage,Sdept),主键Sno,性别只能为'男'或'女',年龄在15-45之间。
解:
CREATE TABLE Student (
Sno CHAR(10) PRIMARY KEY,
Sname VARCHAR(20) NOT NULL,
Ssex CHAR(2) CHECK(Ssex IN ('男','女')),
Sage INT CHECK(Sage BETWEEN 15 AND 45),
Sdept VARCHAR(30) DEFAULT '计算机系'
);
加外键举例:
CREATE TABLE SC (
Sno CHAR(10),
Cno CHAR(6),
Grade INT,
PRIMARY KEY(Sno,Cno),
FOREIGN KEY(Sno) REFERENCES Student(Sno),
FOREIGN KEY(Cno) REFERENCES Course(Cno)
);
注意事项
• PRIMARY KEY 隐含 NOT NULL + UNIQUE
• 外键列的数据类型必须与引用的主键完全一致
• MySQL/SQLite/PostgreSQL的DDL语法有细微差异(如AUTO_INCREAMENT)
• 删除父表前必须先删子表(或先删外键约束)
SQL-DML查询 重点 难度 2/3
通俗理解
DML(数据操作语言)中的SELECT是SQL最核心最强大的语句——就像对着数据库"问问题"。你想知道的答案越多,SELECT语句的写法就越复杂。从最简单的"查一张表的所有内容",到多表连接、子查询、分组统计、排序分页,层层递进。
核心公式/考点
• SELECT完整语法结构:
SELECT [DISTINCT] 列列表
FROM 表列表
[WHERE 行过滤条件]
[GROUP BY 分组列]
[HAVING 组过滤条件]
[ORDER BY 排序列 [ASC|DESC]]
[LIMIT 偏移量,数量] 或 [OFFSET 行数 FETCH NEXT 行数 ROWS ONLY]
• 连接查询:
INNER JOIN — 内连接(只取匹配的行)
LEFT/RIGHT/FULL OUTER JOIN — 外连接
自连接 — 同一张表自己连自己
• 子查询:
WHERE子句中:IN, EXISTS, 比较运算符
FROM子句中:作为临时表
SELECT子句中:标量子查询(返回单值)
• 聚合函数:COUNT, SUM, AVG, MAX, MIN
• GROUP BY + HAVING:分组统计后再筛选组
表格对比
子句作用执行顺序注意
FROM确定数据源1可多表/子查询
WHERE行级过滤2不能用聚合函数
GROUP BY分组3分组合并
HAVING组级过滤4可用聚合函数
SELECT选列5可表达式/聚合
ORDER BY排序6可用别名
LIMIT分页7最后执行
真题演练
例题:学生S(学号,姓名,系别,年龄),选课SC(学号,课号,成绩)。查询"计算机系选了课且平均分≥80的学生姓名"。
解:
SELECT S.姓名, AVG(SC.成绩) as 平均分
FROM S
JOIN SC ON S.学号 = SC.学号
WHERE S.系别 = '计算机系' AND SC.成绩 IS NOT NULL
GROUP BY S.学号, S.姓名
HAVING AVG(SC.成绩) >= 80
ORDER BY 平均分 DESC;
执行顺序:
1.FROM S JOIN SC → 连接两表
2.WHERE 系别='计算机系' → 过滤行
3.GROUP BY 学号,姓名 → 按学生分组
4.HAVING AVG(成绩)≥80 → 过滤组
5.SELECT 姓名,AVG(成绩) → 选择列
6.ORDER BY 平均分 DESC → 排序
注意事项
• WHERE过滤行,HAVING过滤组——不要混用
• COUNT(*)计数包含NULL行,COUNT(列)不包括NULL
• 子查询用EXISTS比IN效率高(找到第一个就停)
• SQL执行顺序:FROM→WHERE→GROUP BY→HAVING→SELECT→ORDER BY→LIMIT
✏️ 做这节的题(13题)
范式理论
函数依赖与范式1NF-3NF 重点 难度 2/3
通俗理解
范式就是"表结构的好坏等级"——就像酒店星级:1NF(一星级)是把数据放进行和列的基本表格;2NF(二星级)是每个非主键列都完全依赖于主键(不能只依赖一半);3NF(三星级)是每列都直接依赖主键(不能间接依赖——比如不能A→B→C地传下去)。BCNF是3NF的加强版。
核心公式/考点
• 函数依赖:X→Y 表示X能决定Y(知道X就知道Y)
完全函数依赖:(学号,课号)→成绩 缺一不可决定成绩
部分函数依赖:(学号,课号)→姓名 只依赖学号(主键的一部分)
传递函数依赖:学号→系号, 系号→系主任 → 学号传递→系主任
• 范式定义:
1NF:所有属性都是原子值(不可再分)
2NF:1NF + 消除非主属性对主码的部分函数依赖
3NF:2NF + 消除非主属性对主码的传递函数依赖
BCNF:3NF + 消除主属性对主码的部分和传递函数依赖
表格对比
范式解决的问题要消除的依赖例子(违反)
1NF非原子列表中还有表电话号码字段存多个号码
2NF数据冗余部分依赖SC(学号,课号,姓名,成绩)→姓名只依赖学号
3NF传递依赖传递依赖S(学号,系号,系主任)→系主任通过系号传递
真题演练
例题:关系模式R(学号,姓名,系号,系主任,课号,成绩),主码(学号,课号)。判断属于第几范式?如何分解到3NF?
解:
1) 所有列原子 → 满足1NF
2) 主码(学号,课号):
姓名、系号、系主任只依赖于学号(部分依赖)→ 不满足2NF
3) 分解到2NF:
R1(学号,姓名,系号,系主任) 主码:学号
R2(学号,课号,成绩) 主码:(学号,课号)
4) R1中:学号→系号, 系号→系主任(传递依赖)→ 不满足3NF
5) 分解到3NF:
R11(学号,姓名,系号)
R12(系号,系主任)
R2(学号,课号,成绩)
最终3NF三个表 ✅
注意事项
• 判断范式时先确定主码,再找所有函数依赖
• 部分依赖:非主属性依赖于主码的"真子集"
• 传递依赖:X→Y, Y→Z, 且Y不→X(Y不是X的子集)
• 3NF已经能解决绝大多数冗余和异常问题,BCNF要求更高
✏️ 做这节的题(7题)
ER模型
ER实体关系模型 重点 难度 2/3
通俗理解
ER模型就像画一张"数据地图"——实体(Entity)是地图上的"地点"(如学生、课程、教师),属性是地点的"特征"(如学生有学号姓名),关系(Relationship)是地点之间的"路线"(如学生"选修"课程)。用方框画实体、椭圆画属性、菱形画关系,一目了然。
核心公式/考点
• ER图三要素:
实体(矩形):独立存在的事物,如学生、课程
属性(椭圆):实体的特征,如学生的学号、姓名
关系(菱形):实体之间的联系,如学生"选修"课程
• 关系类型(映射基数):
1:1(一对一):一个A对应一个B
1:N(一对多):一个A对应多个B
M:N(多对多):多个A对应多个B
• ER图转关系模式:
每个实体→一个关系表
1:1关系:一方的PK放入另一方
1:N关系:N方的表加入1方的PK
M:N关系:新建关系表,含双方PK
表格对比
关系类型符号例子转换为关系模式
1:1——1——1——班级↔班长一方PK放入另一方
1:N——1——N——班级↔学生N方表加班级PK
M:N——M——N——学生↔课程新建选课表(学号,课号)
真题演练
例题:某学校有"教师"和"课题",一个教师可以参与多个课题,一个课题由多个教师参与,每个教师在某个课题中有一个"贡献度"。画出ER图的核心要素并转换为关系模式。
解:
ER图要素:
实体:教师(工号,姓名,职称)
实体:课题(编号,名称,经费)
关系:参与(M:N,属性:贡献度)
关系模式转换:
教师(工号,姓名,职称) PK=工号
课题(编号,名称,经费) PK=编号
参与(工号,编号,贡献度) PK=(工号,编号)
FK:工号→教师, 编号→课题
注意事项
• ER图实体用矩形、属性用椭圆、关系用菱形——不能搞混
• 主码属性在ER图中用下划线标注
• 派生属性(如年龄可由出生日期算出)用虚线椭圆表示
• M:N关系必须新建关系表(不新建会导致数据冗余)
✏️ 做这节的题(5题)
事务并发
事务ACID 重点 难度 2/3
通俗理解
事务就是"要么全部做完,要么什么都不做"的一组操作。就像转账:A扣100和B加100必须同时成功或同时失败,不能A扣了钱但B没收到。ACID四个特性:
原子性(Atomicity):不可分割,全做或全不做
一致性(Consistency):事务前后数据状态合法(不违反约束)
隔离性(Isolation):并发事务互不干扰
持久性(Durability):一提交就永久保存
核心公式/考点
• 事务状态:活动→部分提交→提交/中止
• 事务的COMMIT和ROLLBACK
• 并发问题:
脏读(Dirty Read):读到未提交数据
不可重复读(Non-repeatable Read):同一事务两次读同数据结果不同
幻读(Phantom Read):同一范围查询两次结果不同(新插入行)
• 隔离级别(由低到高):
READ UNCOMMITTED(读未提交):可能脏读
READ COMMITTED(读已提交):解决脏读
REPEATABLE READ(可重复读):解决脏读+不可重复读
SERIALIZABLE(串行化):全解决,但性能最差
表格对比
隔离级别脏读不可重复读幻读
READ UNCOMMITTED❌可能❌可能❌可能
READ COMMITTED✅不会❌可能❌可能
REPEATABLE READ✅不会✅不会❌可能(InnoDB用间隙锁解决)
SERIALIZABLE✅不会✅不会✅不会
真题演练
例题:事务T1转账给T2:①读取A=100 ②A扣50→A=50 ③写入A ④读取B=200 ⑤B加50→B=250 ⑥写入B。T1执行到③时崩溃。事务的哪个特性保证数据正确?
解:原子性保证——③写A后崩溃,系统通过日志REDO/UNDO回滚T1
A恢复到100,B未改(还没执行到⑥),数据恢复一致状态 ✅
DBMS通过"日志文件"实现:
UNDO日志:记录旧值,提交前崩溃则恢复旧值
REDO日志:记录新值,提交后崩溃则重做新值
注意事项
• ACID中"一致性"是目的,原子性+隔离性+持久性是手段
• MySQL InnoDB默认隔离级别是REPEATABLE READ(通过MVCC+间隙锁解决幻读)
• 隔离级别越高,并发性能越差——需要在"一致性"和"性能"间权衡
• 持久性通常通过WAL(Write-Ahead Logging)实现
封锁并发控制 重点 难度 2/3
通俗理解
封锁就像图书馆占座——你去书架上拿书(读数据)时放个"我正看着"的牌子(共享锁),别人也能看但不能拿走。你要改书的内容(写数据)时放个"我在修改"的牌子(排他锁),别人既不能看也不能改。两段锁协议就是保证"拿牌子的阶段"和"还牌子的阶段"不交叉——先集中拿齐全部门牌,再集中还。
核心公式/考点
• 封锁类型:
共享锁(S锁/读锁):可读不可写,其他事务也可加S锁
排他锁(X锁/写锁):可读可写,其他事务不能加任何锁
• 封锁协议:
一级封锁协议:修改前加X锁(防止丢失修改)
二级封锁协议:一级+读前加S锁,用完即释放(防止脏读)
三级封锁协议:一级+读前加S锁,事务结束才释放(防止不可重复读)
• 两段锁协议(2PL):所有加锁操作在第一个解锁之前
扩展阶段:只加锁不解锁
收缩阶段:只解锁不加锁
满足2PL的可串行化调度
表格对比
封锁协议加锁要求解决丢失修改解决脏读解决不可重复读
一级改前加X锁→事务结束
二级一级+读前加S锁→用完释放
三级一级+读前加S锁→事务结束
真题演练
例题:T1: R(A) W(A) R(B) W(B) 和 T2: R(A) W(A) 并发执行。分析可能的问题。
解:如果不加锁,可能出现:
1) T1读A=100,T2读A=100,T2写A=90,T1写A=80(丢失T2修改)→ 丢失修改
2) T1写A=80(未提交),T2读A=80(脏读),T1回滚→T2读到脏数据
加三级封锁协议:
T1操作A前加X锁,T2等待 → T1提交释放锁 → T2获取X锁操作
保证了串行化执行(T1→T2),避免了所有并发问题
注意事项
• 死锁:事务A等B释放锁,事务B等A释放锁 → 互相等待
预防:一次性封锁、顺序封锁
检测:等待图(有环则死锁),选代价小的事务回滚
• 活锁:事务一直等不到锁(优先级低),用"先来先服务"避免
• 多粒度封锁(意向锁)提高大范围锁的效率
✏️ 做这节的题(6题)
数据库高级特性
数据库完整性约束 重点 难度 2/3
通俗理解
完整性约束就是数据库的"规则警察"——防止非法数据进入数据库。实体完整性说"每个人都要有身份证且不能重复"(主键非空唯一)。参照完整性说"你填的部门编号必须真的在部门表里存在"(外键)。用户定义完整性说"年龄不能超过150岁"(CHECK约束)。触发器则是"自动化警员"——一旦有人违反规则,自动触发处罚措施。
核心公式/考点
• 实体完整性:PRIMARY KEY 约束(非空+唯一)
• 参照完整性:FOREIGN KEY 约束
ON DELETE/ON UPDATE 选项:
CASCADE:级联(删除/更新父表,子表跟着变)
SET NULL:设空
NO ACTION/RESTRICT:拒绝
• 用户定义完整性:
CHECK约束、NOT NULL、UNIQUE、DEFAULT
• 断言(ASSERTION):跨多表的复杂约束(多数DBMS支持有限)
• 触发器(TRIGGER):
事件:INSERT/UPDATE/DELETE
时机:BEFORE/AFTER/INSTEAD OF
粒度:行级(FOR EACH ROW)/语句级(FOR EACH STATEMENT)
表格对比
完整性类型实现方式违反时例子
实体完整性PRIMARY KEY拒绝插入重复学号→报错
参照完整性FOREIGN KEY拒绝/级联/置空删除有选课记录的课程→级联删除选课
用户定义完整性CHECK/UNIQUE/NOT NULL拒绝年龄>200→报错
业务规则完整性触发器/断言自定义工资涨幅不超过20%→自动回滚
真题演练
例题:员工表emp(eid,name,salary,did),部门表dept(did,dname,budget)。要求:
1) 员工工资在3000-100000之间
2) 部门预算不能少于所有该部门员工工资总和
3) 删除部门时,该部门员工自动设为NULL
解:
CREATE TABLE dept (
did INT PRIMARY KEY,
dname VARCHAR(50),
budget DECIMAL(10,2)
);
CREATE TABLE emp (
eid INT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
salary DECIMAL(10,2) CHECK(salary BETWEEN 3000 AND 100000),
did INT,
FOREIGN KEY(did) REFERENCES dept(did)
ON DELETE SET NULL
ON UPDATE CASCADE
);
-- 预算约束用触发器实现:
CREATE TRIGGER check_budget
BEFORE UPDATE OF budget ON dept
FOR EACH ROW
BEGIN
IF NEW.budget < (SELECT SUM(salary) FROM emp WHERE did = NEW.did) THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '预算不能小于工资总和';
END IF;
END;
注意事项
• 外键的引用列必须是主键或UNIQUE列
• CHECK约束不能引用其他表的列(SQL标准允许但MySQL不支持)
• 触发器会降低性能——不要用触发器做普通约束能做的工作
• SQLite的触发器不直接支持存储过程写法的SIGNAL,实际用SELECT RAISE(ABORT)
视图与索引 重点 难度 2/3
通俗理解
视图(View)就像数据库的"快捷方式/望远镜"——它不存真实数据,只存一个SELECT语句。每次查视图就像执行这个SELECT。可以简化复杂查询、限制用户能看的数据。索引(Index)就像书的目录——让你不用翻遍全书就能找到要找的内容。但目录本身也占空间,书内容变了目录也要更新。
核心公式/考点
• 视图(View):
CREATE VIEW 视图名 AS SELECT语句
优点:安全性(隐藏敏感列)、简化查询、逻辑独立性
可更新视图的限制:不能有聚合、DISTINCT、GROUP BY、多表连接等(各DBMS有差异)
WITH CHECK OPTION:通过视图修改数据时强制满足视图条件
• 索引(Index):
聚集索引(Clustered):数据物理顺序与索引顺序一致,一张表一个
非聚集索引(Non-clustered):索引顺序与数据物理顺序无关
唯一索引:索引列值唯一
复合索引(联合索引):多个列组合索引
最左匹配原则:复合索引(a,b,c)可以使用(a)、(a,b)、(a,b,c)但不用(b)或(b,c)
表格对比
索引类型数据存储每表数量查询速度插入/更新速度
聚集索引叶结点存整行数据1个范围查询极快慢(数据重排)
非聚集索引叶结点存数据指针多个快+一次指针跳转中(索引更新)
覆盖索引含查询所有需要列多个最快(不用回表)
真题演练
例题:表emp(id,name,dept,salary),查询"销售部工资大于5000的员工"。
创建什么索引最有效?
解:
查询条件:dept='销售部' AND salary>5000
最合适的索引:复合索引(dept, salary)
原因:先按dept定位到"销售部"的索引段(等于匹配),再在该段内按salary过滤(范围匹配)→ 最左前缀原则生效
CREATE INDEX idx_dept_salary ON emp(dept, salary);
SELECT * FROM emp WHERE dept='销售部' AND salary>5000; -- 走索引
注意事项
• 索引不是越多越好——每个索引都会降低INSERT/UPDATE/DELETE速度
• 复合索引遵循"最左匹配"原则——把等值查询列放前面,范围查询放后面
• 视图不存数据(物化视图除外),每次查询都重新执行SELECT
• 频繁更新的列不适合建索引
• 性别这种"低选择度"列(只有两个值)索引效果差
存储过程与触发器 重点 难度 2/3
通俗理解
存储过程就像数据库里的"函数/小程序"——你把一串SQL操作封装起来,起个名字,以后直接"调用名字"执行。好处是:一次编译多次运行、减少网络传输(不用来回发多条SQL)、封装业务逻辑。触发器则是"自动触发的小程序"——当某表发生INSERT/UPDATE/DELETE时自动执行,就像家里装了感应灯——人进来自动亮。
核心公式/考点
• 存储过程(Stored Procedure):
CREATE PROCEDURE 过程名(参数) AS BEGIN ... END
参数类型:IN(输入)、OUT(输出)、INOUT(输入输出)
优点:提高性能(预编译)、减少网络流量、封装逻辑、权限控制
缺点:跨数据库移植困难、调试不便、增加数据库服务器压力
• 触发器(Trigger):
- CREATE TRIGGER 触发器名 {BEFOREAFTER} {INSERTUPDATEDELETE} ON 表名
行级触发器(FOR EACH ROW):每条受影响的行都触发
语句级触发器(FOR EACH STATEMENT):每句SQL只触发一次
用途:审计日志、数据校验、自动维护汇总数据、级联操作
NEW和OLD关键字:NEW新行,OLD旧行
表格对比
特性存储过程触发器普通SQL
调用方式CALL 过程名()自动触发手动执行
预编译✅ 是✅ 是❌ 每次解析
返回结果通过OUT参数/结果集无(不能返回数据)SELECT返回
可移植性❌ 差(语法差异大)❌ 差✅ 标准
适合场景复杂业务逻辑封装自动约束/审计简单查询/更新
真题演练
例题:创建存储过程,根据部门名返回该部门的员工总数和平均工资。
解(MySQL语法):
DELIMITER //
CREATE PROCEDURE GetDeptStats(IN dname VARCHAR(50), OUT emp_count INT, OUT avg_salary DECIMAL(10,2))
BEGIN
SELECT COUNT(*), AVG(salary) INTO emp_count, avg_salary
FROM emp
WHERE dept = dname;
END //
DELIMITER ;
-- 调用:
CALL GetDeptStats('销售部', @cnt, @avg);
SELECT @cnt, @avg;
-- 触发器例:自动记录工资修改日志
CREATE TRIGGER log_salary_change
AFTER UPDATE ON emp
FOR EACH ROW
BEGIN
IF OLD.salary != NEW.salary THEN
INSERT INTO salary_log(emp_id, old_salary, new_salary, change_time)
VALUES (OLD.id, OLD.salary, NEW.salary, NOW());
END IF;
END;
注意事项
• 不同DBMS存储过程语法差异巨大(MySQL/SQL Server/PostgreSQL/Oracle各不相同)
• 触发器不能过度使用——嵌套触发器可能导致性能灾难
• 存储过程参数不要用SELECT直接返回结果集(各DBMS支持不同)
• 触发器中的NEW和OLD用法:INSERT只有NEW,DELETE只有OLD,UPDATE两者都有
• SQLite的触发器语法:CREATE TRIGGER ... BEGIN ... END;(没有存储过程)
✏️ 做这节的题(4题)