☰
数据库实验二全流程:ER图设计、关系模式转换与SQL实操详解
2026/10/13 9:50:37 网站建设 项目流程

数据库这门课,学到实验二这个节点,算是真正开始上手干活了。如果实验一还在跟创建数据库、建个简单表较劲,那实验二个人版要面对的基本就是一套完整的数据库设计流程再加SQL实操。很多同学做到这里会觉得东西突然变多,其实拆开看,核心就两件事:把现实世界的业务规则翻译成关系模型,再用SQL把关系模型落地成能跑的表和数据。这篇文章我按自己做实验的完整顺序来讲,把每一步为什么要这么干、干了有什么效果、坑在哪里都说清楚。

1. 实验整体设计与需求拆解

实验二个人版,名目上虽然是“个人版”,但它考核的内容一点都不缩水。个人版的核心限制通常体现在数据规模和数据来源上——不需要像团队项目那样接多人协同、并发控制、复杂权限这些,数据量也是自己可控的几十条到几百条级别。但正因为它是一个人独立完成,整个数据库设计从需求分析到逻辑设计、物理设计、实施维护,每一环都得你自己把关,反而是把数据库设计的全流程走一遍的最佳时机。

1.1 核心需求解析

我的实验题是做一个“学生选课管理系统”,这个题目在数据库实验里属于经典中的经典,因为实体间的关系足够典型:学生(Student)和课程(Course)是多对多关系,中间必须靠选课记录(SC)来衔接,同时选课记录里还承载着成绩(Grade)这个关键属性。

题目要求拆解下来大致是这么几点:

  • 数据库名:选一个有意义的名字,比如StudentCourseDB。
  • 表数量:至少三张核心表,学生表、课程表、选课表。
  • 字段要求:每张表要有主键,字段类型合理,能够体现完整性约束。
  • 数据操作:插入、修改、删除、查询都要覆盖,查询部分要包含多表连接、分组统计、排序等常用操作。
  • 进阶要求(加分项):视图、索引、存储过程、事务处理等。

这里有个容易被忽略的点,就是为什么要用选课表而不是直接给学生表加一个“课程ID”字段。如果只考虑一个学生选一门课,那直接加字段确实省事,但现实是一个学生可以选多门课,一门课也有多个学生选,这就构成了多对多联系。多对多联系在关系模型中不能直接用两张表的外键表示,必须拆出第三张表,这张表叫“联系表”或“中间表”,里面同时存放学生外键和课程外键,再附上这个联系特有的属性(比如成绩、选课时间)。

1.2 方案选型与设计思路

数据库管理系统我选了SQL Server来练习。也有同学用MySQL或者SQLite,各有优势。SQL Server的图形化界面在实验阶段比较友好,建表、设计约束、看执行计划都可以点点鼠标完成,对初学者来说能减少很多挫败感。而且实验环境的数据库引擎就是它,保持一致方便调试。如果是自己电脑上装开发版,跟学校实验室的环境也基本一致。

表结构设计上,我的初始方案是这样的:

  • 学生表 Student:Sno(学号,主键)、Sname(姓名)、Ssex(性别)、Sage(年龄)、Sdept(系别)。
  • 课程表 Course:Cno(课程号,主键)、Cname(课程名)、Cpno(先修课号)、Ccredit(学分)。
  • 选课表 SC:Sno(外键)、Cno(外键)、Grade(成绩)。

实体属性为什么这样取舍,背后是有讲究的。比如学生表里学号用字符型而不是数值型,因为学号虽然长得像数字,但它不参与任何算术运算,而且可能存在前导零的情况,如果用整数类型,前导零会被自动吞掉,学号就失真了。再比如性别这种字段,用CHAR(1)加检查约束就够了,不需要搞成定长字符串更不用说用大文本类型。这些都是实验里面老师不会明说、但实际操作中一定会遇到的选择。

设计完成后,下一步就是把这些设计思想落到实体关系图上,也就是常说的 ER 图。

2. 概念结构与逻辑结构设计:从 ER 图到关系模式

数据库设计最怕上来就写建表语句。哪怕题目只要求你去实施,你也要先在纸上把实体、属性和联系理清楚。很多实验报告写到最后发现逻辑矛盾,比如明明是多对多却只建了两张表,根本原因都是跳过了概念设计这一步直接写 SQL。

