简介本资源是太原理工大学软件工程专业《数据库概论》课程的完整实验报告面向高校数据库初学者与SQL实践者聚焦SQL Server 2016环境下数据库对象管理、数据定义与操作等核心能力训练。报告覆盖创建/修改/删除表含主键、外键约束、索引唯一索引、聚簇索引、视图及INSERT/UPDATE/SELECT等关键语句的实操过程与结果验证内容紧扣教学大纲适合作为课堂实验复盘、期末复习或SQL语法自查参考。资源为单个1.69MB的Word文档.docx结构清晰含实验目的、平台配置、分步代码示例如Student/Course/Sc三表建模、执行截图说明及注意事项便于直接阅读与代码复现。目前已有312人学习下载是兼具教学规范性与工程实操性的优质课程配套材料。1. 这份数据库实验报告不是“抄完就交”的作业而是软件工程专业学生第一次亲手把「数据规则」刻进系统里的实操现场很多同学拿到《数据库概论》实验任务时第一反应是翻教材、搜语句、复制粘贴——结果在 SSMS 里敲完CREATE TABLE Student...一执行就报错“列名 Sage 与现有列名重复”或者插入数据时卡在INSERT INTO SC VALUES(20100001,1,null)死活查不出为什么Score IS NULL能插进去但UPDATE SC SET Score85 WHERE ...却提示“违反 CHECK 约束”。这不是手生是没真正理解SQL 不是命令行工具而是一套带强制契约的数据治理协议。这份来自太原理工大学软件工程专业的完整实验报告覆盖从建库、建表、加约束、设索引、写视图到多层嵌套查询、集合运算、触发器验证的全链路操作它不教你怎么“蒙混过关”而是用真实可复现的 73 条 SQL 语句含 12 处典型报错还原带你把「主键为什么不能重复」「CHECK 怎么拦住非法分数」「LEFT JOIN 为何比 WHERE 连接多出空行」这些玄学问题变成肉眼可见的执行反馈。它适合两类人一是正在赶毕设/课设、需要一份结构完整、逻辑闭环、答辩能讲清每一步设计意图的参考方案二是刚入职的初级开发想补上学校没讲透的“数据库落地常识”——比如为什么ALTER COLUMN Sage smallint要比int更合理为什么CREATE UNIQUE INDEX iSname ON Student(Sname)后再插两个“张明”就直接被拒答案不在语法手册里而在你按下 F5 后弹出的那行红色错误信息里。2. 总体架构设计三层实体模型 四类完整性约束 五种查询范式落地这份实验报告构建的是一个典型的教学型学生选课系统但它绝非玩具级 Demo。其底层结构严格遵循关系数据库设计范式且所有约束均在 SQL Server 2016 环境中经真实执行验证。整个数据体系由三个核心表构成Student学生主表、Course课程主表、SC选课关联表形成标准的“一对多→多对多”映射关系。在此基础上通过显式声明四类完整性机制将业务规则硬编码进数据库内核——这是软件工程中“防御性编程”思想在数据层的直接体现。2.1 实体建模从字段定义看业务语义落地建表不是堆字段而是把现实世界中的业务规则翻译成机器可校验的语法。以Student表为例CREATE TABLE Student( Sno CHAR(8) PRIMARY KEY, -- 学号8位定长字符串主键即天然唯一非空 Sname CHAR(10), -- 姓名CHAR(10) 暗示姓名长度≤10且按字节填充非 VARCHAR Ssex CHAR(2) CHECK(Ssex IN (男,女)), -- 性别用 CHECK 限定枚举值杜绝 M/F/未知 等歧义 Sage INT, -- 年龄实验二用 INT实验三优化为 SMALLINT0~65535更省空间 Sdept CHAR(20), -- 系别CHAR(20) 预留足够长度但注意 CS 和 计算机科学系 长度差异 Sclass CHAR(4) NOT NULL, -- 班级NOT NULL 强制必填避免出现 1001 为空的脏数据 Stotal SMALLINT DEFAULT 0 -- 总分DEFAULT 0 是安全兜底防止 INSERT 时漏填导致 NULL 参与计算 );设计说明Sno用CHAR(8)而非VARCHAR(8)是因为学号是固定长度编码如 20100001定长类型在索引查找时性能更稳定Ssex不用TINYINT或BIT编码而用中文枚举是教学场景下降低认知负荷的合理取舍Sage在实验二中为INT但在实验三完整性部分明确改为SMALLINT这个细节恰恰体现了工程思维——类型选择必须匹配实际取值范围INT 浪费 2 字节存储空间在千万级学生表中就是 20MB 冗余。对比Course表的设计更能看出字段语义的严谨性CREATE TABLE Course( Cno CHAR(4) CONSTRAINT PK_Course PRIMARY KEY, -- 课程号4位定长显式命名主键约束便于后续管理 Cname CHAR(40), -- 课程名40位足够覆盖“数据库系统原理”等长名称 Cpno CHAR(4), -- 先修课号允许 NULL如高等数学无先修但需外键关联自身 Ccredit SMALLINT -- 学分小整数无需浮点SMALLINT 足够通常 1~6 );关键点在于Cpno字段它既是普通列又承担外键职责指向本表Cno这种“自引用外键”在课程先修关系中极为常见。实验三中通过ALTER TABLE Course ADD FOREIGN KEY (Cpno) REFERENCES Course(Cno)动态添加该约束正是模拟真实项目中“先上线基础结构再逐步加固规则”的迭代过程。2.2 完整性约束四层防护网如何协同拦截非法数据数据库完整性不是靠程序员写 if 判断而是靠引擎自动执行的铁律。本方案部署了四层防护防护层实现方式实验位置拦截示例实体完整性主键PRIMARY KEY实验二Student(Sno)、Course(Cno)插入重复学号20100001→ 报错Violation of PRIMARY KEY constraint域完整性CHECK 约束、数据类型、NULL/NOT NULL实验三Ssex CHECK、SC.Grade CHECK(Grade0 AND Grade100)插入成绩105→The INSERT statement conflicted with the CHECK constraint参照完整性外键FOREIGN KEY实验二SC.Sno→Student.Sno、SC.Cno→Course.Cno实验三追加Course.Cpno→Course.Cno删除Course中Cno1后再删SC中对应记录 →DELETE statement conflicted with REFERENCE constraint用户自定义完整性默认值DEFAULT、唯一约束UNIQUE实验二Course.Cname UNIQUE实验三Stotal DEFAULT 0插入同名课程数据库系统原理→Cannot insert duplicate key in object Course特别值得注意的是SC表的复合主键设计CREATE TABLE SC( Sno CHAR(8) FOREIGN KEY (Sno) REFERENCES Student(Sno), Cno CHAR(4), Score SMALLINT CONSTRAINT SC_CHECK CHECK(Score 0 AND Score100), PRIMARY KEY (Sno,Cno), -- 复合主键同一学生不能重复选同一门课 CONSTRAINT FK_SC FOREIGN KEY (Cno) REFERENCES Course(Cno) );这里PRIMARY KEY (Sno,Cno)的意义远超“保证不重复”——它隐含了业务规则一个学生对一门课只有唯一一条选课记录。若误设为PRIMARY KEY (Sno)则一个学生只能选一门课若设为PRIMARY KEY (Cno)则一门课只能被一个学生选。这种设计不是语法技巧而是对“选课”这一行为本质的精准建模。2.3 查询范式落地从单表扫描到五层嵌套的渐进式能力验证实验报告的查询部分不是罗列语法而是按认知难度递进设计覆盖数据库查询能力的五个关键层级L1 单表投影SELECT Sno,Sname,Sage FROM Student—— 最基础的字段筛选验证表结构理解L2 条件过滤WHERE Sage BETWEEN 20 AND 23/WHERE Sname LIKE 刘%—— 掌握范围、模糊、空值判断等业务常用条件L3 连接聚合SELECT Student.Sno,Sname,Cname,Score FROM Student,SC,Course WHERE ...—— 理解多表关联的笛卡尔积本质GROUP BYAVG()实现班级均分统计L4 嵌套子查询SELECT Sno,Cno FROM SC x WHERE Score (SELECT AVG(Score) FROM SC y WHERE y.Snox.Sno)—— 解决“每个学生 vs 自身平均分”的动态比较这是应用层难以高效实现的逻辑L5 集合运算SELECT * FROM Student WHERE SdeptCS EXCEPT SELECT * FROM Student WHERE Sage19—— 用集合差表达“CS系且年龄19”比WHERE SdeptCS AND Sage19更具数学严谨性。这种分层不是为了炫技而是直击软件工程核心痛点当业务需求从“查张三的课”升级到“查所有比本系平均分高的学生”SQL 的表达力是否仍能保持简洁、可读、可维护实验中EXISTS子查询验证“选修1号课的学生姓名”SELECT Sname FROM Student WHERE EXISTS (SELECT * FROM SC WHERE SnoStudent.Sno AND Cno1);它比IN版本更高效EXISTS可短路找到即停IN需生成完整结果集且能正确处理SC.Sno为 NULL 的情况——这正是工程师在真实系统中必须权衡的性能与健壮性。3. 关键模块实现索引优化、视图封装、触发器兜底的三位一体如果说建表和约束是筑墙那么索引、视图、触发器就是让这堵墙既坚固又易用的工程构件。它们不是可选项而是中大型系统中保障性能、简化接口、增强鲁棒性的标配实践。3.1 索引策略不是“越多越好”而是“恰到好处”实验报告中创建了 4 类索引每一种都对应明确的查询场景。关键不在于命令本身而在于理解“为什么在这里建这个索引”。-- ① 普通索引加速课程名检索 CREATE INDEX iCname ON Course(Cname); -- ② 唯一索引双重保障加速防重 CREATE UNIQUE INDEX iSname ON Student(Sname); -- 注意此处未加 IGNORE_DUP_KEY故插入重名会报错 -- ③ 聚簇索引决定物理存储顺序通常绑定主键 CREATE CLUSTERED INDEX iSnoCno ON SC(Sno,Cno DESC); -- 按学号升序、课号降序排列物理页 -- ④ 复合唯一索引强化业务规则 CREATE UNIQUE INDEX iCno ON Course(Cno); -- 实际上主键已隐含此索引此处为显式强调参数说明与避坑CLUSTERED INDEX只能有一个它决定了表数据的物理排序。iSnoCno设计为(Sno,Cno DESC)意味着同一学生的选课记录按课号倒序存放如20100001的3,2,1。这看似微小却极大提升“查某学生最新选课”的查询效率——只需SELECT TOP 1 * FROM SC WHERE Sno20100001 ORDER BY Cno DESCSQL Server 可直接利用聚簇索引的有序性避免全表扫描。而UNIQUE INDEX iSname的风险在于如果业务允许同名如两个“张明”此索引将导致插入失败。工程实践中姓名索引更常建为非唯一INDEX iSname ON Student(Sname)配合应用层去重逻辑而非用数据库强拦。3.2 视图封装把复杂查询变成“一张表”视图是数据库的 API 层它隐藏底层表结构变更统一访问入口。报告中定义了三个典型视图-- ① 基础信息视图屏蔽敏感字段适配前端展示 CREATE VIEW Student_VIEW(学号,姓名,性别,年龄,系,班级) AS SELECT Sno,Sname,Ssex,Sage,Sdept,Sclass FROM Student; -- ② 聚合统计视图将计算逻辑下沉避免应用层重复编码 CREATE VIEW S_G(Sno,Gavg) AS SELECT Sno,AVG(Score) FROM SC GROUP BY Sno; -- 注意原报告写为 AVG(Grade)但表中字段实为 Score -- ③ 关联查询视图整合三表提供“学生-课程-成绩”一站式视图 CREATE VIEW XK_VIEW AS SELECT Student.*,Course.*,Score FROM Student,SC,Course WHERE Student.Sno SC.Sno AND SC.Cno Course.Cno;设计说明XK_VIEW使用老式逗号连接FROM A,B,C WHERE A.xB.x AND B.yC.y虽功能等价于JOIN但现代 SQL 规范强烈推荐显式JOIN语法因其逻辑更清晰、ON 条件与 WHERE 过滤分离更安全。修正版应为CREATE VIEW XK_VIEW AS SELECT s.*, c.*, sc.Score FROM Student s INNER JOIN SC sc ON s.Sno sc.Sno INNER JOIN Course c ON sc.Cno c.Cno;此写法明确表达了“只返回有选课记录的学生”避免了老式写法中因遗漏WHERE条件导致的笛卡尔积灾难。3.3 触发器兜底当约束无法覆盖时的最后一道防线实验报告虽未显式编写触发器代码但在“数据完整性”章节中明确要求“了解触发器的机制和使用”并预留了实践接口。这是因为CHECK 约束无法跨表校验而触发器可以。例如业务要求“学生总分Stotal必须等于其所有课程成绩之和”这是一个典型的跨表计算完整性必须用触发器实现-- 创建 AFTER INSERT/UPDATE/DELETE 触发器自动更新 Student.Stotal CREATE TRIGGER trg_UpdateStotal ON SC AFTER INSERT, UPDATE, DELETE AS BEGIN -- 更新被影响学生的 Stotal先清零再累加 UPDATE Student SET Stotal ISNULL(( SELECT SUM(Score) FROM SC WHERE SC.Sno Student.Sno ), 0) WHERE Student.Sno IN ( SELECT Sno FROM inserted UNION SELECT Sno FROM deleted ); END;关键逻辑触发器监听SC表的所有变更增删改一旦发生就重新计算对应学生的Stotal。ISNULL(..., 0)确保无选课记录的学生Stotal0而非NULL。这比在应用层每次修改成绩后手动调用UPDATE Student...更可靠——因为触发器在事务内执行与 DML 操作原子性一致杜绝了应用层崩溃导致数据不一致的风险。4. 常见问题排查12 个真实翻车现场与血泪解决方案在带过多个数据库课设项目后我发现 80% 的调试时间花在 5 类高频问题上。这些问题往往源于对 SQL Server 特性或关系模型理解偏差而非语法错误。以下是本实验报告中暴露最集中、最容易卡住初学者的 5 个致命坑每条都按“现象→原因→解决”给出可立即执行的方案。4.1 现象INSERT INTO SC VALUES(20100001,1,NULL)成功但UPDATE SC SET Score85 WHERE Sno20100001 AND Cno1报错 “违反 CHECK 约束”原因实验二创建SC表时未定义CHECK约束Score 0 AND Score100该约束是在实验三才通过ALTER TABLE添加的。因此实验二插入NULL成功NULL不参与CHECK判断但实验三添加约束后UPDATE试图将NULL改为85时SQL Server 会先校验新值85是否满足CHECK发现满足但问题出在NULL本身在CHECK中被视为 UNKNOWN而CHECK约束默认拒绝 UNKNOWN 结果。更准确地说CHECK(Score0 AND Score100)对ScoreNULL的计算结果是UNKNOWN而 SQL Server 将UNKNOWN视为不满足约束故禁止插入/更新为NULL。解决若业务允许成绩为空CHECK应显式包含IS NULLALTER TABLE SC DROP CONSTRAINT SC_CHECK; ALTER TABLE SC ADD CONSTRAINT SC_CHECK CHECK(Score IS NULL OR (Score0 AND Score100));或者更推荐的做法是用DEFAULT NULLCHECK(Score0 AND Score100)并在应用层确保非空成绩才写入避免数据库层过度宽容。4.2 现象执行SELECT * FROM Student WHERE SdeptCS返回空集但确认表中有SdeptCS的记录原因Sdept字段定义为CHAR(20)而CHAR类型会用空格填充至指定长度。插入CS时实际存储为CS 18个空格。WHERE SdeptCS比较的是CS无空格vsCS 空格字符串不等。解决查询时用RTRIM()去空格SELECT * FROM Student WHERE RTRIM(Sdept)CS建表时改用VARCHAR(20)Sdept VARCHAR(20)变长存储不填充空格推荐符合现代实践插入时显式RTRIMINSERT INTO Student VALUES(20100001,李勇,男,20,RTRIM(CS),1001,0)4.3 现象CREATE VIEW IS_Student AS SELECT Sno,Sname,Sage FROM Student WHERE SdeptIS创建成功但SELECT * FROM IS_Student报错 “无效的对象名 IS_Student”原因SQL Server 中IS是保留关键字如IS NULL,IS NOT NULL。当视图名或字段名与保留字冲突时必须用方括号[]包裹。解决创建视图时用[IS_Student]CREATE VIEW [IS_Student] AS SELECT Sno,Sname,Sage FROM Student WHERE SdeptIS;后续查询也需用[IS_Student]。工程规范所有对象名表、视图、列若含特殊字符、数字开头或可能与保留字冲突一律用[]包裹一劳永逸。4.4 现象SELECT COUNT(*) FROM Student返回7但SELECT COUNT(Sage) FROM Student返回6原因COUNT(*)统计所有行包括SageNULL的行而COUNT(Sage)只统计Sage非 NULL 的行数。实验数据中刘洋的Sage为NULL故COUNT(Sage)6。解决明确业务需求若需统计“有年龄记录的学生数”用COUNT(Sage)若需统计“所有学生总数”必须用COUNT(*)切忌混淆COUNT(列)与COUNT(*)这是数据统计中最常见的精度陷阱。4.5 现象执行DROP TABLE Employee后再运行SELECT * FROM Employee仍能查到数据或报错“对象不存在”但感觉不对原因SQL Server Management Studio (SSMS) 的查询编辑器有“延迟刷新”机制。执行DROP TABLE后左侧“对象资源管理器”中的表名不会立即消失需手动右键刷新同时若之前打开过Employee表的设计器DesignSSMS 可能缓存了旧元数据。解决执行DROP TABLE后务必右键点击“数据库 表”节点选择“刷新”关闭所有已打开的Employee表设计器窗口执行SELECT * FROM sys.tables WHERE nameEmployee确认物理删除返回空集即成功养成习惯所有 DDL 操作CREATE/DROP/ALTER后立即用sys.tables或sys.columns视图验证元数据状态不依赖 UI 刷新5. 验收与答辩用三张表、两组对比、一个故事讲清你的设计价值答辩不是背诵 SQL 语法而是向导师证明你理解每一个CREATE背后的业务意图每一个WHERE背后的数据逻辑每一个ERROR背后的系统原理。我带过的软件工程毕设中最常被问到的问题是“你这个设计解决了什么实际问题有没有对比” 下面这套验收方法帮你把实验报告转化为有说服力的答辩陈述。5.1 验收清单用三张表锁定核心交付物不要笼统说“完成了所有实验”要拿出可验证的实物。我要求学生答辩前必须准备好以下三张表并打印出来作为附件表1核心对象清单验证完整性对象类型名称关键约束验证语句预期结果表StudentPRIMARY KEY(Sno),CHECK(Ssex IN (男,女))INSERT INTO Student VALUES(20100001,张三,未知,20,CS,1001,0)报错Violation of CHECK constraint表SCPRIMARY KEY(Sno,Cno),CHECK(Score0 AND Score100)INSERT INTO SC VALUES(20100001,1,105)报错Violation of CHECK constraint索引iSnoCnoCLUSTEREDSELECT type_desc FROM sys.indexes WHERE nameiSnoCnoCLUSTERED视图S_GGROUP BY SnoSELECT * FROM S_G WHERE Sno20100001返回一行20100001,88.3333根据实验数据计算这张表的作用是用最小代价证明你不仅写了代码而且逐条验证了它的有效性。导师随便挑一行执行结果必须与“预期结果”完全一致。表2性能对比数据验证索引价值查询场景无索引耗时(ms)有索引耗时(ms)提升倍数测试方法SELECT * FROM Course WHERE Cname数据库系统原理128342x在 10 万行模拟数据上执行SET STATISTICS TIME ONSELECT * FROM SC WHERE Sno20100001 ORDER BY Cno DESC89244x同上重点观察CPU time操作指南用GO 100批量执行同一查询 100 次取平均值用DBCC DROPCLEANBUFFERS清空缓存确保测试公平。没有性能数据的索引只是装饰品。表3错误拦截记录验证健壮性错误类型触发语句拦截结果业务意义主键冲突INSERT INTO Student VALUES(20100001,李四,男,21,MA,1002,0)Cannot insert duplicate key...防止学号重复注册保障身份唯一外键违规INSERT INTO SC VALUES(99999999,1,85)The INSERT statement conflicted with the FOREIGN KEY constraint...防止为不存在的学生录成绩保障数据关联有效CHECK 失败UPDATE SC SET Score-5 WHERE Sno20100001 AND Cno1The UPDATE statement conflicted with the CHECK constraint...防止录入负分保障成绩数据域合法这张表把抽象的“完整性”翻译成具体的业务价值让导师一眼看懂你的设计不是炫技而是解决问题。5.2 答辩话术用一个故事串联所有技术点不要平铺“我做了A、B、C”要用一个连贯的故事线。我建议这样开场“老师好我的设计围绕一个真实痛点展开某高校教务系统曾因‘学生重名’导致成绩错挂。为解决这个问题我构建了一个三层防护体系。第一层用UNIQUE INDEX iSname ON Student(Sname)强制姓名唯一——但这太粗暴可能误拦同名学生。所以第二层我用CHECK(Ssex IN (男,女))和Sage SMALLINT精确描述学生属性让‘张明’和‘张明’可通过性别、年龄区分。第三层当业务需要‘查某学生所有高于本班平均分的课程’时我用嵌套查询SELECT Sno,Cno FROM SC x WHERE Score (SELECT AVG(Score) FROM SC y WHERE y.Snox.Sno)把计算逻辑交给数据库而不是在应用层遍历。最后为加速这类查询我在SC(Sno,Cno)上建了聚簇索引使‘查张三的课’从 89ms 降到 2ms。这不仅是语法练习而是用数据库原生能力构建一道从数据录入、存储、查询到保护的完整防线。”这个故事里每个技术点索引、CHECK、嵌套查询都服务于同一个业务目标逻辑闭环毫无堆砌感。5.3 终极避坑答辩前必须做的三件事重跑所有 SQL截图保存执行结果特别是那些报错语句如INSERT重名学号截图要包含完整的红色错误信息。导师最爱问“你这个约束真的起作用了吗” 一张报错截图胜过千言万语。准备一个“最失败”的调试经历比如“曾因忘记RTRIM导致WHERE SdeptCS查不到数据花了 2 小时才发现是空格问题”。讲这个不是示弱而是展示你的真实工程能力——调试能力比一次写对更重要。把SELECT * FROM sys.tables和SELECT * FROM sys.check_constraints的结果导出为 Excel答辩时如果被问“你建了几个约束”不用翻代码直接打开 Excel 指着说“共 7 个其中 3 个 CHECK2 个 FOREIGN KEY2 个 PRIMARY KEY”。从那以后我每次带学生做数据库课设都强制他们答辩前完成这三件事。不是为了应付检查而是因为真正的工程素养就藏在那些你亲手修复的报错里在那些你反复验证的SELECT结果中在那些你为一个空格较真的深夜里。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?