☰
数据库原理第四版课件拆解:从理论到SQL实验的避坑指南
2026/10/3 15:51:31 网站建设 项目流程

简介:这份《数据库原理(第四版)》课件面向数据库初学者与希望系统梳理理论的专业人员,覆盖从第一章绪论到第十章的核心内容,重点讲解数据库系统开发流程、数据模型设计以及网状模型的实现细节。课件以数据模型为主线,深入剖析概念模型、逻辑结构与物理结构,并借助学生、宿舍、教师、教研室及家庭关系等实例,直观展示网状模型的数据结构、数据操纵与完整性约束,帮助读者理解一对多联系、码字段与DBTG系统的约束机制。资源包内含1个PPT文件,压缩后约289KB,以幻灯片形式呈现,便于课堂演示与自学翻阅。目前已有168人学习下载,适合需要夯实数据库基础、掌握网状模型建模思路并衔接后续关系模型与SQL学习的读者参考使用。

1. 数据库原理(第四版)课件:从“看得懂”到“讲得出、跑得通”的拆解路径

很多人拿到《数据库原理(第四版)》课件的第一反应是:PDF 翻一遍,概念都认识,可一到实验课建表、写事务、调索引就卡壳。这门课的核心不是背定义,而是把关系模型、SQL、事务、并发控制、索引与查询优化串成一条能动手验证的链路。课件本身通常覆盖 ER 模型、关系代数、范式分解、SQL 语法、事务 ACID、封锁协议、日志恢复、索引结构这些模块,但真正让学习者翻车的,往往是“理论听得懂、实验跑不通”。这篇笔记面向三类人:正在跟课但实验报告写不动的学生、需要把课件内容转成可演示案例的助教、以及想用一套最小环境复现数据库核心机制的开发者。我会按“先立概念、再搭环境、再逐模块跑通、最后排坑”的顺序,把课件里最常考也最常翻车的几个点拆成可抄作业的步骤。数据库原理这门课,光看课件不够,得让 SQL 在终端里真的返回结果,让事务在并发下真的阻塞,让索引在 EXPLAIN 里真的生效。

2. 把课件里的关系模型落到一张能跑的表:环境、DDL 与约束验证

2.1 选型理由:为什么用 SQLite + MySQL 双环境对照

课件里的例子通常偏理论,比如“学生-课程-选课”三张表,但不同教材用的方言不一样。我的习惯是:用 SQLite 做单机快速验证,因为它零配置、文件即数据库,适合验证 DDL、约束、简单事务;用 MySQL 8.x 做并发和锁的验证,因为 SQLite 的写锁是库级,讲行锁、间隙锁会失真。两者对照,能让你看清“标准 SQL”和“具体实现”的边界。安装上,SQLite 直接下载可执行文件即可,MySQL 建议用 Docker 起一个最小实例,避免污染本机。

# 启动一个 MySQL 8 容器,映射到本机 3306,密码设为 course123 docker run -d --name db-course \ -e MYSQL_ROOT_PASSWORD=course123 \ -p 3306:3306 \ mysql:8.0 \ --default-authentication-plugin=mysql_native_password

参数说明:--default-authentication-plugin是为了兼容老客户端;端口映射如果本机已有 MySQL,改成 3307 避免冲突。启动后用docker exec -it db-course mysql -uroot -pcourse123进入命令行。

2.2 建表与约束:把课件里的 ER 图翻译成 DDL

课件里常见的 ER 图有实体、属性、联系,落到 DDL 时要处理主键、外键、唯一约束、非空约束。以“学生-课程-选课”为例,选课表的主键是联合主键,外键分别指向学生和课程。很多同学在实验里只建表不加外键,导致后面讲参照完整性时没有素材。

-- 学生表:学号为主键,姓名非空,年龄有检查约束 CREATE TABLE student ( sno CHAR(8) PRIMARY KEY, sname VARCHAR(20) NOT NULL, age INT CHECK (age BETWEEN 15 AND 60), dept VARCHAR(30) ); -- 课程表:课程号为主键,学分有默认值 CREATE TABLE course ( cno CHAR(6) PRIMARY KEY, cname VARCHAR(40) NOT NULL, credit DECIMAL(3,1) DEFAULT 2.0 ); -- 选课表:联合主键,两个外键,成绩允许为空 CREATE TABLE sc ( sno CHAR(8), cno CHAR(6), grade DECIMAL(4,1), PRIMARY KEY (sno, cno), FOREIGN KEY (sno) REFERENCES student(sno) ON DELETE CASCADE, FOREIGN KEY (cno) REFERENCES course(cno) ON DELETE RESTRICT );