2.1 ER 图设计要点

ER 图里包含实体、属性和联系三要素,符号约定是矩形表示实体、椭圆表示属性、菱形表示联系。学生选课系统的 ER 图核心是两个实体、一个联系:

  • 学生实体:学号、姓名、性别、年龄、系别。
  • 课程实体:课程号、课程名、先修课号、学分。
  • “选修”联系:一个学生可以选择多门课,一门课可以被多个学生选择,所以是 M:N 联系。这个联系本身有属性“成绩”,因为成绩既不属于学生也不属于课程,它是"选"这个动作产生的结果,只能挂在联系上。

画 ER 图时要特别注意,联系上的属性最容易漏。我当时就漏掉了成绩属性,先把它画成了课程的属性,后来发现如果成绩挂在课程上,那所有选这门课的同学成绩都一样,这显然不对。成绩必须挂在“选修”联系上,因为不同学生同一门课的成绩不同,同一学生不同课程的成绩也不同。这一改,后面建表结构就顺了。

2.2 关系模式转换与范式验证

ER 图转换成关系模式有固定的规则:

  • 实体转成关系(表),属性转成字段,实体的标识符作为主键。
  • 1:N 联系,可以将“1”这一侧的主键放到“N”这一侧的关系中作为外键。
  • M:N 联系,必须独立转换成一张新的关系表,表的主键是两端实体的主键组合。

所以选课表的主键是(Sno,Cno)联合主键,因为一个学生的选课记录里,只有学号和课程号合在一起才能唯一定位一条选课记录。成绩字段放在选课表中,符合前面 ER 图的逻辑转换规则。

转完关系模式,要用范式理论检验设计质量:

  • 1NF:所有属性都是不可再分的原子值,这一点只要不把多个值塞进一个字段就满足。
  • 2NF:在 1NF 基础上,消除非主属性对码的部分函数依赖。选课表里主键是(Sno,Cno),Grade 对这个联合主键是完全依赖的,因为只有当 Sno 和 Cno 都确定,Grade 才能确定。这里要注意的是,不能让某个非主属性只依赖主键的一部分,比如如果把课程名放到选课表里,那课程名只依赖 Cno,这就是部分依赖,违反 2NF,会产生数据冗余和更新异常。
  • 3NF:消除传递函数依赖。比如在学生表里加一个“系办公室电话”字段,它依赖“系别”,而“系别”依赖“学号”,这就是传递依赖,应该把系别单独拆成一张系表,或者至少将系别信息独立存放。

我们课程里的三张表设计都符合 3NF,这是数据库设计的基本及格线。高于这个标准的进一步拆分会增加查询复杂度,实验阶段做到 3NF 就够了。

2.3 为什么主键和外键的设计如此关键

主键和外键是关系数据库的骨架,它们的正确性直接决定了数据的完整性和一致性。

主键要满足两个特性:唯一性和非空性。学号是学生表的主键,任何两个学生的学号不能相同,也不允许为空。联合主键(Sno,Cno)则由两个字段共同保证唯一性,任何一个字段单独都不足以唯一标识一条记录。

外键的作用是维护引用完整性。选课表中的 Sno 必须是学生表里真实存在的学号,Cno 必须是课程表里真实存在的课程号,否则就出现选了课但学生不存在的情况,这在逻辑上是不允许的。数据库管理系统会在你定义外键约束后自动检查这些规则,不满足条件的插入和删除会直接报错。这是关系型数据库区别于文本文件最核心的一个能力。

外键设计时还要考虑一个问题:删除父表记录时,子表记录怎么办?实验阶段一般用默认的 禁止删除 策略,即学生表里有人选过课,就不允许直接删这个学生,必须先把选课记录删干净。也可以设计成级联删除,删学生时自动把他的选课记录一起删掉。两种策略各有适用场景,实验报告里最好能说明你选择某种策略的理由。

3. 物理实现:建库建表与完整性约束落地

概念设计和逻辑设计完成后,终于可以动手敲 SQL 了。建库建表看起来是最机械的步骤,但里面藏着的细节足够让大部分人改上好几轮。

