简介:本资源是太原理工大学软件工程专业《数据库概论》课程的完整实验报告,面向高校数据库初学者、软件工程专业学生及SQL实践入门者,聚焦SQL Server 2016环境下数据库核心操作能力训练。报告覆盖数据定义(CREATE/ALTER/DROP TABLE)、索引创建(唯一索引、聚簇索引)、视图构建(如IS_Student系别筛选视图)及数据增删改查全流程,含大量可直接复用的SQL语句示例与关键注意事项,有效解决理论脱离实操、语法易错难调试等学习痛点。资源为单个Word文档(.docx),共1个文件,大小1.69MB,内容排版规范、结构清晰,含实验目的、平台配置、分步代码、执行结果与总结反思,便于对照学习与课堂复盘。已有312人学习下载,适合用于课程复习、实验预习、SQL语法速查及数据库基础项目参考。
1. 数据库不是“电子表格”,而是你家的智能管家:一张表管不住全校学生,但一个数据库能管住十年选课记录
你有没有试过用 Excel 管理全班同学的选课?一开始挺好:A列学号、B列姓名、C列选了哪门课、D列成绩……可刚填到第50行,问题就来了:
- 张立改了专业,得在“学生信息表”里改一次,在“选课表”里再找他所有记录挨个改一遍;
- 刘晨重修了《数据库》,新成绩一填,旧成绩没删干净,期末统计时算出两个分数;
- 更糟的是,有人手滑把“CS”输成“cs”,系统不拦着,查“计算机科学系”时这条记录就凭空消失——没人知道它去了哪。
这不是 Excel 的错,是它根本没设计来干这个。而数据库(Database),就是专为解决这类“多人协作、长期维护、数据不能乱”问题造出来的系统。它不像 Excel 那样把所有信息摊在一张纸上,而是像你家的智能管家:
- 它记住谁是谁(Student 表),谁教什么课(Course 表),谁选了哪门课、得了多少分(SC 表);
- 它强制规定“学号必须唯一”“成绩只能是0–100”“选课必须先有学生、再有课程”;
- 它允许你一句问话就拿到答案:“请把所有计算机系、年龄小于20岁的男生姓名和出生年份列出来”,不用手动翻三张表、对十次行。
这份来自太原理工大学软件工程专业的《数据库概论》实验报告,不是一堆冷冰冰的代码截图,而是一套真实可运行的数据库思维训练手册:从建表、插数据、改结构,到设约束、建索引、写视图,再到用嵌套查询挖出隐藏关系——每一步都在教你:怎么让数据自己守规矩,而不是靠人盯着别出错。搞懂它,你就跨过了“会点 SQL”和“真能管好数据”的那道门槛。
2. 为什么非得用数据库?——不是为了炫技,而是为了不让“改一条数据,崩三张表”
2.1 没有数据库时,你的数据就像散落一地的乐高积木
想象你负责管理一个学院的选课系统,初期用 Excel 处理:
- 学生信息存
student.xlsx; - 课程信息存
course.xlsx; - 选课记录存
sc.xlsx(Sno + Cno + Score)。
表面看分工明确,实际埋着三颗雷:
第一颗雷:数据重复,改一处,漏八处
张立从“信息系”转到“计算机系”,你在student.xlsx里改了 Sdept,但sc.xlsx里他的所有选课记录旁都没标系别——下次导出“计算机系选课名单”,系统根本不知道他现在属于哪边,只能靠人工核对。
第二颗雷:逻辑断裂,删一行,断一串关系
某天你想清理已停开的《数据处理》课(Cno='6'),在course.xlsx里删了它,但sc.xlsx里还有学生选了这门课的记录。这些记录变成“幽灵数据”:查不到课程名,统计总分时还参与计算,结果全是错的。
第三颗雷:无校验机制,输错不提醒,错进错出
有人把“刘晨”的性别输成“女1”,Excel 不报错;把成绩输成“150”,Excel 也不拦——等期末汇总发现平均分102,回头翻几百行才发现是某条数据捣的鬼。
这些不是“操作不熟练”,而是工具能力边界决定的必然结果。Excel 是“文档型工具”,数据库是“关系型系统”——前者管内容,后者管关系、管规则、管一致性。
2.2 数据库怎么把“散装数据”拧成一股绳?靠三根核心支柱
这份实验报告里反复出现的CREATE TABLE、ALTER TABLE、INSERT INTO,不是语法练习,是在搭建这三根支柱:
(1)结构定义:先画图纸,再盖楼
CREATE TABLE Student ( Sno CHAR(8) PRIMARY KEY, Sname CHAR(10) NOT NULL, Ssex CHAR(2) CHECK(Ssex IN ('男','女')), Sage SMALLINT, Sdept CHAR(20), Sclass CHAR(4) );这段代码干了四件事:
- 定身份:
Sno是主键(PRIMARY KEY),就像身份证号,全表唯一、不能为空; - 立规矩:
Sname NOT NULL表示姓名必填,Ssex CHECK(...)表示性别只能填“男”或“女”,输别的直接被拒; - 划范围:
SMALLINT比INT节省空间,因为年龄不会超过32767;CHAR(8)固定长度,适合学号这种定长字段; - 留接口:后面建
SC表时,FOREIGN KEY(Sno) REFERENCES Student(Sno)就靠这个Sno字段,把学生和选课死死绑住。
提示:实验报告中
Student表两次出现(实验二 vs 实验三),你会发现第二次多了CHECK和NOT NULL——这就是从“能存数据”升级到“存对数据”的关键跃迁。
(2)关系绑定:让三张表变成一个整体
光有三张表没用,关键是怎么让它们互相认得。实验报告里最常出现的三组关联,就是数据库的“关系骨架”:
Student↔SC:靠Sno(学生学号);Course↔SC:靠Cno(课程号);Course↔Course(自关联):靠Cpno(先修课号),比如《数据库系统原理》的Cpno='5',指向《数据结构》的Cno='5'。
这种绑定不是靠人脑记忆,而是由数据库引擎实时维护。当你执行:
DELETE FROM Course WHERE Cno = '5';如果SC表里还有学生选了这门课,SQL Server 会直接报错:
The DELETE statement conflicted with the REFERENCE constraint "FK_SC".
意思是:“不行,有人正指着这门课呢,删了就断链了!”
这就是外键约束(FOREIGN KEY)的力量——它不让你犯错,而不是等你犯错后再补救。
(3)操作原子化:要么全成,要么全退,绝不半吊子
实验报告里有一句容易被忽略的操作:
SELECT * INTO tmpSC FROM SC; DELETE FROM tmpSC WHERE Sno='20100001' AND Cno='1';为什么要先SELECT INTO复制一张临时表,再删?因为DELETE是事务性操作:
- 如果直接删
SC表里的记录,删到一半断电,可能只删了一半,数据就残了; - 而
SELECT INTO是完整复制,失败了顶多没新表,原表毫发无损; - 后续在
tmpSC上操作,相当于在“沙盒”里演练,确认无误再替换原表。
通俗地说:数据库把每次修改都当成一次“合同签署”——签字前可以反悔,签完就生效,不存在“签了一半又涂改”的情况。
2.3 为什么学生第一次写UPDATE总爱翻车?因为没理解“条件漏写=全表遭殃”
实验报告里有个典型操作:
-- ✅ 正确:只改指定学生 UPDATE Student SET Sage = 22 WHERE Sno = '20100001'; -- ❌ 危险:漏了 WHERE,全表年龄变22! UPDATE Student SET Sage = 22;初学者常栽在这儿,不是手抖,是没建立“数据库没有默认上下文”的认知。Excel 里你点中某行再点编辑,动作天然局限在当前行;但 SQL 的UPDATE、DELETE默认作用于整张表,必须靠WHERE显式圈出目标。
这份报告里所有UPDATE示例都带WHERE,且条件足够具体(Sno='...'或Sname='...'),就是在训练一种肌肉记忆:写修改语句,第一反应不是敲 SET,而是先想 WHERE。
更进一步,报告还示范了“用子查询做动态条件”:
-- 把计算机系全体学生的成绩加5分 UPDATE SC SET Score = Score + 5 WHERE 'CS' = (SELECT Sdept FROM Student WHERE Student.Sno = SC.Sno);这里WHERE后不是固定值,而是一个实时查询——数据库会为SC表每一行,去Student表里查对应学生的系别,匹配上才更新。这种能力,Excel 根本无法实现。
3. 到底什么是“数据完整性”?不是给数据贴标签,而是给数据库装刹车片
3.1 完整性不是“数据很全”,而是“数据不敢乱来”
很多初学者看到“数据完整性”这个词,第一反应是:“哦,就是数据要填满,不能有空”。这是典型误解。
实验报告中“实验三:数据完整性”的标题下,真正做的事是:
- 给
Student.Ssex加CHECK(Ssex IN ('男','女')); - 给
SC.Grade加CHECK(Grade > 0 AND Grade < 100); - 给
Course.Cname加UNIQUE; - 给
Student.Sno设PRIMARY KEY。
这些操作没让数据“变多”,反而让数据库变得更“难用”了——你再也输不进Ssex='未知',再也插不进Grade=105,再也建不了两个同名课程。
所以数据完整性的真实含义是:用技术手段,把业务规则硬编码进数据库,让它成为不可绕过的铁律。
就像汽车的刹车片:你平时感觉不到它存在,但一旦油门踩过头,它立刻介入,防止冲出悬崖。
3.2 四类完整性约束,对应四种现实风险
实验报告覆盖了 SQL Server 支持的全部四类完整性,每一种都在堵一个具体的业务漏洞:
| 约束类型 | 实验报告中的例子 | 对应的现实风险 | 为什么必须由数据库管? |
|---|---|---|---|
| 实体完整性 (保证每条记录有唯一身份) | Student(Sno) PRIMARY KEYSC(Sno,Cno) PRIMARY KEY | 两个学生用同一个学号注册,系统分不清谁是谁 | 人工核对成本高、易遗漏;程序层校验可能被绕过(如直连数据库) |
| 域完整性 (保证字段值合法) | Ssex CHECK(...)Grade CHECK(...)Sname NOT NULL | 性别填“1/0”,成绩填“优秀”,姓名留空导致后续统计报错 | 应用层校验可被前端绕过;不同程序可能用不同规则,标准不统一 |
| 参照完整性 (保证表间关系有效) | SC.Sno FOREIGN KEY → Student.SnoSC.Cno FOREIGN KEY → Course.Cno | 删除课程后,选课记录变成“孤儿”,查成绩时显示“课程不存在” | 人工维护关系链几乎不可能;批量操作时极易遗漏关联数据 |
| 用户自定义完整性 (保证业务逻辑成立) | Course ADD FOREIGN KEY (Cpno) REFERENCES Course(Cno)(课程先修关系自引用) | 《数据库》设先修课为‘999’,但‘999’根本不存在,逻辑失效 | 这是纯业务规则,Excel/程序层无法自动感知和阻止 |
注意:实验报告中
Course表的Cpno字段允许为NULL(表示无先修课),但一旦填了值,就必须指向真实存在的Cno——这就是“有则必真,真则必存”的严谨逻辑。
3.3 约束不是摆设,是能“咬人”的活机制
很多人以为加了约束就万事大吉,其实关键在验证环节。实验报告专门设计了“检查约束作用”的实操步骤,比如:
-- 尝试插入重复学号(违反主键约束) INSERT INTO Student VALUES('20100001','李斌','男',20,'CS','1001',0); -- 结果:Violation of PRIMARY KEY constraint... Cannot insert duplicate key. -- 尝试插入非法性别(违反CHECK约束) INSERT INTO Student VALUES('20100103','王磊','未知',21,'IS','1004',0); -- 结果:The INSERT statement conflicted with the CHECK constraint... -- 尝试插入不存在的先修课号(违反外键约束) INSERT INTO Course VALUES('8','JAVA','999',3); -- 结果:The INSERT statement conflicted with the FOREIGN KEY constraint...这些报错不是开发者的“失败”,而是数据库在成功履职。它用明确的错误信息告诉你:
- 哪条规则被触犯;
- 触犯的是哪张表、哪个字段;
- 甚至提示你该去查哪个约束名(如
CK__Student__Ssex__...)。
这种即时反馈,比事后查日志、翻代码定位问题快十倍。而实验报告把“故意写错”作为正式步骤,正是在培养一种敬畏心:数据库的约束不是装饰,是带牙齿的护栏。
4. 视图、索引、嵌套查询:不是高级功能,而是帮你少写80%重复代码的“快捷指令”
4.1 视图(View):不是新表,而是“预设好的查询按钮”
看到CREATE VIEW IS_Student AS SELECT Sno,Sname,Sage FROM Student WHERE Sdept='IS',别急着背语法。先想:
- 如果你是教务老师,每天要查“信息系学生名单”,你会怎么做?
- 方法A:打开 SSMS,敲
SELECT Sno,Sname,Sage FROM Student WHERE Sdept='IS',按回车; - 方法B:点一下“IS_Student”这个按钮,结果直接出来。
- 方法A:打开 SSMS,敲
视图就是方法B——它把常用查询逻辑固化成一个虚拟表名。实验报告里建了三个视图:
Student_VIEW:屏蔽原始表的冗余字段,只暴露学号、姓名等核心信息;S_G:自动计算每个学生的平均分,不用每次GROUP BY;XK_VIEW:三表连接结果,查“学生+课程+成绩”一键到位。
关键点:视图不存数据,只存查询语句。你查
SELECT * FROM IS_Student,数据库实际执行的是SELECT Sno,Sname,Sage FROM Student WHERE Sdept='IS'。所以它轻量、安全、易维护——改底层表结构,只要查询逻辑不变,视图照常工作。
4.2 索引(Index):不是目录,而是让数据库“秒翻字典”的磁力书签
实验报告里这几行常被跳过,却是性能分水岭:
CREATE INDEX iCname ON Course(Cname); -- 普通索引 CREATE UNIQUE INDEX iSname ON Student(Sname); -- 唯一索引 CREATE CLUSTERED INDEX iSnoCno ON Sc(Sno,Cno DESC); -- 聚簇索引打个比方:
- 一本没索引的字典,查“数据库”得从第一页翻到最后一面;
- 加了索引,就像在书脊贴了按拼音排序的磁力标签,手指一划就到“shu ju ku”页码;
CLUSTERED(聚簇)索引更狠:它直接把字典页按拼音重排,查“数据库”时,相关词条物理上就挨在一起,连带查“数据结构”也飞快。
实验报告中Sc表用(Sno,Cno)建聚簇索引,是因为:
- 绝大多数查询都按“学号+课程号”组合过滤(如查某学生某门课成绩);
- 聚簇索引让这些常用组合在硬盘上连续存储,一次磁盘读取就能拿到多条记录,速度提升数倍。
提示:索引不是越多越好。实验报告只在
Cname、Sname、(Sno,Cno)上建索引,因为这些是高频查询字段;没给Sage建,因为按年龄查的场景少——建了反而拖慢插入速度。
4.3 嵌套查询:不是套娃,而是让数据库替你“多想一层”
看这个查询:
-- 找出每个学生超过他选修课程平均成绩的课程号 SELECT Sno, Cno FROM SC x WHERE Grade >= (SELECT AVG(Grade) FROM SC y WHERE y.Sno = x.Sno);表面是“查成绩高于均分的课”,实际执行分两步:
- 对
SC表每一行(记为x),先算出该学生(x.Sno)所有课程的平均分; - 再判断当前这门课(
x.Cno)的成绩是否 ≥ 这个均分。
这就是相关子查询——子查询里用了外部查询的字段(x.Sno),形成“为每一行动态计算”的能力。
实验报告还展示了其他嵌套模式:
IN子查询:WHERE Sdept IN (SELECT Sdept FROM Student WHERE Sname='刘晨')→ “和刘晨同系的人”;EXISTS子查询:WHERE EXISTS (SELECT * FROM SC WHERE Sno=Student.Sno AND Cno='1')→ “存在选1号课记录的学生”;ALL/ANY子查询:WHERE Sage < ALL (SELECT Sage FROM Student WHERE Sdept='CS')→ “比计算机系所有人年龄都小”。
这些不是炫技。当业务需求变成“找出所有重修次数超过班级平均值的学生”,你就必须用嵌套查询——因为“班级平均值”本身就是一个需要计算的中间结果,无法用单层WHERE表达。
5. 常见误区与易混概念:为什么你写的 SQL 总是“差一点就对了”?
5.1 “DELETE FROM 表名” 和 “DROP TABLE 表名” —— 清空购物车 vs 拆掉整个商场
| 易混点 | 为什么混 | 怎么区分 |
|---|---|---|
都带DELETE,都跟“删”有关 | 初学者只看动词,忽略对象层级 | DELETE FROM Student是删表里的数据行,表结构、索引、约束全在,还能继续插数据;DROP TABLE Student是删整个表,包括结构、数据、所有依赖(如视图、外键),删完SELECT * FROM Student直接报错“对象不存在”。实验报告中drop table Employee是彻底移除测试表,而delete from Student where Sno='20100022'只删特定学生,属日常维护。 |
记口诀:DELETE是“清空内容”,DROP是“端掉容器”。 |
5.2 “WHERE” 和 “HAVING” —— 入场安检 vs 场内抽查
| 易混点 | 为什么混 | 怎么区分 |
|---|---|---|
都用于筛选,都跟在SELECT后 | 两者位置接近,新手易记混执行顺序 | WHERE在分组前过滤原始数据行(如WHERE Sdept='CS'先筛出计算机系学生,再分组);HAVING在分组后过滤分组结果(如HAVING AVG(Score)>90是对每个系的均分结果再筛选)。实验报告中SELECT cno,AVG(Score) FROM sc GROUP BY Cno HAVING AVG(Score) > 90,若写成WHERE AVG(Score)>90会报错,因为WHERE不能用聚集函数。 |
记口诀:WHERE筛“人”,HAVING筛“组”。 |
5.3 “INNER JOIN” 和 “LEFT OUTER JOIN” —— 严格配对 vs 宽容兜底
| 易混点 | 为什么混 | 怎么区分 |
|---|---|---|
都用JOIN,都连表 | 教程常并列介绍,未强调业务意图差异 | INNER JOIN只返回两边都有匹配的记录(如Student JOIN SC只返回“既在学生表、又在选课表”的人);LEFT OUTER JOIN以左表为主,右表没匹配的字段填NULL(如Student LEFT JOIN SC会返回所有学生,哪怕他一门课都没选,Cno和Score显示为空)。实验报告中SELECT ... FROM Student LEFT OUTER JOIN SC就是为了查“所有学生及其选课情况”,确保没选课的学生不被漏掉。 |
记口诀:INNER是“求交集”,LEFT是“保左边”。 |
5.4 “UNION”、“INTERSECT”、“EXCEPT” —— 三兄弟的分工,不是“或/且/非”那么简单
| 易混点 | 为什么混 | 怎么区分 |
|---|---|---|
| 都是集合运算,名字像逻辑运算符 | 名称抽象,缺乏直观场景联想 | UNION是“合并去重”(A班学生 + B班学生,重复姓名只算一次);INTERSECT是“共同部分”(既是A班又是B班的学生);EXCEPT是“A有但B没有”(A班学生减去B班学生)。实验报告中SELECT * FROM Student WHERE Sdept='CS' EXCEPT SELECT * FROM Student WHERE Sage <=19,结果是“计算机系且年龄>19的学生”,不是简单“非”,而是集合差。 |
记口诀:UNION=并集,INTERSECT=交集,EXCEPT=差集(A-B)。 |
5.5 “COUNT(*)” 和 “COUNT(字段名)” —— 数人头 vs 数有名字的人
| 易混点 | 为什么混 | 怎么区分 |
|---|---|---|
| 都叫“统计数量”,结果常一样 | 当字段无NULL时,两者数值相同,掩盖区别 | COUNT(*)统计所有行数(含NULL字段的行);COUNT(字段名)只统计该字段非NULL的行数。实验报告中SELECT COUNT(*) FROM Student统计学生总数,而SELECT COUNT(DISTINCT Sno) FROM Sc统计“选过课的学生人数”(因Sc表可能有Score=NULL的记录,但Sno不为空,所以用COUNT(DISTINCT Sno)准确)。 |
记口诀:*是“数行”,字段名是“数该列有值的行”。 |
6. 什么时候该用这份实验报告?——一份能救命的“防踩坑清单”
6.1 你正在写课程设计/毕设,却卡在“数据总对不上”时
某高校软件工程专业学生做“图书借阅系统”,遇到经典困境:
- 前端显示“张三借了3本书”,后台数据库查
borrow表却只有2条记录; - 导出报表时,“借阅总数”比“归还总数”多17本,但逐条核对找不到哪本没还。
翻开这份实验报告,你会立刻定位到三个自查点:
- 查外键约束:
borrow.book_id是否REFERENCES book(id)?如果没有,前端录入的book_id可能是乱填的无效值,查不到书名,但记录仍存在; - 查
NULL处理:borrow.return_date字段是否允许NULL?如果是,未还书就该存NULL,但报表统计时若用COUNT(return_date)就漏掉了; - 查事务边界:借书操作是否包含“扣库存+增借阅记录”两个步骤?如果没用
BEGIN TRANSACTION,第一步成功、第二步失败,就会出现“库存少了但记录没增”的脏数据。
实验报告中SC表的Score允许NULL(表示未出成绩),而Student.Sname设为NOT NULL(姓名必填),就是教你怎么根据业务语义决定NULL的合理性——不是所有空都是bug,有些空恰恰是业务真相。
6.2 你被要求“优化慢查询”,却不知从哪下手时
某公司内部系统查“近三个月销售Top10”,响应时间从2秒涨到15秒。你EXPLAIN发现执行计划走了全表扫描。
对照实验报告的索引实践,你能快速推演:
- 查询条件是
WHERE sale_date BETWEEN '2024-01-01' AND '2024-03-31',但sale_date字段没索引 → 立刻建CREATE INDEX idx_sale_date ON sales(sale_date); - 查询还要
ORDER BY amount DESC LIMIT 10,如果amount也没索引,排序会极慢 → 考虑复合索引CREATE INDEX idx_date_amount ON sales(sale_date, amount); - 实验报告中
CREATE CLUSTERED INDEX iSnoCno ON Sc(Sno,Cno DESC)的思路可复用:把最常一起用的过滤字段(sale_date)和排序字段(amount)组合建索引,让数据库一次定位、有序返回。
注意:实验报告里
iSnoCno的Cno DESC是为匹配ORDER BY Sno, Cno DESC的查询,说明索引字段顺序和排序方向都要与实际查询一致——这是很多优化失败的根源。
6.3 你接手遗留系统,面对“不敢改、怕崩掉”的恐惧时
某实验室维护一个运行8年的学生成绩系统,代码混乱,但没人敢动数据库结构,因为“一改就挂”。
这份报告就是你的“拆弹指南”:
- 先看
CREATE TABLE语句,确认主键、外键、NOT NULL、CHECK是否齐全(实验报告中Student表的Ssex CHECK和SC表的Grade CHECK就是防错底线); - 再查
SELECT * FROM sys.foreign_keys(SQL Server 系统视图),确认所有外键是否启用(is_disabled=0),避免“有约束名但没生效”的假象; - 最后模拟修改:想加字段?先
ALTER TABLE ... ADD COLUMN ... NULL,观察应用是否报错;想改类型?先SELECT验证现有数据是否全符合新范围(如SMALLINT能否容纳所有Sage)。
实验报告中“修改约束”部分ALTER TABLE SC DROP CONSTRAINT SC_CHECK; ALTER TABLE SC ADD CONSTRAINT SC_CHECK CHECK(...)的操作,就是在教你怎么安全地迭代规则——先移除旧约束,验证数据无冲突,再加新约束,全程可控。
从那以后我每次改生产库结构,都强制走一遍“查约束→备数据→小范围试→监控日志”四步。不是怕麻烦,是明白:数据库的稳定性,不来自运气,而来自对每一条CREATE、ALTER、INSERT背后逻辑的敬畏。希望帮到你。
本文还有配套的精品资源,点击获取