逻辑说明:ON DELETE CASCADE表示删除学生时自动删除其选课记录,ON DELETE RESTRICT表示课程被选后不能直接删。这两个动作是课件里“参照完整性”的典型考点,实验时故意删一条被引用的课程,观察报错信息,比背定义有效。参数上,CHAR(8)和VARCHAR(20)的选择要看实际数据长度,学号固定 8 位用 CHAR 更省空间,姓名变长用 VARCHAR。

2.3 用 INSERT + SELECT 验证约束是否真的生效

建完表后,插入几条数据,然后故意违反约束,看数据库返回什么错误。这一步是很多实验报告缺失的“反向验证”。

INSERT INTO student VALUES ('20240001','张三',20,'计算机'); INSERT INTO course VALUES ('C001','数据库原理',3.0); INSERT INTO sc VALUES ('20240001','C001',88.5); -- 故意插入不存在的学号,应报外键错误 INSERT INTO sc VALUES ('99999999','C001',90); -- 故意插入超范围年龄,应报 CHECK 错误 INSERT INTO student VALUES ('20240002','李四',200,'数学');

执行后你会看到类似ERROR 1452和ERROR 3819的报错。把报错原文记进实验报告,说明“约束在数据库层拦截了非法数据”,这比写“约束保证了完整性”更有说服力。如果用的是 SQLite,外键默认不开启,需要执行PRAGMA foreign_keys = ON;,这是一个经典坑,后面避坑章节会展开。

3. 事务与并发控制:在课件示例上跑出阻塞和死锁

3.1 事务 ACID 的验证:用 BEGIN / COMMIT / ROLLBACK 观察

课件讲 ACID 时通常给一个转账例子。要验证原子性,可以开两个会话,一个执行转账的一半,另一个查询余额,看是否读到中间状态。MySQL 默认隔离级别是 REPEATABLE READ,SQLite 默认是 SERIALIZABLE,行为不同,正好用来对照。

-- 会话 A BEGIN; UPDATE account SET balance = balance - 100 WHERE id = 1; -- 此时不提交,去会话 B 查询 -- 会话 B SELECT balance FROM account WHERE id = 1; -- 在 MySQL RR 下仍读到旧值

逻辑说明:会话 A 未提交时,会话 B 在 MySQL 的 REPEATABLE READ 下读到的是快照旧值,体现隔离性;如果会话 A 执行ROLLBACK,余额恢复,体现原子性。参数上,autocommit变量控制是否自动提交,实验时建议显式SET autocommit = 0;,避免每条语句自动提交导致事务边界模糊。

3.2 悲观锁与乐观锁:课件里常提,实验里怎么落地

热搜词里出现了“数据库乐观锁、悲观锁的实现原理和适用场景”,这正好是课件并发控制章节的延伸。悲观锁用SELECT ... FOR UPDATE在事务里锁住行,适合写冲突高的场景;乐观锁用版本号字段,更新时检查版本,适合读多写少。

-- 悲观锁:在事务中锁定行,直到提交 BEGIN; SELECT * FROM account WHERE id = 1 FOR UPDATE; UPDATE account SET balance = balance - 100 WHERE id = 1; COMMIT; -- 乐观锁:表里加 version 字段 UPDATE account SET balance = balance - 100, version = version + 1 WHERE id = 1 AND version = 3; -- 如果受影响行数为 0,说明版本已被别人改过,需要重试

参数说明:FOR UPDATE在 MySQL 中会加排他锁,若 WHERE 条件没有索引,可能升级为表锁,这是实验里最容易翻车的地方。乐观锁的version字段建议用 INT,每次更新自增,应用层判断affected_rows决定是否重试。适用场景上,悲观锁适合库存扣减这类强一致需求,乐观锁适合文章点赞这类允许重试的场景。

3.3 死锁复现:两个会话互相等锁

课件讲死锁时往往只给定义,实际复现一次印象更深。开两个会话,按相反顺序更新两行。