3.1 建库与建表语句的核心细节

创建数据库这一步比较简单,但有几个命名和配置上的习惯值得养成:

CREATE DATABASE StudentCourseDB; GO USE StudentCourseDB; GO

如果你的实验环境是 SQL Server,GO是批处理分隔符,意味着前面的语句作为一个批次提交。USE语句则是切换当前上下文,告诉数据库引擎接下来在哪个库里面操作。这两句是后续所有操作的起点。

建表语句有讲究,我最初版本是这样写的:

CREATE TABLE Student ( Sno CHAR(10) PRIMARY KEY, Sname VARCHAR(20) NOT NULL, Ssex CHAR(2) DEFAULT '男' CHECK (Ssex IN ('男','女')), Sage TINYINT, Sdept VARCHAR(30) );

几个细节说明一下:

  • Sno CHAR(10):学号用定长字符串,长度按实际学号的位数来定。定长 CHAR 比变长 VARCHAR 在检索时略快,特别是主键经常被拿来等值查询时。
  • Sname VARCHAR(20):姓名用变长,因为不同人的名字长度不一样,用定长会浪费空间。
  • Ssex CHAR(2):加 CHECK 约束,让数据库来把关性别的合法性,不合法数据根本进不来。
  • Sage TINYINT:年龄用 TINYINT 而不是 INT,因为正常学生年龄在 0~255 之间,TINYINT 一个字节就够了,省空间而且语义明确。

课程表和选课表定义时要注意先修课这个字段,它是自引用外键:

CREATE TABLE Course ( Cno CHAR(6) PRIMARY KEY, Cname VARCHAR(50) NOT NULL, Cpno CHAR(6), Ccredit NUMERIC(3,1) CHECK (Ccredit > 0), FOREIGN KEY (Cpno) REFERENCES Course(Cno) ); CREATE TABLE SC ( Sno CHAR(10), Cno CHAR(6), Grade DECIMAL(4,1) CHECK (Grade >= 0 AND Grade <= 100), PRIMARY KEY (Sno, Cno), FOREIGN KEY (Sno) REFERENCES Student(Sno), FOREIGN KEY (Cno) REFERENCES Course(Cno) );

自引用外键在课程表里表示“这门课的先修课是哪门”,先修课的课程号必须是课程表中已有记录的课程号,这个约束可以保证不会出现先修课不存在的脏数据。

建表顺序也容易踩坑。必须先建父表再建子表,因为子表的外键引用了父表的主键。如果先建 SC 表,它会找不到 Student 和 Course 表而直接报错。同理,删除表的时候顺序反着来,先删子表再删父表。

3.2 数据类型选择的经验与禁忌

实验报告中常出现的数据类型选择问题集中在几个点上:

字段场景错误示例正确做法原因
学号INTCHAR(10)学号不参与运算,可能有前导零
性别VARCHAR(20)CHAR(2)+CHECK长度够用即可,约束保证合法值
成绩INTDECIMAL(4,1)可能有小数(如 87.5)
年龄INTTINYINT够用且省空间
课程名TEXTVARCHAR(50)TEXT 不适合做等值/排序场景

很多人会习惯性把所有字符字段都定义成 VARCHAR(255),省事是真省事,但带来的问题是数据库无法有效约束数据长度,而且在索引场景下,过长的变长字段会影响索引性能。设计字段类型时的一条黄金准则是:够用就好,能短不长,能用约束就不裸奔。

3.3 完整性约束的实施细节

约束是数据库保护数据质量的最后一道防线,它比应用程序里做校验更可靠,因为任何客户端绕过应用直连数据库时,约束依然生效。

SQL Server 里约束分这么几类:

  • 主键约束 PRIMARY KEY:唯一且非空,自动创建唯一索引。
  • 默认值约束 DEFAULT:不显式提供值时的备用值。
  • 检查约束 CHECK:限定取值范围。
  • 外键约束 FOREIGN KEY:限定引用来源。
  • 唯一约束 UNIQUE:字段值不允许重复,但允许单个 NULL。

定义约束有两种方式:建表时内联定义,或者建表后通过ALTER TABLE追加。我建议在建表语句里直接写全,一方面结构清晰一目了然,另一方面也避免刚建完表就插入数据却发现约束漏了的情况。

