简介本资源是南华大学大数据技术专业《数据库原理》课程配套的完整实验报告面向高校数据库初学者与实践者聚焦DBMS操作、SQL语言应用及关系型数据库设计核心能力训练。报告涵盖5大实验模块从SQL Server Management Studio图形化创建“学生选课200905”数据库及三张关联表学生/课程/选修到纯SQL语句建库建表、主外键约束与默认值设置从单表条件查询、排序分组到多表JOIN连接、间接先行课推导等高级查询再到数据增删改及完整性验证内容体系完整、步骤详实、代码可复用。资源为1个5.38MB的Word文档.doc格式结构清晰含目录、实验题目、要求、代码与结果说明便于对照学习与课程作业参考。目前已有377人学习下载是掌握数据库基础建模、SQL实战编写与关系理解的优质教学实践材料。1. 这份数据库原理实验报告不是交差材料而是你第一次真正“建库、连表、查数”的完整闭环很多同学拿到《数据库原理》课设或实验任务时第一反应是翻教材、抄SQL、凑截图——结果答辩被问一句“为什么这里用LEFT JOIN而不是INNER JOIN”当场卡壳。这份南华大学大数据技术专业使用的《数据库原理实验报告》核心价值恰恰在于它把“学生选课”这个经典教学场景拆解成了可验证、可回溯、可答辩的五层动作链从图形化建库起步到手写SQL定义约束再到单表/多表/嵌套三层查询递进最后落点到数据完整性与业务语义的对齐。它不教你怎么背语法而是逼你面对真实问题比如“查计算机系在1999–2000年出生的学生”你得同时处理WHERE中的范围条件、DATEDIFF计算年龄的陷阱、以及院系名称字段是否带空格再比如“查间接先行课”你必须理解自连接的逻辑断点在哪否则查出来全是NULL。适合正在做课程设计、毕设开题、或需要一份能过导师初审的数据库实操模板的同学——它不是答案集而是一套带设计意图、带错误预判、带验收锚点的落地脚手架。2. 数据库与表结构设计从GUI建库到SQL建模的双轨验证2.1 图形化建库用SSMS完成数据库初始化与基础约束在SQL Server Management StudioSSMS中创建名为学生选课200905的数据库本质是完成三件事分配初始文件大小、设置日志增长策略、指定排序规则。这不是点击下一步就完事的操作——若忽略排序规则如误选SQL_Latin1_General_CP1_CI_AS而非中文兼容的Chinese_PRC_CI_AS后续插入含中文的“计算机系”时可能触发隐式转换警告若初始数据文件设为1MB且禁用自动增长当插入千条学生记录时会直接报错“文件已满”。实际操作中我通常将初始大小设为10MB文件增长设为5MB避免实验中途因空间不足中断。建表环节更需警惕字段设计的“表面合理”。以学生200905表为例学号必须设为CHAR(10)而非VARCHAR(10)因学号长度固定且需参与索引查找CHAR在等值查询时性能更稳定性别定义为NCHAR(2)支持中文“男”/“女”并设默认值N男注意SQL Server中中文字符串必须加前缀N否则存入乱码出生日期用DATE类型非DATETIME因业务只需年月日精度冗余会增加索引体积备注设为NVARCHAR(200)N前缀保证中文存储200长度预留扩展空间如后期加“休学状态”“联系方式”等字段。提示SSMS建表时在“列属性”面板中手动勾选“允许空值”比代码更直观但务必核对每列——学号和课程号必须取消勾选否则主键约束失效。2.2 SQL建库建表用DDL语句实现可复现、可版本化的结构定义图形界面操作无法留存、难以协作因此实验第二部分强制要求用T-SQL重建数据库StuCou200905及三张表。这步的关键不是“写对”而是写出带业务语义的约束。例如创建课程200905表时CREATE TABLE 课程200905 ( 课程号 CHAR(6) PRIMARY KEY, 课程名 NVARCHAR(50) NOT NULL, 先行课 CHAR(6) NULL, 学分 TINYINT DEFAULT 2 CHECK (学分 BETWEEN 1 AND 8), CONSTRAINT FK_课程_先行课 FOREIGN KEY (先行课) REFERENCES 课程200905(课程号) );这段代码里藏着四个设计决策TINYINT替代INT学分最大值为8TINYINT仅占1字节千条记录可省3KB空间CHECK约束防止录入学分0或学分100这类无效值自引用外键FK_课程_先行课明确“先行课”必须是本表已存在的课程号确保课程依赖关系可追溯NULL允许先行课为空因首门课程如“高等数学”无先行课硬设NOT NULL会阻断建表。对比选修200905表的外键设计CREATE TABLE 选修200905 ( 学号 CHAR(10) NOT NULL, 课程号 CHAR(6) NOT NULL, 分数 TINYINT NULL CHECK (分数 BETWEEN 0 AND 100), PRIMARY KEY (学号, 课程号), CONSTRAINT FK_选修_学生 FOREIGN KEY (学号) REFERENCES 学生200905(学号) ON DELETE CASCADE, CONSTRAINT FK_选修_课程 FOREIGN KEY (课程号) REFERENCES 课程200905(课程号) ON DELETE NO ACTION );此处ON DELETE CASCADE与ON DELETE NO ACTION的差异直指业务逻辑学生退学时其所有选课记录应自动清除级联删除但若删除某门课程系统不应擅自删掉所有学生的该课成绩NO ACTION阻止误操作。这种设计选择远比背诵“外键是什么”更能体现数据库思维。2.3 表间关系建模用ER图固化业务规则避免“连错表”的低级翻车三张表的关系不是靠JOIN临时拼凑的而是由实体属性决定的。学生200905与课程200905之间是典型的多对多关系——一个学生可选多门课一门课可被多个学生选。但关系型数据库不支持直接表达多对多必须引入关联实体选修200905将其主键设为(学号, 课程号)复合主键。这带来两个硬性约束同一学生不能重复选同一门课主键唯一性保证选修200905中学号必须存在于学生200905课程号必须存在于课程200905外键强制参照完整性。在SSMS中建立关系时需在“外键关系”对话框中显式勾选“检查现有数据”否则历史脏数据如选修表中存在学号999999但学生表无此记录会导致关系创建失败。我一般会先执行SELECT COUNT(*) FROM 选修200905 WHERE 学号 NOT IN (SELECT 学号 FROM 学生200905)探查数据一致性清掉脏数据再建关系——这是很多同学忽略的“前置验尸”步骤。注意先行课字段的自引用外键意味着课程200905表必须在自身数据插入完毕后才能启用该约束。实践中我先建表不加外键插入全部课程数据包括先行课为空的课程再用ALTER TABLE ADD CONSTRAINT追加外键避免插入顺序导致的循环依赖报错。3. 查询能力进阶从单表筛选到多层嵌套的语义穿透3.1 单表查询WHERE子句里的业务边界意识实验2的10道单表查询题表面是练LIKE、BETWEEN、ORDER BY实则训练对业务条件边界的敏感度。例如第7题“查询计算机系在1999—2000年之间出生的学生的姓名”。错误写法SELECT 姓名 FROM 学生200905 WHERE 所在系 计算机系 AND 出生日期 BETWEEN 1999-01-01 AND 2000-12-31;问题出在BETWEEN包含端点而“1999—2000年之间”在中文语境中通常指1999年1月1日至2000年12月31日但若学生生日是2000-12-31按DATE类型存储为2000-12-31查询无误可一旦字段是DATETIME含时间BETWEEN 1999-01-01 AND 2000-12-31实际覆盖到2000-12-31 23:59:59而2000-12-31当天0点的数据会被漏掉。更鲁莽的是用YEAR(出生日期) IN (1999,2000)——若数据库跨年份迁移YEAR()函数在不同SQL方言中行为不一致。正确解法兼顾可读性与健壮性SELECT 姓名 FROM 学生200905 WHERE 所在系 N计算机系 AND 出生日期 1999-01-01 AND 出生日期 2001-01-01; -- 用替代BETWEEN避免端点歧义再看第9题“查询选修了两门以上课程的学生学号与课程数”。这里GROUP BY和HAVING的组合是关键SELECT 学号, COUNT(*) AS 课程数 FROM 选修200905 GROUP BY 学号 HAVING COUNT(*) 2;必须用HAVING而非WHERE因为WHERE在分组前过滤行无法对聚合结果COUNT(*)做条件而HAVING作用于分组后是唯一能筛出“课程数2”的子句。若误写WHERE COUNT(*) 2SQL Server会直接报错“在聚合函数中使用了无效列”。3.2 多表连接查询JOIN类型选择即业务逻辑选择多表查询的难点不在语法而在理解JOIN类型背后的现实映射。以第1题为例“查询选修了【数据库原理】的计算机系的学生学号和姓名”。标准解法SELECT s.学号, s.姓名 FROM 学生200905 s INNER JOIN 选修200905 sc ON s.学号 sc.学号 INNER JOIN 课程200905 c ON sc.课程号 c.课程号 WHERE s.所在系 N计算机系 AND c.课程名 N数据库原理;这里三个INNER JOIN意味着只返回同时满足“是计算机系学生”、“有选课记录”、“该课名为数据库原理”的三重交集。若用LEFT JOIN替换第二个连接SELECT s.学号, s.姓名 FROM 学生200905 s LEFT JOIN 选修200905 sc ON s.学号 sc.学号 -- 此处LEFT INNER JOIN 课程200905 c ON sc.课程号 c.课程号 WHERE s.所在系 N计算机系 AND c.课程名 N数据库原理;结果会变成空——因为LEFT JOIN保留了所有学生但sc.课程号对未选课学生为NULLc.课程名 N数据库原理条件无法匹配NULL最终无结果。这就是典型“JOIN类型误用导致语义崩塌”。再看第5题“查询所有学生的信息和他们的所选修课程的课程编号”。题目要求“所有学生”意味着即使没选课也要显示此时必须用LEFT JOINSELECT s.*, sc.课程号 FROM 学生200905 s LEFT JOIN 选修200905 sc ON s.学号 sc.学号;若用INNER JOIN没选课的学生将彻底消失违背“所有学生”的需求。JOIN不是技术选择而是对“业务全集”的承诺。3.3 高级查询嵌套与组合查询中的逻辑分层实验3的嵌套查询本质是训练将自然语言需求逐层翻译为SQL逻辑树的能力。以第3题为例“查询其他系中比计算机系学生年龄都小的学生信息”。拆解逻辑第一步找出计算机系学生的最小年龄即最年轻者第二步找出其他系所在系 ! 计算机系中年龄小于该最小值的学生。T-SQL实现SELECT * FROM 学生200905 WHERE 所在系 ! N计算机系 AND DATEDIFF(YEAR, 出生日期, GETDATE()) ( SELECT MIN(DATEDIFF(YEAR, 出生日期, GETDATE())) FROM 学生200905 WHERE 所在系 N计算机系 );注意两点DATEDIFF(YEAR, 出生日期, GETDATE())计算年龄是近似值忽略月份但教学场景可接受生产环境应改用CASE WHEN MONTH(GETDATE()) MONTH(出生日期) OR (MONTH(GETDATE()) MONTH(出生日期) AND DAY(GETDATE()) DAY(出生日期)) THEN YEAR(GETDATE()) - YEAR(出生日期) - 1 ELSE YEAR(GETDATE()) - YEAR(出生日期) END子查询必须用MIN()而非TOP 1因TOP 1无序可能返回任意一条记录无法保证“最小年龄”。再看组合查询第5题“查询0602001和0602002学生同时选修的课程号”要求用INTERSECT与EXISTS两种方法。INTERSECT写法简洁SELECT 课程号 FROM 选修200905 WHERE 学号 0602001 INTERSECT SELECT 课程号 FROM 选修200905 WHERE 学号 0602002;而EXISTS需构造相关子查询SELECT DISTINCT sc1.课程号 FROM 选修200905 sc1 WHERE sc1.学号 0602001 AND EXISTS ( SELECT 1 FROM 选修200905 sc2 WHERE sc2.学号 0602002 AND sc2.课程号 sc1.课程号 );EXISTS的优势在于可读性更强——它直白表达了“对0602001的每一条选课记录检查是否存在0602002的同课程记录”而INTERSECT更像集合运算对初学者隐藏了执行逻辑。4. 实验常见问题排查那些让导师皱眉的5个高频翻车点4.1 现象建表时提示“无法创建外键引用的表不存在”原因SQL脚本中CREATE TABLE语句的顺序错误。例如先创建选修200905表并声明外键引用学生200905但学生200905表尚未创建。SQL Server按脚本顺序执行遇到未定义的表名即报错。解决严格按依赖顺序编写建表脚本——先建被引用表学生200905、课程200905再建引用表选修200905。若需循环依赖如课程表自引用先建表不加外键数据插入后再用ALTER TABLE ADD CONSTRAINT追加。4.2 现象查询“姓李的学生”返回空但数据确认存在原因WHERE 姓名 LIKE 李%未加N前缀且数据库排序规则为SQL_Latin1_General_CP1_CI_AS。中文字符在此规则下可能被当作乱码处理导致匹配失败。解决统一使用Unicode字符串前缀N即WHERE 姓名 LIKE N李%或在建库时指定COLLATE Chinese_PRC_CI_AS。教学环境中N前缀是最简单可靠的方案。4.3 现象COUNT(*)统计选课人数为0但SELECT * FROM 选修200905能看到数据原因COUNT(*)统计的是行数但若查询中JOIN了空表如课程200905中无“数据库原理”课程INNER JOIN会过滤掉所有行导致COUNT(*)为0。解决先单独验证各表数据如SELECT COUNT(*) FROM 课程200905 WHERE 课程名 N数据库原理若为0说明课程未录入需补数据而非调查询。4.4 现象ORDER BY排序结果与预期不符如“李明”排在“李华”之后原因ORDER BY 姓名默认按数据库排序规则比较Chinese_PRC_CI_AS下按拼音排序“李华”LiHua在“李明”LiMing前但若规则为SQL_Latin1_General_CP1_CI_AS则按Unicode码位排序结果不可控。解决显式指定排序规则ORDER BY 姓名 COLLATE Chinese_PRC_CI_AS确保按中文习惯排序。4.5 现象插入学生数据时报错“违反主键约束”但SELECT查不到该学号原因学号字段定义为CHAR(10)插入时用了06020017位SQL Server会自动右补空格至10位即0602001 而查询时若写WHERE 学号 0602001字符串比较会忽略尾部空格看似匹配实则存储值含空格。解决插入时用RTRIM(0602001)或直接定义学号 VARCHAR(10)更推荐在建表时用CHAR(10)但插入时严格补足空格保持类型一致性。5. 方案验收与答辩准备用三张表、五个查询、一套逻辑征服导师5.1 验收清单你的实验报告必须能现场演示这5个关键动作导师不会逐行看你写的SQL但一定会让你现场执行以下操作并解释每步背后的逻辑。提前演练这五项答辩成功率提升80%验收项操作指令导师想听的解释要点常见失分点1. 数据库初始化验证SELECT name, create_date FROM sys.databases WHERE name IN (学生选课200905, StuCou200905)说明两个库的存在意义前者用于GUI操作练习后者用于SQL脚本复现体现双轨验证思想只说“建好了”说不出为何要建两个库2. 主外键约束生效验证INSERT INTO 选修200905 VALUES (999999, C01, 85)学号999999不存在解释外键如何阻止脏数据FK_选修_学生约束触发报错“INSERT 语句与 FOREIGN KEY 约束冲突”不知错误代码含义只会说“报错了”3. 单表查询边界验证SELECT TOP 10 * FROM 学生200905 WHERE 所在系 N计算机系 ORDER BY 出生日期 DESC说明ORDER BY ... DESC对应“找最年轻学生”TOP 10是安全限制避免全表扫描把DESC说成“降序排列”却不提业务目的找新生4. 多表连接语义验证SELECT s.姓名, c.课程名 FROM 学生200905 s LEFT JOIN 选修200905 sc ON s.学号sc.学号 LEFT JOIN 课程200905 c ON sc.课程号c.课程号 WHERE s.学号0602001对比LEFT JOIN与INNER JOIN结果前者显示该生所有信息含NULL课程名后者只显示已选课程——证明LEFT保障了“学生全集”混淆LEFT与RIGHT说不出为何选LEFT5. 嵌套查询逻辑验证SELECT 课程名 FROM 课程200905 WHERE 课程号 IN (SELECT 先行课 FROM 课程200905 WHERE 课程名 N数据库原理)解释这是“查数据库原理的先行课”子查询先定位课程号外层再查课程名体现分层思维把IN子查询说成“先查内层再查外层”忽略SQL Server实际可能采用哈希连接优化提示答辩时打开SSMS让导师看到实时执行结果比截图更有说服力。提前把这5条SQL存为查询窗口命名清晰如“验收1-双库验证.sql”避免现场手打出错。5.2 答辩话术用“业务问题→SQL解法→设计权衡”三段论回应质疑导师最爱问“为什么这里用EXISTS不用IN”、“CHAR和VARCHAR到底差在哪”。别背概念用三段论回答业务问题先还原场景。“比如查‘同时选两门课的学生’本质是要确认‘对每个学生是否存在另一门课记录’这是典型的‘存在性判断’。”SQL解法给出具体写法。“EXISTS子查询中SELECT 1不取实际数据只判断存在性比IN子查询SELECT 课程号更轻量且EXISTS在找到第一条匹配就停止IN需生成完整结果集。”设计权衡点明取舍。“教学场景用IN更易懂但生产环境EXISTS性能更优——就像我们实验用CHAR(10)保证学号对齐虽占空间但查询快若学生号长度不一才换VARCHAR。”再比如被问“BETWEEN和 AND 哪个好”不要答“都行”。要说“BETWEEN写起来短但 AND 能精确控制端点比如查‘2000年出生’ 2000-01-01 AND 2001-01-01比BETWEEN 2000-01-01 AND 2000-12-31更安全因为后者漏掉12月31日全天的DATETIME数据——我们实验用DATE类型所以影响小但养成习惯很重要。”5.3 终极避坑从那以后我每次建库都强制走一遍“三查一测”查排序规则SELECT DATABASEPROPERTYEX(StuCou200905, Collation)必须是Chinese_PRC_CI_AS否则中文存取全乱查约束状态SELECT name, is_disabled FROM sys.foreign_keys WHERE parent_object_id OBJECT_ID(选修200905)确保外键is_disabled0查数据一致性SELECT COUNT(*) FROM 选修200905 sc LEFT JOIN 学生200905 s ON sc.学号s.学号 WHERE s.学号 IS NULL结果必须为0否则有孤儿记录测核心查询用实验2第1题SELECT * FROM 学生200905 WHERE 所在系 N计算机系确认能返回至少1条记录——这是所有后续查询的基石。这套流程我带过三届学生凡跳过“三查一测”的80%在多表查询时卡在NULL值处理上。希望帮到你。本文还有配套的精品资源点击获取