-- 会话 A BEGIN; UPDATE account SET balance = balance - 10 WHERE id = 1; -- 不提交,去会话 B -- 会话 B BEGIN; UPDATE account SET balance = balance - 10 WHERE id = 2; -- 不提交,回会话 A -- 会话 A UPDATE account SET balance = balance - 10 WHERE id = 2; -- 阻塞 -- 会话 B UPDATE account SET balance = balance - 10 WHERE id = 1; -- 死锁,MySQL 会回滚其中一个

执行后会看到ERROR 1213: Deadlock found。把死锁日志用SHOW ENGINE INNODB STATUS导出来,能看到两个事务的等待关系。这个实验的价值在于:让你理解“加锁顺序一致”不是口号,而是避免死锁的具体手段。

4. 索引与查询优化:让课件里的“理论代价”变成 EXPLAIN 里的数字

4.1 B+ 树索引在磁盘上的直观理解

课件讲 B+ 树时通常画图,但学生很难感知“为什么索引能加速”。可以在 MySQL 里建一张十万行的表,对比有无索引的查询耗时。

-- 建一张测试表 CREATE TABLE big_table ( id INT PRIMARY KEY AUTO_INCREMENT, val INT, name VARCHAR(50) ); -- 插入十万行(用存储过程或脚本) INSERT INTO big_table (val, name) SELECT FLOOR(RAND()*10000), CONCAT('name', FLOOR(RAND()*10000)) FROM information_schema.columns a, information_schema.columns b LIMIT 100000; -- 无索引查询 EXPLAIN SELECT * FROM big_table WHERE val = 5000; -- 加索引后再看 CREATE INDEX idx_val ON big_table(val); EXPLAIN SELECT * FROM big_table WHERE val = 5000;

逻辑说明:第一次EXPLAIN的type是ALL,表示全表扫描;加索引后变成ref,rows估算值大幅下降。参数上,EXPLAIN的key列显示实际使用的索引,filtered表示过滤比例。这个对比能让你在实验报告里写出“索引将扫描行数从十万降到约十行”的具体数字,而不是空谈“索引提高效率”。

4.2 复合索引的最左前缀:课件考点,实验验证

复合索引(a, b, c)在查询条件包含a、a,b、a,b,c时生效,跳过a直接查b不生效。这个规则背起来容易,验证一次就记住。

CREATE INDEX idx_abc ON big_table(val, name); -- 生效:条件包含最左列 val EXPLAIN SELECT * FROM big_table WHERE val = 100; -- 不生效:跳过 val 直接查 name EXPLAIN SELECT * FROM big_table WHERE name = 'name100';

执行后看key列,第一条显示idx_abc,第二条显示NULL。注意,如果查询只需要索引列,可能走覆盖索引,Extra显示Using index,这是另一种优化点。参数上,索引列顺序要根据 WHERE 和 ORDER BY 的实际使用频率定,不是越多越好,写多的表要控制索引数量。

4.3 慢查询日志:把“感觉慢”变成“证据慢”

课件里讲查询优化往往停留在理论,实际排查要用慢查询日志。MySQL 开启慢查询日志后,超过阈值的 SQL 会被记录。

SET GLOBAL slow_query_log = ON; SET GLOBAL long_query_time = 0.5; SET GLOBAL slow_query_log_file = '/var/log/mysql/slow.log'; -- 执行一条没走索引的查询 SELECT * FROM big_table WHERE name = 'name9999';

然后在日志文件里能看到这条 SQL 的执行时间、扫描行数。参数说明:long_query_time单位是秒,实验环境设 0.5 秒即可;生产环境通常设 1 到 2 秒。log_queries_not_using_indexes可以额外记录未走索引的查询,但日志量会大,按需开启。

5. 避坑与排查:课件实验里最容易翻车的五个点

5.1 外键不生效:现象是插入非法数据没报错

现象:在 SQLite 里建了外键,插入不存在的学号,数据库居然接受了。原因:SQLite 默认关闭外键约束,必须显式开启。解决:每次连接后执行PRAGMA foreign_keys = ON;,或者在连接字符串里加参数。MySQL 的 MyISAM 引擎也不支持外键,建表时要确认ENGINE=InnoDB。

5.2 事务没回滚:现象是 ROLLBACK 后数据还在

现象:执行BEGIN后更新数据,再ROLLBACK,查询发现数据没变回去。原因:很多客户端默认autocommit=1,BEGIN之前每条语句已经自动提交,或者 DDL 语句会隐式提交。解决:先SET autocommit = 0;,并且事务里避免混入CREATE、ALTER这类 DDL。用SELECT @@autocommit;确认当前值。