ALTER TABLE SC ADD CONSTRAINT FK_SC_Sno FOREIGN KEY (Sno) REFERENCES Student(Sno);

这种方式适合后期补充约束。但如果你是在实验过程中发现表结构已经建好、数据已导入,再追加约束时如果已有数据不满足约束条件,数据库会直接报错,不会帮你清理数据。所以先想清楚约束再建表,比事后修补省事得多。

4. SQL 实操:增删改查与进阶功能的完整实现

表建好之后就到了数据操作环节。实验二通常要求你把增删改查、多表连接、分组统计、视图、索引这些内容都做一遍。这部分代码量大,而且每类操作都有它的注意事项。

4.1 基础数据操作:INSERT、UPDATE、DELETE 的书写规范

插入数据时,可以先往 Student 表插入几条测试数据。生产中插入数据要注意字段顺序,严格按表结构来写,避免省略字段列表导致数据错位。更稳妥的方式是显式列出字段名:

INSERT INTO Student (Sno, Sname, Ssex, Sage, Sdept) VALUES ('2024001', '张三', '男', 20, '计算机系'); INSERT INTO Student (Sno, Sname, Ssex, Sage, Sdept) VALUES ('2024002', '李四', '女', 19, '计算机系');

批量插入可以用合并写法,注意 SQL Server 和老版 MySQL 的语法不同,SQL Server 从 2008 开始支持表值构造函数,可以用多行 VALUES 一次性插入多条:

INSERT INTO Course (Cno, Cname, Cpno, Ccredit) VALUES ('C001', '数据库原理', NULL, 4), ('C002', '数据结构', 'C001', 3), ('C003', '操作系统', 'C002', 3);

这里注意到一个点:Cpno对 C001 是 NULL,因为数据库原理这门课没有先修课。NULL 表示未知,而不是空字符串。这两者的区别在关系数据库里非常关键,后者是长度为 0 的字符串,前者是什么都没有,它参与逻辑运算时的结果通常是 UNKNOWN,而不是 TRUE 或 FALSE。

修改数据用 UPDATE,必须带 WHERE 条件,否则会更新全表。这是所有数据库开发者踩过的最深的坑之一。我在实验时故意练习了一下不带条件的 UPDATE,然后立刻用 SELECT 看了一眼全表数据,发现所有学生的系别全变成了同一个,这种错误如果发生在真实生产环境,后果不堪设想。所以养成习惯:执行 UPDATE 和 DELETE 之前,先用同条件的 SELECT 看一眼影响范围,确认无误再执行。

删除数据的规则更严格,因为外键的存在,直接删父表记录可能报错:

DELETE FROM Student WHERE Sno = '2024001';

如果这个学生有选课记录,SQL Server 会返回外键冲突错误,拒绝删除。正确做法是先删除选课表中对应的记录,再删除学生记录。这也是现实中常用的处理逻辑——先清理子表,再动父表。

4.2 查询语句:从单表查询到多表连接

查询是 SQL 的精华所在,也是实验二考核的重头戏。单表查询比较简单,重点讲两个容易出错的地方:

一是 WHERE 条件的书写顺序,虽然逻辑上条件顺序不影响结果,但要注意 AND、OR 的优先级问题。OR 会破坏条件组合,产生意料之外的结果。

SELECT Sno, Grade FROM SC WHERE Cno = 'C001' AND Grade >= 60 OR Cno = 'C002' AND Grade >= 60;

上面这个查询看起来是要找 C001 或 C002 中及格的学生,但实际执行的逻辑是(Cno='C001' AND Grade>=60) OR (Cno='C002' AND Grade>=60),如果这就是意图,那没问题。但如果你想找的是两个课都及格,那就得用括号明确优先级。括号在 SQL 里不是可选项,而是语义的一部分。

二是模糊查询的通配符。LIKE '%数据%'表示包含“数据”二字的字符串,%代表任意长度字符串,_代表单个字符。通配符使用起来很方便,但它会阻止索引的使用,在大表上性能会很差。实验数据量小感觉不出来,但要养成一个意识:能用等值查询就别用模糊查询。

