我要提问
ARTICLE DETAIL

资讯详情

前沿编程新知与开发实战干货的深度解读。

数据库原理实验报告:SQL Server建库建表与查询实战指南

数据库原理实验报告:SQL Server建库建表与查询实战指南 简介这份南华大学《数据库原理》实验报告面向大数据技术等专业学生以及正在学习数据库课程、需要完成实验作业或复习备考的读者帮助梳理从DBMS认识到SQL查询的完整实验脉络。资源包内含1个doc文档大小约5.38MB以实验报告正文形式呈现结构清晰、便于直接参考。报告围绕SQL Server Management Studio展开涵盖利用管理工具与SQL语句创建“学生选课200905”“StuCou200905”数据库建立学生、课程、选修三张表并定义主码、外码、默认值与空值约束进而理解一对多、多对多等表间关系。实验2、实验3则聚焦单表查询与多表连接查询涉及条件筛选、排序、分组、JOIN及间接先行课等典型题目并延伸至数据更新与实验总结。目前已有375人学习下载适合需要对照实验步骤、理解SQL语句写法与数据完整性约束的学习者参考。1. 从一份实验报告说起数据库原理到底怎么落地很多人拿到《数据库原理》这门课第一反应是背 SQL 语法、记范式定义考完就忘。但真正到了工作里让你从零建一个库、设计三张表、把主外键关系理清楚再写几条能跑出正确结果的查询不少人还是会卡壳。这份实验报告的价值就在这儿——它不是纯理论而是把 SQL Server Management Studio 这个工具和 SQL 语句两条线并排走了一遍从建库建表到单表查询、多表连接、嵌套与组合查询每一步都有对应的题目和代码。适合正在上数据库课、需要交实验报告的学生也适合想系统过一遍 SQL 基础、把「学生选课」这个经典模型吃透的开发者。下面我按实验顺序拆一遍重点讲清楚每步在干什么、参数怎么定、哪里容易翻车。2. 实验一建库建表与表间关系先把地基打对2.1 用管理工具建库建表图形界面不是点着玩实验一要求先用 SQL Server Management Studio 的图形界面创建数据库【学生选课200905】再建三张表学生200905、课程200905、选修200905。很多人觉得图形界面就是右键新建、填名字、点确定但真正容易出问题的是字段类型和约束。学生表里学号用 char(10) 还是 varchar(10)常见做法是学号固定长度用 char(10)姓名用 nvarchar(20) 支持中文性别用 nchar(2) 并设默认值 男出生日期用 date院系名称用 nvarchar(50)备注用 nvarchar(200) 允许为空。课程表里课程号用 char(6)课程名 nvarchar(50)先行课 char(6) 允许为空因为有些课没有先行课学分用 decimal(3,1) 并设默认值 2.0。选修表里学号和课程号都是 char 类型分数用 decimal(5,1) 允许为空因为可能还没出成绩。主键的定义直接决定后续查询能不能正确关联。学生表主键是学号课程表主键是课程号选修表的主键是学号课程号组合主键。外键方面选修表的学号引用学生表的学号选修表的课程号引用课程表的课程号。这里有个血泪经验如果先建选修表再建学生表和课程表外键会创建失败因为被引用的表还不存在。正确顺序是先建学生表和课程表最后建选修表。提示图形界面建表时默认值是在列属性窗口的「默认值或绑定」里填不是直接在类型里写。填 男 要带单引号填 2.0 不用带。2.2 用 SQL 语句重建一遍StuCou200905 的完整脚本图形界面点完之后实验要求用 SQL 语句再建一个库叫 StuCou200905表名变成 Stu200905、Cou200905、SC200905。这一步才是真正要掌握的东西因为工作中很少让你点鼠标建表都是写脚本。-- 创建数据库 CREATE DATABASE StuCou200905; GO USE StuCou200905; GO -- 学生表 CREATE TABLE Stu200905 ( 学号 CHAR(10) PRIMARY KEY, 姓名 NVARCHAR(20) NOT NULL, 性别 NCHAR(2) DEFAULT 男, 出生日期 DATE, 院系名称 NVARCHAR(50), 备注 NVARCHAR(200) ); -- 课程表 CREATE TABLE Cou200905 ( 课程号 CHAR(6) PRIMARY KEY, 课程名 NVARCHAR(50) NOT NULL, 先行课 CHAR(6) NULL, 学分 DECIMAL(3,1) DEFAULT 2.0 ); -- 选修表 CREATE TABLE SC200905 ( 学号 CHAR(10), 课程号 CHAR(6), 分数 DECIMAL(5,1), PRIMARY KEY (学号, 课程号), FOREIGN KEY (学号) REFERENCES Stu200905(学号), FOREIGN KEY (课程号) REFERENCES Cou200905(课程号) );这段脚本里GO 是批处理分隔符不是 SQL 语句本身在 SSMS 里用来分批执行。PRIMARY KEY 直接跟在列后面是列级约束写在最后是表级约束组合主键必须用表级约束。FOREIGN KEY 后面跟 REFERENCES 指定被引用的表和列被引用的列必须是主键或唯一键。DEFAULT 后面跟的值要和列类型匹配男 是字符串所以带引号2.0 是数字所以不带。建完表之后插入数据注意插入顺序也要先插学生和课程再插选修否则外键约束会报错。插入语句里字符串用单引号日期用 1999-01-01 这种格式SQL Server 一般能自动识别。2.3 表间关系到底怎么理解一对多与多对多学生表和课程表之间没有直接外键它们通过选修表关联。一个学生可以选多门课一门课可以被多个学生选所以学生和课程是多对多关系选修表就是中间表。选修表的组合主键学号课程号保证了同一个学生不能重复选同一门课这就是实体完整性的体现。外键约束保证了参照完整性你不能往选修表里插一个学生表里不存在的学号也不能删掉一个已经被选修表引用的学生记录。如果非要删得先删选修表里的对应记录或者建外键时加 ON DELETE CASCADE。实验里没要求级联删除所以默认是 RESTRICT删被引用的记录会直接报错。注意如果建表时忘了设外键后面可以用 ALTER TABLE 补但前提是现有数据必须满足外键约束否则加不上去。3. 实验二单表查询与多表连接把 SELECT 写明白3.1 单表查询WHERE 后面那些条件怎么写才不翻车实验二的单表查询有 10 个题目覆盖了比较运算符、逻辑运算符、LIKE、BETWEEN、ORDER BY、GROUP BY 和聚合函数。挑几个容易出错的讲。查询姓“李”的学生学号和姓名用WHERE 姓名 LIKE 李%。百分号是通配符匹配任意多个字符。如果写成LIKE 李_下划线只匹配一个字符那就只能查出姓李且名字只有两个字的学生。这里有个玄学问题如果数据库排序规则区分大小写LIKE 李% 可能受排序规则影响但中文一般不受影响。查询考试成绩有不及格的学生的学号用WHERE 分数 60。但要注意分数为 NULL 的记录不会被查出来因为 NULL 和任何值比较都返回 UNKNOWN。如果想把没出成绩的也算上得写WHERE 分数 60 OR 分数 IS NULL。查询选修了两门以上课程的学生学号与课程数用GROUP BY 学号 HAVING COUNT(*) 2。这里 WHERE 和 HAVING 的区别是关键WHERE 在分组前过滤行HAVING 在分组后过滤组。聚合函数不能写在 WHERE 里只能写在 HAVING 里。-- 查询选修课程数大于等于2的学生的学号、平均成绩和选课门数按平均成绩降序 SELECT 学号, AVG(分数) AS 平均成绩, COUNT(*) AS 选课门数 FROM SC200905 GROUP BY 学号 HAVING COUNT(*) 2 ORDER BY 平均成绩 DESC;AVG 会自动忽略 NULL 值如果某个学生有一门课分数为 NULL那门课不计入平均分但计入 COUNT(*)。如果想让 COUNT 也忽略 NULL得写 COUNT(分数)。ORDER BY 默认升序DESC 是降序可以跟多个列比如ORDER BY 平均成绩 DESC, 学号 ASC。3.2 多表连接查询INNER JOIN 和 OUTER JOIN 的结果差在哪多表查询的核心是把两张或多张表按关联条件拼在一起。实验里要求比较内连接和外连接的结果这是很多人绕不过去的坎。查询选修了【数据库原理】的计算机系学生学号和姓名需要三张表Stu200905、SC200905、Cou200905。写法有两种一种是 JOIN ON一种是 WHERE 隐式连接。-- 显式内连接 SELECT s.学号, s.姓名 FROM Stu200905 s JOIN SC200905 sc ON s.学号 sc.学号 JOIN Cou200905 c ON sc.课程号 c.课程号 WHERE c.课程名 N数据库原理 AND s.院系名称 N计算机系; -- 隐式连接 SELECT s.学号, s.姓名 FROM Stu200905 s, SC200905 sc, Cou200905 c WHERE s.学号 sc.学号 AND sc.课程号 c.课程号 AND c.课程名 N数据库原理 AND s.院系名称 N计算机系;两种写法结果一样但显式 JOIN 更清晰尤其是多张表的时候。N数据库原理 前面的 N 表示 Unicode 字符串防止中文乱码虽然 SQL Server 里不加 N 一般也能识别但加上更保险。查询所有学生的信息和他们选修的课程编号包括没选课的学生这时候必须用 LEFT JOIN。如果用 INNER JOIN没选课的学生会被过滤掉。SELECT s.学号, s.姓名, sc.课程号 FROM Stu200905 s LEFT JOIN SC200905 sc ON s.学号 sc.学号;LEFT JOIN 保留左表所有行右表没匹配的填 NULL。RIGHT JOIN 反过来FULL JOIN 两边都保留。实验里还要求查每一门课的间接先行课就是先行课的先行课这需要把课程表自己和自己连接叫自连接。SELECT c1.课程号, c2.先行课 AS 间接先行课 FROM Cou200905 c1 JOIN Cou200905 c2 ON c1.先行课 c2.课程号 WHERE c2.先行课 IS NOT NULL;自连接必须给表起两个别名否则没法区分。这里 c1 是当前课c2 是它的先行课c2.先行课 就是间接先行课。WHERE 条件过滤掉没有间接先行课的记录。3.3 聚合与排序GROUP BY 和 ORDER BY 的执行顺序很多人写查询时搞不清 GROUP BY 和 ORDER BY 谁先谁后。实际执行顺序是 FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY。所以 ORDER BY 里可以用 SELECT 里定义的别名但 WHERE 里不能用。实验里有一题是查计算机系学生选修课程数大于 2 的姓名、平均成绩和选课门数按平均成绩降序。这里 WHERE 过滤院系GROUP BY 按学生分组HAVING 过滤课程数ORDER BY 排序。注意姓名不在 GROUP BY 里但可以出现在 SELECT 里因为按学号分组后姓名是唯一的。如果按院系分组SELECT 里就不能直接写姓名除非用聚合函数。提示SQL Server 对 GROUP BY 的列要求比较严格SELECT 里非聚合列必须出现在 GROUP BY 里否则报错。MySQL 默认宽松模式可能不报错但结果不确定。4. 实验三嵌套查询与组合查询子查询和集合运算怎么选4.1 嵌套查询IN、EXISTS 和比较运算符的适用场景实验三的题目要求用嵌套查询和组合查询两种方法实现同一需求这是为了让你理解不同写法的等价性。嵌套查询就是把一个 SELECT 的结果作为另一个 SELECT 的条件。统计选修了【数据库原理】课程的学生人数可以用 IN 嵌套SELECT COUNT(*) FROM SC200905 WHERE 课程号 IN ( SELECT 课程号 FROM Cou200905 WHERE 课程名 N数据库原理 );也可以用 EXISTSSELECT COUNT(*) FROM SC200905 sc WHERE EXISTS ( SELECT 1 FROM Cou200905 c WHERE c.课程号 sc.课程号 AND c.课程名 N数据库原理 );IN 适合子查询返回少量值的情况EXISTS 适合子查询返回大量值或需要关联外层表的情况。EXISTS 只关心子查询有没有返回行不关心返回什么所以 SELECT 1 就够了。性能上EXISTS 通常比 IN 快因为 IN 要先执行子查询再拿结果去匹配EXISTS 是逐行检查。查询没有选修【数据库原理】课程的学生信息用 NOT IN 或 NOT EXISTS。这里有个坑如果子查询结果里有 NULLNOT IN 会返回空结果因为 NULL 和任何值比较都是 UNKNOWN。所以用 NOT IN 时要确保子查询列没有 NULL或者改用 NOT EXISTS。查询其他系中比计算机系学生年龄都小的学生信息需要用到 ALL 或 MAXSELECT * FROM Stu200905 WHERE 院系名称 N计算机系 AND 出生日期 ALL ( SELECT 出生日期 FROM Stu200905 WHERE 院系名称 N计算机系 );出生日期越大年龄越小所以比所有计算机系学生年龄都小等价于出生日期大于所有计算机系学生的出生日期。ALL 表示比子查询结果里所有值都大也可以用 (SELECT MAX(出生日期) ...)替代。4.2 组合查询UNION、INTERSECT 和 EXCEPT 的等价改写组合查询是把两个 SELECT 的结果做集合运算。UNION 是并集INTERSECT 是交集EXCEPT 是差集。实验要求用组合查询和嵌套子查询两种方法实现同一需求这是为了让你看到不同思路。查询被 0602001 或 0602002 学生选修的课程号用 UNIONSELECT 课程号 FROM SC200905 WHERE 学号 0602001 UNION SELECT 课程号 FROM SC200905 WHERE 学号 0602002;UNION 会自动去重如果不想去重用 UNION ALL。用 IN 条件查询的等价写法SELECT DISTINCT 课程号 FROM SC200905 WHERE 学号 IN (0602001, 0602002);查询两个学生同时选修的课程号用 INTERSECTSELECT 课程号 FROM SC200905 WHERE 学号 0602001 INTERSECT SELECT 课程号 FROM SC200905 WHERE 学号 0602002;用 EXISTS 嵌套的等价写法SELECT 课程号 FROM SC200905 sc1 WHERE 学号 0602001 AND EXISTS ( SELECT 1 FROM SC200905 sc2 WHERE sc2.学号 0602002 AND sc2.课程号 sc1.课程号 );查询被 0602001 选修但没被 0602002 选修的课程号用 EXCEPTSELECT 课程号 FROM SC200905 WHERE 学号 0602001 EXCEPT SELECT 课程号 FROM SC200905 WHERE 学号 0602002;用 NOT EXISTS 的等价写法SELECT 课程号 FROM SC200905 sc1 WHERE 学号 0602001 AND NOT EXISTS ( SELECT 1 FROM SC200905 sc2 WHERE sc2.学号 0602002 AND sc2.课程号 sc1.课程号 );INTERSECT 和 EXCEPT 在 SQL Server 里支持但 MySQL 不支持MySQL 里要用 JOIN 或 EXISTS 改写。这是数据库移植时常见的坑。4.3 子查询的层次嵌套与括号配对实验要求里专门提到「语句的层次嵌套关系以及括号的配对使用问题」这不是废话。嵌套查询写多了括号很容易多一层少一层尤其是子查询里再套子查询。一个典型的三层嵌套查选修了「数据库原理」且成绩大于该课平均成绩的学生学号。SELECT 学号 FROM SC200905 WHERE 课程号 (SELECT 课程号 FROM Cou200905 WHERE 课程名 N数据库原理) AND 分数 ( SELECT AVG(分数) FROM SC200905 WHERE 课程号 (SELECT 课程号 FROM Cou200905 WHERE 课程名 N数据库原理) );这里最内层子查询查课程号中间层算平均分最外层过滤。括号从内到外配对写的时候建议先写最内层再一层层往外包。如果括号对不上SSMS 会报语法错误但错误位置不一定准确可能指向括号附近。注意子查询返回多行时不能用 比较要用 IN 或 ANY/ALL。如果子查询返回多列也不能直接比较得用 EXISTS 或 JOIN。5. 避坑与排查实验里最容易翻车的五个地方5.1 中文乱码N 前缀和排序规则现象插入中文数据后查询出来是问号或者 WHERE 条件里中文匹配不上。原因数据库默认排序规则不是中文排序规则或者字符串没加 N 前缀。SQL Server 里 varchar 存中文可能乱码nvarchar 不会。解决建库时指定排序规则COLLATE Chinese_PRC_CI_AS字符串常量加 N 前缀比如N计算机系。列类型用 nvarchar 而不是 varchar。5.2 外键冲突插入顺序和删除顺序现象往选修表插数据时报「外键约束冲突」或者删学生表记录时报「被引用」。原因插入时先插了选修表被引用的学生或课程还不存在。删除时先删了学生表选修表里还有引用。解决插入顺序按被引用表先插引用表后插。删除顺序反过来。或者建外键时加 ON DELETE CASCADE 自动级联删除但实验里一般不用。5.3 NULL 值陷阱NOT IN 和聚合函数现象WHERE 学号 NOT IN (SELECT 学号 FROM ...)返回空结果。原因子查询结果里有 NULLNULL 和任何值比较都是 UNKNOWNNOT IN 整个条件变成 UNKNOWN过滤掉所有行。解决子查询里加WHERE 学号 IS NOT NULL或者改用 NOT EXISTS。聚合函数 AVG、SUM、COUNT 会自动忽略 NULL但 COUNT(*) 不忽略。5.4 分组报错SELECT 列不在 GROUP BY 里现象SELECT 姓名, AVG(分数) FROM ... GROUP BY 学号报错「列名无效」。原因SQL Server 要求 SELECT 里非聚合列必须出现在 GROUP BY 里。姓名不在 GROUP BY 里所以报错。解决把姓名加到 GROUP BY 里或者用聚合函数包起来比如MAX(姓名)。如果按学号分组后姓名唯一可以加GROUP BY 学号, 姓名。5.5 连接条件漏写笛卡尔积现象多表查询结果行数爆炸比如三张表各 10 行结果出来 1000 行。原因JOIN 或 WHERE 里漏了连接条件导致笛卡尔积。解决检查每个 JOIN 后面有没有 ON或者 WHERE 里有没有关联条件。N 张表连接至少需要 N-1 个连接条件。写完后先SELECT COUNT(*)看看行数是否合理。6. 进阶技巧把实验报告变成可复用的 SQL 模板实验报告交完就扔太可惜了。我一般会把「学生选课」这个模型整理成一套可复用的 SQL 模板以后遇到类似的业务场景直接改表名和字段名就能用。比如电商场景里学生对应买家课程对应商品选修对应订单查询逻辑几乎一样。具体做法是建一个sql_templates目录按查询类型分文件single_table.sql、join_queries.sql、subqueries.sql、set_operations.sql。每个文件里把实验里的题目改写成参数化查询用dept_name、course_name这种变量代替硬编码值。-- 模板查某院系选修某课程的学生 DECLARE dept NVARCHAR(50) N计算机系; DECLARE course NVARCHAR(50) N数据库原理; SELECT s.学号, s.姓名 FROM Stu200905 s JOIN SC200905 sc ON s.学号 sc.学号 JOIN Cou200905 c ON sc.课程号 c.课程号 WHERE s.院系名称 dept AND c.课程名 course;这样改参数就能复用不用每次重写 JOIN。另外实验里没要求建索引但实际工作中外键列和经常出现在 WHERE 里的列都应该建索引。比如CREATE INDEX idx_sc_stu ON SC200905(学号)能加速按学号查选修记录。验证查询是否正确我习惯用「小数据量手工核对」先插三五条数据手动算出预期结果再跑查询对比。如果结果一致再插大量数据测性能。这个习惯是从一次翻车经历来的——当时写了个多表连接小数据量看着对上了十万行数据后发现漏了一个连接条件结果全错。从那以后我每次写完复杂查询都强制走一遍小数据量核对再跑 EXPLAIN 看执行计划。希望帮到你。本文还有配套的精品资源点击获取
返回列表