5.3 死锁排查找不到头绪:现象是应用报死锁但不知道哪两条 SQL 冲突

现象:MySQL 返回Deadlock found,但业务代码里看不出哪两个事务互相等。原因:死锁信息在 InnoDB 状态里,默认不打印到错误日志。解决:执行SHOW ENGINE INNODB STATUS\G,看LATEST DETECTED DEADLOCK段落,里面会列出两个事务持有的锁和等待的锁。把这段日志保存下来,对照代码里的加锁顺序,通常能定位到两个更新顺序相反的接口。

5.4 索引建了没用上:现象是 EXPLAIN 里 key 为 NULL

现象:明明建了索引,查询还是全表扫描。原因可能有三种:查询条件对索引列做了函数操作,比如WHERE YEAR(create_time) = 2024;隐式类型转换,比如字符串列用数字查;或者复合索引跳过了最左列。解决:把函数移到等号右边,比如WHERE create_time >= '2024-01-01' AND create_time < '2025-01-01';用SHOW WARNINGS看优化器改写后的 SQL;用EXPLAIN确认key列。

5.5 隔离级别理解偏差:现象是同一事务里两次查询结果不同

现象:在 REPEATABLE READ 下,同一个事务里两次SELECT结果不一致。原因:第一次查询后别人提交了修改,而你的快照没有更新,或者你误用了READ COMMITTED。解决:用SELECT @@transaction_isolation;确认当前隔离级别;理解 MySQL 的 RR 是通过 MVCC 快照实现的,但SELECT ... FOR UPDATE会读最新版本。实验时把隔离级别在READ COMMITTED和REPEATABLE READ之间切换,观察同一段代码的不同输出。

6. 把课件变成可演示的课程设计:一个最小验证脚本与我的习惯

如果你要把这套内容用于课程设计或实验答辩,我建议写一个一键验证脚本,把建表、插入、事务、索引、死锁复现串起来。下面是一个用 Python + SQLite 的最小示例,重点不是代码多漂亮,而是每一步都有断言,失败时能定位到具体环节。

import sqlite3 conn = sqlite3.connect(':memory:') conn.execute('PRAGMA foreign_keys = ON;') # 关键:开启外键 cur = conn.cursor() # 建表 cur.executescript(''' CREATE TABLE student (sno TEXT PRIMARY KEY, sname TEXT NOT NULL); CREATE TABLE course (cno TEXT PRIMARY KEY, cname TEXT NOT NULL); CREATE TABLE sc ( sno TEXT, cno TEXT, grade REAL, PRIMARY KEY (sno, cno), FOREIGN KEY (sno) REFERENCES student(sno), FOREIGN KEY (cno) REFERENCES course(cno) ); ''') # 插入合法数据 cur.execute("INSERT INTO student VALUES ('20240001','张三')") cur.execute("INSERT INTO course VALUES ('C001','数据库原理')") cur.execute("INSERT INTO sc VALUES ('20240001','C001',88.5)") # 验证外键:插入非法学号应抛异常 try: cur.execute("INSERT INTO sc VALUES ('99999999','C001',90)") print('外键未生效,需要检查 PRAGMA') except sqlite3.IntegrityError as e: print('外键生效,错误信息:', e) # 验证事务回滚 cur.execute("BEGIN") cur.execute("UPDATE sc SET grade = 100 WHERE sno = '20240001'") cur.execute("ROLLBACK") cur.execute("SELECT grade FROM sc WHERE sno = '20240001'") print('回滚后成绩:', cur.fetchone()[0]) # 应为 88.5 conn.close()

逻辑说明:PRAGMA foreign_keys = ON必须在建表前执行;executescript会隐式提交,所以事务验证放在后面单独做;ROLLBACK后查询确认数据恢复。参数上,SQLite 的内存数据库适合快速验证,换成文件数据库只需把:memory:改成路径。这个脚本可以直接放进实验报告,作为“可复现证据”。

进阶一点,你可以把 MySQL 的死锁复现也脚本化,用两个线程模拟两个会话,捕获Deadlock异常后打印SHOW ENGINE INNODB STATUS。这样答辩时不用现场手敲两个终端,演示更稳。我自己的习惯是:每学完课件一章,就写一个最小验证脚本,脚本里必须有至少一个“故意失败”的用例。数据库原理这门课,失败用例比成功用例更能帮你记住边界。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询