多表连接查询是重点。内连接只返回两个表中匹配的记录:

SELECT Student.Sno, Student.Sname, Course.Cname, SC.Grade FROM Student INNER JOIN SC ON Student.Sno = SC.Sno INNER JOIN Course ON SC.Cno = Course.Cno;

左外连接(LEFT JOIN)则保留左表所有记录,右表没有匹配就返回 NULL:

SELECT Student.Sno, Student.Sname, SC.Cno, SC.Grade FROM Student LEFT JOIN SC ON Student.Sno = SC.Sno;

这个查询可以找出所有学生,包括那些没有选任何课的学生,他们的 Cno 和 Grade 会是 NULL。这在统计选课覆盖率时非常有用。

连接查询最容易犯的错误是漏掉连接条件,导致笛卡尔积。比如忘了写ON SC.Sno = Student.Sno,结果会是学生数和选课记录数的乘积,数据多到让你怀疑人生。排查这种问题时,先看结果的行数是否合理,再逐步加条件缩小范围。

4.3 聚合、分组与排序的常见误区

分组统计是实验二的必考内容,典型需求如“查询各门课程的选课人数和平均成绩”:

SELECT Cno, COUNT(*) AS 选课人数, AVG(Grade) AS 平均成绩 FROM SC GROUP BY Cno;

GROUP BY 有个铁律:SELECT 中出现的非聚合列,必须出现在 GROUP BY 子句中。刚才的查询如果改成SELECT Cno, Sno, COUNT(*) ... GROUP BY Cno,Sno 不在分组里,数据库会直接报错。如果数据库允许你写,那它返回的 Sno 也是不确定的,没有任何意义。

HAVING 和 WHERE 的分工问题也经常搞混。WHERE 在分组前过滤行,HAVING 在分组后过滤组:

SELECT Cno, AVG(Grade) AS 平均成绩 FROM SC WHERE Grade IS NOT NULL GROUP BY Cno HAVING AVG(Grade) >= 60;

这个查询先排除成绩为空的行,然后按课程分组,最后只保留平均成绩在 60 分以上的组。执行顺序是 WHERE 先于 GROUP BY,HAVING 后于 GROUP BY,理解这个顺序对排查错误至关重要。

排序用 ORDER BY,注意多字段排序的优先级,先排第一个字段,相同再排第二个:

SELECT Sno, Cno, Grade FROM SC ORDER BY Sno ASC, Grade DESC;

4.4 视图、索引与存储过程的实操要点

视图是虚拟表,它不存储数据,只是一条保存的查询语句。实验里创建视图的意义在于展示你对逻辑数据独立性的理解。比如创建一个“计算机系学生选课成绩视图”:

CREATE VIEW CS_Student_Grade AS SELECT Student.Sno, Student.Sname, Course.Cname, SC.Grade FROM Student JOIN SC ON Student.Sno = SC.Sno JOIN Course ON SC.Cno = Course.Cno WHERE Student.Sdept = '计算机系';

创建完视图后,可以像查表一样查询它。视图的一个实用场景是权限控制,比如给助教一个视图,他只能看成绩但不能看其他敏感字段。另一个场景是简化复杂查询,把固定的连接逻辑封装起来,业务侧直接查视图就行。

索引这块,实验要求不高,但理解它的原理很有价值。索引就像书的目录,没有目录时要一页页翻,有目录就能直接定位。在 SELECT 频繁的字段上建索引,在 UPDATE/DELETE/INSERT 频繁的字段上要谨慎建索引,因为每次写操作都要同步更新索引,反而拖慢性能。实验里可以演示一下创建和维护索引的语法:

CREATE INDEX idx_SC_Grade ON SC(Grade);

注意,主键和外键字段在 SQL Server 里默认会自动建立索引,不需要手动重复创建。手工创建索引时不要建立在数据区分度很低的字段上,比如性别这种只有“男”“女”两值的字段,索引帮助不大。这个经验在真实工作中非常实用。

存储过程是实验的可选加分项,它的核心价值在于封装业务逻辑。比如实现一个“根据学号查询学生已修学分总数”的存储过程:

CREATE PROCEDURE GetTotalCredits @Sno CHAR(10) AS BEGIN SELECT Student.Sno, Student.Sname, SUM(Course.Ccredit) AS 总学分 FROM Student JOIN SC ON Student.Sno = SC.Sno JOIN Course ON SC.Cno = Course.Cno WHERE Student.Sno = @Sno GROUP BY Student.Sno, Student.Sname; END;

调用存储过程用EXEC GetTotalCredits '2024001'。存储过程和视图的区别在于,它不仅能查询,还能执行包含事务的复杂逻辑,比如选课操作(检查学分、检查先修课、插入选课记录)可以放在一个存储过程里,通过事务保证一致性。

5. 常见问题与排查技巧实录

实验做得再顺利,也免不了遇到一些让人头疼的报错。这里把我在实践中遇到的典型问题整理成速查清单,几乎每个数据库初学者都会碰到。

5.1 常见报错信息速查表

报错现象根本原因解决方案
建表失败,提示外键不存在子表先于父表创建先建父表再建子表,或先建表后加外键
插入数据报主键重复主键字段重复或未设自增检查主键字段值是否重复,必要时清空表重插
删除父表记录报冲突子表存在引用该记录的外键先删除子表相关记录,再删父表,或改用级联删除
UPDATE 影响行数远大于预期漏写 WHERE 条件立即回滚事务或从备份恢复,养成先 SELECT 再 UPDATE 的习惯
分组查询报错列不在 GROUP BY违反分组规则将非聚合列加入 GROUP BY,或去掉该列
中文乱码数据库/表/列字符集不一致建库时统一使用简体中文兼容的字符集
模糊查询查不到结果使用了中文通配符而非英文百分号检查%是否为英文半角符号
视图创建失败权限不足或查询语句有语法错误检查权限,先跑 SELECT 语句验证逻辑

5.2 三个最容易踩的隐蔽坑

第一个是 NULL 值引发的连锁问题。COUNT(Grade)和COUNT(*)结果可能不一样,前者不统计 NULL,后者统计全部行。求平均分时如果把缺考学生的成绩设为 NULL,AVG自动跳过 NULL,结果会偏高。如果要把缺考当成 0 分处理,就得用ISNULL或COALESCE函数显式转:

SELECT AVG(ISNULL(Grade, 0)) FROM SC;

第二个是隐式类型转换。比如学号字段是 CHAR(10),而你在查询时传入的是整数 2024001,数据库会尝试把整型转换成字符型再比较。数据量小的时候感觉不到,数据量大时这种转换会让索引失效,查询性能直线下降。保持字段类型和查询参数类型一致,是最基本的素养。

第三个是外键约束的方向性问题。选课表中 Sno 是学生表主键的引用,但它本身也可以被其他表引用。比如如果要建一个“退课记录”表,它里面的 Sno、Cno 又要引用选课表里的 Sno、Cno。这种链式引用在删除时会造成联动问题,需要格外小心。实验阶段不做这么复杂,但要意识到外键关系不是单向的,它的维护成本是向两边延伸的。

5.3 实验报告的检查清单

做完整套实验后,正式提交之前,我建议按这个清单自查一遍:

  • 三张表的主键、外键、CHECK、DEFAULT、UNIQUE 约束是否齐全。
  • 每张表至少插入 5 条以上有效数据,且数据类型符合定义。
  • 查询语句是否覆盖了等值查询、范围查询、模糊查询、排序。
  • 是否至少包含一个多表连接查询,连接条件有没有缺漏。
  • 是否包含一个带 GROUP BY 和 HAVING 的分组统计查询。
  • 是否创建了至少一个视图,并用视图执行了一次查询。
  • 是否说明了索引选择的理由,不能为了建索引而建索引。
  • 删除操作是否体现了外键约束的引用完整性,不能全表删除。
  • 所有 SQL 语句都能重新连贯执行,不要有粘贴一半的情况。

这个清单其实就是实验考核评分点的映射。数据库实验的分数差异,往往不是差在代码量上,而是差在这些规范性和完整性细节上。建了三张表但没有外键约束,和建了三张表但约束完备,同样的问题看上去差不多,得分差距却是档次性的。

6. 深度扩展:从实验二通向真实项目开发的三个关键认知

实验二做完,数据库原理这门课最核心的技术点基本都过了一遍。但站在一个从业者角度回头看这个实验,我想补三个可能超出课程要求、但对后续开发非常有价值的认知。

6.1 事务管理:为什么实验里没用到但生产里离不开

事务是数据库保证数据一致性的核心机制。先修课、状态修改、选课扣学分等操作,真实的业务场景中都是多条 SQL 一起执行,比如“扣库存+生成订单+更新余额”,任何一步失败,整个操作都应该回滚,不能出现库存扣了但订单没生成的情况。实验里的数据量小,单条语句失败了大不了删掉重来。但真实系统中,必须把所有关联操作包在事务里:

BEGIN TRANSACTION; UPDATE SC SET Grade = 90 WHERE Sno = '2024001' AND Cno = 'C001'; UPDATE Student SET Sage = Sage + 1 WHERE Sno = '2024001'; COMMIT TRANSACTION;

中间任何一句报错,执行ROLLBACK TRANSACTION就能回滚到事务开始之前。这个机制在并发场景下尤其重要,多个用户同时操作同一份数据时,事务的隔离级别决定了你能否避免读到脏数据或幻读。

6.2 日志与备份:从删库到跑路的距离只有一条命令

做实验时,一条DROP DATABASE就能让三张表全部消失,你只需要用脚本重新建库建表,几分钟就恢复。但在真实环境,数据库里存的是用户数据、订单记录、财务流水,一次误操作可能就是灾难。实验阶段养成一个习惯:关键操作之前先备份。在 SQL Server 里,备份只需要一句:

BACKUP DATABASE StudentCourseDB TO DISK = 'D:\backup\StudentCourseDB.bak';

恢复也简单:

RESTORE DATABASE StudentCourseDB FROM DISK = 'D:\backup\StudentCourseDB.bak';

有备份兜底,很多操作你才敢放手去做。这个习惯比学多少 SQL 技巧都重要。

6.3 索引思维:查询优化从实验阶段就要建立

实验数据量小,一条没索引的查询也是毫秒级返回,你根本察觉不到性能差异。但真实生产环境,千万行级别的表,没有索引的查询可能从毫秒变成几十秒,接口直接超时。

理解索引,核心是理解 B+ 树和主键索引与非主键索引的区别。主键索引(聚簇索引)决定了数据在磁盘上的物理存储顺序,非主键索引(二级索引)保存的是主键值,查到后再回表取数据。这也是为什么建议主键用自增整数而不是随机字符串的原因之一,自增让新数据追加写入而不是随机插入,减少页分裂,提升写入性能。

实验里建索引时,可以故意对比一下有索引和没索引的执行计划差异,这个习惯会让你在以后写复杂查询时主动去想:这个 WHERE 条件能不能用索引?函数包裹字段后索引还能不能用?前缀查询用 LIKE 'abc%' 能用索引,LIKE '%abc' 就用不了。这些知识课本上有,但只有亲手实践过,印象才真正深刻。

写在最后

这个实验二做下来,最大的体会是:数据库设计像盖房子,需求分析是打地基,ER 图是画图纸,建表和 SQL 才是砌砖。很多人一上来就砌砖,地基没打牢,图纸也没画,最后房子歪了都不知道问题在哪。认真把 ER 图画明白、把关系模式转换规则吃透、把三种范式验证走一遍,后面写 SQL 完全是水到渠成的事。

再分享一个小技巧:实验过程中所有 SQL 脚本都用文本文件保存下来,按“建库、建表、插入、查询、视图、存储过程”分节存放,每执行成功一段就在头部加一行注释说明这段的用途。提交实验时直接把脚本文件附上,报告里对应截图也方便。更重要的是,你自己的脚本以后做复习,或者面试前手写 SQL,翻出来就是一份极好的资料,比临时翻书高效得多。

数据库这门课,实验二是一个真正的分水岭。跨过它,你对“数据”二字的理解就不再是 Excel 表格,而是一套有约束、有关系、有逻辑的系统工程。后面的实验还有权限管理、并发控制、数据库恢复这些更深入的内容,但基础打在这里,后面每一步都会顺很多。

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

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

立即咨询