简介《数据库原理》实验报告面向南华大学大数据技术及相关专业学生服务于数据库课程实验、期末复习与实验报告撰写参考。内容围绕 SQL Server Management Studio 的使用展开覆盖认识 DBMS、图形界面与 SQL 语句建库建表、主码外码与默认值设置、单表查询和多表连接查询、高级 SQL 查询以及数据插入更新删除等基本操作能帮助读者理解表间一对多、多对多关系并掌握 SELECT、JOIN、分组排序等查询思路。资源以 1 个 doc 文档交付压缩包约 5.38MB结构按实验题目、实验要求、实验代码与总结组织便于对照练习和查漏补缺。已有 377 人学习下载适合需要参考完整实验流程、代码写法与报告框架的数据库初学者使用。1. 一份数据库原理实验报告真正值钱的是那条从建库到组合查询的链路课程群里流转的数据库原理实验报告多数人扫一眼就存进网盘吃灰等到期末要交才翻出来。我拆的这份不太一样它把四个实验按依赖顺序串成了一条可执行的链路——先在某高校实验室的 SQL Server 环境里用 SSMS 点出库和表再用 T-SQL 把同样的结构写一遍接着是单表十题、多表八题、嵌套与组合六题最后收在 INSERT/UPDATE/DELETE 上。对刚接触数据库的大数据技术专业学生它是一份能照着敲的作业骨架对已经工作、SQL 写得半生不熟的人它是一份把主外码、连接、子查询这些概念重新对齐的清单。下面按我复现时的顺序拆重点讲清楚每步为什么这么做、参数怎么改、哪里容易翻车。2. 建库建表三张表的主外码、默认值与图形化/脚本两条路2.1 先把主码外码和默认值定死再动手建表实验一给了三张表的字段清单看起来简单但真正决定后面查询能不能跑通的是建表阶段这几个约束有没有定对。学生表的主码是学号课程表的主码是课程号选修表的主码是学号课程号这个组合——注意它不是单独一列而是两列联合主码这一点如果建错后面“一个学生选同一门课两次”就拦不住。外码方向也要先想清楚选修表的学号引用学生表课程号引用课程表所以建表顺序必须是学生、课程在前选修在后。反过来先建选修表外码约束会直接报错。默认值这块性别给男、学分给4是实验要求里明确写的实际做的时候我建议把默认值只加在确实有业务含义的列上别给出生日期也塞一个默认值否则插入脏数据时反而掩盖问题。数据类型的选择有个容易忽略的点学号用CHAR(8)还是VARCHAR(10)取决于学号是否定长。这份报告里学号形如0602001定长 7 位用CHAR(7)更省空间也更快比较但如果后续要兼容带字母的学号VARCHAR(10)更稳。我一般会按“当前数据定长就用 CHAR可能变长就用 VARCHAR”来选不追求一步到位。2.2 SSMS 图形化建库建表的操作顺序图形化那条路适合第一次接触 SQL Server 的人建立空间感但顺序错了会反复返工。我复现时的操作顺序是这样的第一步对象资源管理器里右键“数据库”→“新建数据库”名称填学生选课200905文件路径保持默认点确定。这里不建议改文件增长方式课程实验的数据量用默认配置完全够。第二步展开新建的库右键“表”→“新建表”在列编辑器里逐列填名称、类型、是否允许 Null。填完后选中“学号”那一行点工具栏的主键按钮如果是联合主键按住 Ctrl 同时选中两列再点主键。第三步保存表时 SSMS 会弹窗让你填表名填学生200905。这里有个坑表名以中文开头完全没问题但如果表名里带空格或特殊符号后续写 SQL 就必须用方括号包起来不熟练的人很容易在这里卡住。第四步建表间关系不要靠手写外码走“数据库关系图”更直观右键“数据库关系图”→“新建”把三张表加进来从学生表的学号拖到选修表的学号上弹窗里确认主键表和外码表点确定。课程表同理。关系图保存时如果提示“缺少表”说明还有表没加进来补上再存。图形化建完之后强烈建议右键数据库→“任务”→“生成脚本”把整个库的结构导成 .sql 文件。这份脚本就是你后面写 T-SQL 版本的底稿比对着界面手敲快得多。2.3 用 T-SQL 复现 StuCou 库建库、建表、插数据实验一的后半段要求用 SQL 语句再建一个StuCou200905库结构和前面一样。我习惯把整个脚本写成可重复执行的形式开头先判断库是否存在存在就删掉重建避免反复调试时残留旧表。-- 可重复执行库已存在则先删除 IF DB_ID(StuCou200905) IS NOT NULL DROP DATABASE StuCou200905; GO CREATE DATABASE StuCou200905; GO USE StuCou200905; GO -- 先建被引用的学生表 CREATE TABLE dbo.学生200905 ( [学号] CHAR(7) NOT NULL, [姓名] VARCHAR(20) NOT NULL, [性别] CHAR(2) NOT NULL CONSTRAINT DF_学生_性别 DEFAULT 男, [出生日期] DATE NULL, [院系名称] VARCHAR(30) NULL, [备注] VARCHAR(50) NULL, CONSTRAINT PK_学生200905 PRIMARY KEY ([学号]) ); GO -- 课程表先行课允许为空表示没有先修要求 CREATE TABLE dbo.课程200905 ( [课程号] CHAR(3) NOT NULL, [课程名] VARCHAR(30) NOT NULL, [先行课] CHAR(3) NULL, [学分] SMALLINT NOT NULL CONSTRAINT DF_课程_学分 DEFAULT 4, CONSTRAINT PK_课程200905 PRIMARY KEY ([课程号]) ); GO -- 选修表联合主码 两个外码 CREATE TABLE dbo.选修200905 ( [学号] CHAR(7) NOT NULL, [课程号] CHAR(3) NOT NULL, [分数] SMALLINT NULL, CONSTRAINT PK_选修200905 PRIMARY KEY ([学号], [课程号]), CONSTRAINT FK_选修_学生 FOREIGN KEY ([学号]) REFERENCES dbo.学生200905([学号]), CONSTRAINT FK_选修_课程 FOREIGN KEY ([课程号]) REFERENCES dbo.课程200905([课程号]) ); GO这段脚本里有几个参数值得单独说。CHAR(7)对应报告里 7 位学号如果换成VARCHAR(10)也不会报错但联合主码的长度会影响索引大小课程实验数据量小感知不到真实项目里要算一下。CONSTRAINT DF_学生_性别 DEFAULT 男给默认值起了名字好处是后续想删默认值可以直接DROP CONSTRAINT DF_学生_性别不用去查系统视图。外码没有写ON DELETE CASCADE这是故意的——实验阶段先看清约束报错比让级联删除悄悄带走数据更有教学价值。插数据这一步报告里给了三张表的示例数据我建议按学生、课程、选修的顺序插同样是因为外码。分数列允许 NULL用来表示“选了但还没出成绩”这个设计在后面统计平均分时会带出一个坑第 5 章会讲。-- 按外码依赖顺序插入 INSERT INTO dbo.学生200905 ([学号],[姓名],[性别],[出生日期],[院系名称],[备注]) VALUES (0602001,李雷,男,1999-03-12,计算机系,NULL), (0602002,韩梅梅,女,2000-07-08,计算机系,NULL), (0602003,王强,男,1998-11-23,数学系,NULL); GO INSERT INTO dbo.课程200905 ([课程号],[课程名],[先行课],[学分]) VALUES (C01,数据库原理,NULL,4), (C02,数据结构,C01,4), (C03,操作系统,C02,3); GO INSERT INTO dbo.选修200905 ([学号],[课程号],[分数]) VALUES (0602001,C01,88),(0602001,C02,76), (0602002,C01,92),(0602002,C03,NULL), (0602003,C02,55); GO插入完立刻跑一句SELECT * FROM dbo.选修200905;确认行数和分数列都对。如果这里就发现分数成了 NULL 或者行数少一条八成是外码拦住了插入去检查学号、课程号是不是在被引用表里存在。2.4 数据更新INSERT、UPDATE、DELETE 的边界实验四的数据更新在报告里篇幅不长但它是后面所有查询的“数据源维护”环节。INSERT 已经在 2.3 里用过重点说 UPDATE 和 DELETE 的两个边界。UPDATE 最容易翻车的是漏写 WHERE。UPDATE 选修200905 SET 分数 分数 5;会把全表所有分数都加 5跑之前最好先用同样的 WHERE 跑一遍 SELECT 看命中几行。我一般的习惯是先写SELECT * FROM ... WHERE ...确认结果集没问题再把 SELECT 换成 UPDATEWHERE 原样保留。DELETE 的边界在外码上。如果直接DELETE FROM 学生200905 WHERE 学号0602001;而选修表里还有这个学生的选课记录SQL Server 会报外码冲突拒绝删除。这是参照完整性在起作用不是 bug。想删就得先删选修表里的子记录或者建外码时声明级联删除——课程实验阶段我建议保留报错让你亲眼看到约束在拦什么。-- 先看命中范围再动手 SELECT * FROM dbo.选修200905 WHERE [学号]0602001 AND [课程号]C02; UPDATE dbo.选修200905 SET [分数] 82 WHERE [学号]0602001 AND [课程号]C02; -- 删除子记录后再删主记录 DELETE FROM dbo.选修200905 WHERE [学号]0602003; DELETE FROM dbo.学生200905 WHERE [学号]0602003;这两条 DELETE 的顺序不能反反了就是外码报错。实际项目里我更倾向于用逻辑删除加一个is_deleted列而不是物理 DELETE但课程实验要求练物理删除照做就行。3. 单表查询十题WHERE 骨架、LIKE 通配与聚合的边界3.1 单表查询的固定骨架单表查询的十道题看起来五花八门拆开看都是同一个骨架SELECT 列清单 FROM 表 WHERE 行过滤 GROUP BY 分组 HAVING 组过滤 ORDER BY 排序。区别只在于哪些子句出现、哪些省略。我在复现时先把十道题按“考什么”分了四类条件过滤第 1、2、3、4、5、7 题、表达式计算第 6 题年龄、排序取前 N第 8 题、分组聚合第 9、10 题。分类之后写起来就不容易乱因为每一类对应的子句位置是固定的——WHERE 里不能出现聚合函数聚合条件必须挪到 HAVING这是新手最常犯的语法错误。3.2 十道题逐条拆解与代码第 1 题查计算机系全体学生信息是最直白的等值过滤。注意报告里列了六个字段实际写的时候用SELECT *也行但实验报告里最好把列名写全答辩时显得你知道自己在取什么。-- 1. 计算机系全体学生 SELECT [学号],[姓名],[性别],[出生日期],[院系名称],[备注] FROM dbo.学生200905 WHERE [院系名称] 计算机系;第 2 题姓“李”用LIKE 李%。这里有个中文通配的细节%匹配任意长度_匹配单个字符。如果写成LIKE 李_就只匹配“李”加一个字的姓名两个字的名字会被漏掉。另外 SQL Server 里字符串比较默认不区分大小写但中文没有大小写问题真正要注意的是尾随空格——CHAR类型会自动补空格比较时可能和预期不一致所以姓名列我用了VARCHAR而不是CHAR。-- 2. 姓李的学生 SELECT [学号],[姓名] FROM dbo.学生200905 WHERE [姓名] LIKE 李%; -- 3. 没有先行课的课程 SELECT [课程名] FROM dbo.课程200905 WHERE [先行课] IS NULL;第 3 题的关键是IS NULL不能写成 NULL。SQL 里 NULL 表示未知任何和 NULL 的等值比较结果都是 UNKNOWN不会返回行。这是血泪经验里排前三的坑后面第 5 章还会再提一次。第 4 题查有不及格的学生学号重点是DISTINCT。一个学生可能有多门不及格不去重的话学号会重复出现。-- 4. 有不及格记录的学生去重 SELECT DISTINCT [学号] FROM dbo.选修200905 WHERE [分数] 60; -- 5. 选修 C01 或 C02 的学生 SELECT [学号],[分数] FROM dbo.选修200905 WHERE [课程号] IN (C01,C02);第 5 题的IN等价于两个OR但可读性更好课程号多了也不容易漏括号。注意IN列表里如果有 NULL行为会变得微妙实际项目里我会避免在 IN 列表里放 NULL。第 6 题查计算机系学生姓名及年龄年龄是算出来的不是存出来的。SQL Server 里常用DATEDIFF(YEAR, 出生日期, GETDATE())但它算的是“跨了多少个年份边界”不是精确周岁。比如 1999-12-31 出生在 2000-01-01 这天算出来是 1 岁实际才 1 天。课程实验用这个够用但要知道它的偏差方向。-- 6. 姓名及年龄按年份差非精确周岁 SELECT [姓名], DATEDIFF(YEAR, [出生日期], GETDATE()) AS 年龄 FROM dbo.学生200905 WHERE [院系名称] 计算机系; -- 7. 1999—2000 年出生的计算机系学生 SELECT [姓名] FROM dbo.学生200905 WHERE [院系名称] 计算机系 AND [出生日期] BETWEEN 19990101 AND 20001231;第 7 题的日期字面量我用了19990101这种紧凑格式而不是1999-01-01。原因是后者在某些语言设置下会被解析成“1999 年 1 月 1 日”还是“1 月 1 日 1999 年”取决于会话的DATEFORMAT紧凑格式没有歧义。BETWEEN 的边界是闭区间20001231当天的记录会被包含进来如果出生日期列带时间部分20001231会漏掉当天 00:00 之后的记录——课程实验里出生日期是纯 DATE没这个问题但换成 DATETIME 就要改成 20010101。第 8 题姓李按字典序取前两个SQL Server 里用TOP 2配合ORDER BY。这里有个玄学点中文姓名的“字典顺序”取决于排序规则collation默认安装通常是Chinese_PRC_CI_AS按拼音排如果服务器排序规则是别的结果可能不同。实验报告里不会写这一层但换机器跑出不一样结果时先查SELECT SERVERPROPERTY(Collation);。-- 8. 姓李的学生按姓名排序取前两个 SELECT TOP 2 [学号],[姓名] FROM dbo.学生200905 WHERE [姓名] LIKE 李% ORDER BY [姓名]; -- 9. 选修两门以上课程的学生 SELECT [学号], COUNT(*) AS 课程数 FROM dbo.选修200905 GROUP BY [学号] HAVING COUNT(*) 2; -- 10. 选课数 2 的学号、平均分、门数按平均分降序 SELECT [学号], AVG([分数]) AS 平均成绩, COUNT(*) AS 选课门数 FROM dbo.选修200905 GROUP BY [学号] HAVING COUNT(*) 2 ORDER BY AVG([分数]) DESC;第 9 题报告原文写的是“两门以上”我按 2处理第 10 题写的是“大于等于 2”用 2。这两个条件差一个等号结果集会差一批人抄作业时最容易在这里对不上答案。3.3 年龄计算、日期区间和 TOP 的坑年龄那题除了DATEDIFF的边界偏差还有一个隐藏问题如果出生日期是 NULLDATEDIFF返回 NULL这一行会出现在结果里但年龄列是空。实验数据里没有 NULL 出生日期所以看不出来但真实数据里要决定是过滤掉还是显示为空。日期区间的坑集中在BETWEEN的闭区间语义和字面量格式上。我的习惯是纯 DATE 列用BETWEEN 起始日 AND 结束日带时间的列一律改成 起始日 AND 次日避免“结束日当天 00:00 之后的记录被漏掉”这种经典翻车。TOP 的坑在于它必须和ORDER BY一起用才有确定结果。SELECT TOP 2 * FROM 学生;语法上合法但返回哪两行取决于执行计划今天跑和明天跑可能不一样。实验报告里第 8 题明确要求按字典序所以ORDER BY不能省。另外TOP是 SQL Server 的方言换成 MySQL 要写LIMIT换成 Oracle 要写FETCH FIRST跨库迁移时这是第一批要改的语法。4. 多表连接与嵌套子查询从 JOIN 到 EXISTS 的八加三题4.1 连接的本质笛卡尔积怎么被 ON 收窄多表连接的八道题很多人在第一题就卡住原因是没想明白连接到底在做什么。两张表做连接第一步是把左表的每一行和右表的每一行两两组合得到笛卡尔积第二步是用ON里的条件把不满足的行过滤掉。所以ON写漏了结果行数就是两张表行数的乘积数据量一大直接卡死查询窗口。理解这个之后内连接和外连接的区别就清楚了内连接只保留ON匹配上的组合左外连接保留左表全部行右表没匹配上的列补 NULL。实验报告第 5、6 题要“所有学生的信息”和“所有课程的名字”用的就是左外连接因为要包含没选课的学生和没被选的课程。三表连接可以看成两次两两连接学生和选修先连结果再和课程连。写的时候我习惯把连接条件紧跟在对应的JOIN后面而不是全堆在 WHERE 里这样内连接和外连接混用时不容易把外连接写成内连接。4.2 八道多表题逐条拆解第 1 题查选修数据库原理的计算机系学生需要三张表学生表过滤院系课程表过滤课程名选修表做桥接。-- 1. 选修“数据库原理”的计算机系学生 SELECT s.[学号], s.[姓名] FROM dbo.学生200905 s JOIN dbo.选修200905 sc ON s.[学号] sc.[学号] JOIN dbo.课程200905 c ON sc.[课程号] c.[课程号] WHERE s.[院系名称] 计算机系 AND c.[课程名] 数据库原理;第 2 题查间接先行课是自连接的典型。课程表自己和自己连一门课的先行课是另一门课先行课的先行课再连一次。报告要求“有间接先行课才算”所以两次连接都用内连接天然过滤掉没有先行课的课程。-- 2. 间接先行课课程 - 先行课 - 先行课的先行课 SELECT a.[课程号] AS 课程, c.[课程号] AS 间接先行课 FROM dbo.课程200905 a JOIN dbo.课程200905 b ON a.[先行课] b.[课程号] JOIN dbo.课程200905 c ON b.[先行课] c.[课程号];第 3 题查学号、姓名、课程名、成绩是最标准的三表内连接。第 4 题查选修了课程的学生姓名可以用DISTINCT去重也可以用EXISTS两种写法结果一样执行计划在数据量小时差别不大。-- 3. 学号、姓名、课程名、成绩 SELECT s.[学号], s.[姓名], c.[课程名], sc.[分数] FROM dbo.学生200905 s JOIN dbo.选修200905 sc ON s.[学号] sc.[学号] JOIN dbo.课程200905 c ON sc.[课程号] c.[课程号]; -- 4. 选修了课程的学生姓名EXISTS 写法 SELECT s.[姓名] FROM dbo.学生200905 s WHERE EXISTS ( SELECT 1 FROM dbo.选修200905 sc WHERE sc.[学号] s.[学号] );第 5 题要所有学生信息加课程编号包括没选课的学生必须用左外连接。这里有个细节LEFT JOIN之后没选课的学生课程号是 NULL如果只要学生信息、课程号可有可无这样写没问题如果后续还要对课程号做过滤过滤条件不能写在 WHERE 里否则外连接会被退化成内连接——这是第 5 章要展开的坑之一。-- 5. 所有学生 所选课程编号保留没选课的学生 SELECT s.[学号], s.[姓名], s.[性别], s.[出生日期], s.[院系名称], s.[备注], sc.[课程号] FROM dbo.学生200905 s LEFT JOIN dbo.选修200905 sc ON s.[学号] sc.[学号]; -- 6. 所有课程 被选情况保留没被选的课程 SELECT c.[课程名], sc.[学号], sc.[课程号], sc.[分数] FROM dbo.课程200905 c LEFT JOIN dbo.选修200905 sc ON c.[课程号] sc.[课程号];第 7 题“所有可能的选修情况”是个容易看漏的题它要的是学生和课程的笛卡尔积不管实际选没选。用CROSS JOIN最直白或者省略连接条件让两张表直接逗号分隔。数据量大时笛卡尔积很危险但课程实验里学生和课程都只有几行没问题。-- 7. 所有学生 × 所有课程的组合 SELECT s.[学号], s.[姓名], c.[课程号], c.[课程名] FROM dbo.学生200905 s CROSS JOIN dbo.课程200905 c;第 8 题查计算机系选课数大于 2 的学生是“连接 分组 HAVING”的组合。注意COUNT(*)数的是选课记录行数如果一个学生同一门课选了两次联合主码保证不会这里会重复计数用COUNT(DISTINCT sc.[课程号])更稳。-- 8. 计算机系选课数 2 的学生 SELECT s.[姓名], AVG(sc.[分数]) AS 平均成绩, COUNT(*) AS 选课门数 FROM dbo.学生200905 s JOIN dbo.选修200905 sc ON s.[学号] sc.[学号] WHERE s.[院系名称] 计算机系 GROUP BY s.[学号], s.[姓名] HAVING COUNT(*) 2 ORDER BY AVG(sc.[分数]) DESC;4.3 自连接查间接先行课第 2 题的自连接值得单独说两句因为它是八道题里唯一一道“同一张表出现三次”的。三次引用必须用三个不同的别名否则 SQL Server 会报“对象名重复”。别名我用了 a、b、c对应“课程本身、直接先行课、间接先行课”三层。写自连接时最容易犯的错是把连接方向搞反。a.[先行课] b.[课程号]表示“a 的先行课是 b 这门课”方向反了写成b.[先行课] a.[课程号]语义就变成“a 是 b 的先行课”结果集完全不同。验证方法是拿一门已知有间接先行课的课程手动推一遍C03 的先行课是 C02C02 的先行课是 C01所以 C03 的间接先行课应该是 C01。跑完 SQL 对一下对不上就是方向反了。另外如果一门课只有直接先行课、没有间接先行课两次内连接之后这一行会被过滤掉正好符合题目“有间接先行课才算”的要求。如果题目改成“所有课程及其间接先行课”就要把第二次连接改成左外连接。4.4 嵌套子查询IN、EXISTS、比较运算符的选型多表连接之外实验二里还混了几道嵌套子查询的题比如“查询其他系中比计算机系学生年龄都小的学生”。这道题的关键词是“都小”对应ALL或MAX比计算机系所有学生年龄都小等价于出生日期比计算机系最晚出生的那个还晚。-- 比计算机系所有学生都小出生日期更晚 SELECT [学号],[姓名],[出生日期],[院系名称] FROM dbo.学生200905 WHERE [院系名称] 计算机系 AND [出生日期] ( SELECT MAX([出生日期]) FROM dbo.学生200905 WHERE [院系名称] 计算机系 );IN、EXISTS、ANY/ALL这三类子查询的选型我的经验是子查询结果是小列表就用IN关心“存在性”就用EXISTS做全称比较就用ALL或聚合函数改写。EXISTS在子查询列上不关心返回什么SELECT 1就够写SELECT *也不会更慢但可读性差一些。NOT IN要特别小心如果子查询结果里含 NULL整个NOT IN会返回空结果集这是 SQL 三值逻辑的经典陷阱改成NOT EXISTS更安全。5. 避坑排查五个现象、五个原因、五个解法5.1 五个翻车现场现象一建表时报“对象名无效”或外码引用失败。原因几乎都是建表顺序错了先建了选修表而学生表还不存在。解法是把脚本按“学生→课程→选修”的顺序排或者先建三张表不加外码最后用ALTER TABLE ... ADD CONSTRAINT统一补外码。我一般用后者因为外码单独一段报错时一眼能看出是哪张表的问题。现象二WHERE 先行课 NULL查出来零行。原因是 NULL 不能用等号比较任何 NULL的结果都是 UNKNOWN。解法是改成IS NULL反过来“不为空”用IS NOT NULL不能写 NULL。这个坑在实验二第 3 题就会出现抄代码时如果照抄了 NULL结果集是空的但不会报错最容易被忽略。现象三左外连接写成内连接没选课的学生消失了。现象是查询结果比预期少几行。原因是把右表的过滤条件写进了WHERE比如WHERE sc.[分数] 60而右表没匹配上的行分数是 NULLNULL 参与比较返回 UNKNOWN整行被过滤掉外连接退化成内连接。解法是把右表的过滤条件挪到ON里LEFT JOIN 选修 sc ON s.学号 sc.学号 AND sc.分数 60。这个区别在实验二第 5、6 题里体现得最明显。现象四分组统计平均分结果偏高或偏低。原因是AVG([分数])会自动忽略 NULL 行而COUNT(*)会把 NULL 行算进去。选修表里如果有一条“选了但没出成绩”的记录分数为 NULLAVG的分母是剔除了这条的COUNT(*)却把它算进门数两个数字对不上。解法是统一口径要么用AVG(ISNULL([分数],0))把 NULL 当 0 处理业务上不一定合理要么用COUNT([分数])只数有成绩的记录。我一般会在报告里写清楚口径避免答辩时被追问。现象五日期字面量1999-1-1在某些机器上解析失败或解析成错误日期。原因是DATEFORMAT会话设置不同yyyy-mm-dd在大多数配置下没问题但yyyy-m-d这种不补零的写法在部分语言设置下会歧义。解法是统一用19990101紧凑格式或者用CONVERT(DATE, 1999-01-01, 120)显式指定格式。这条经验在跨机器交作业时特别有用同一份脚本在自己电脑上跑得好好的换到实验室机器就报错八成是日期格式。5.2 用校验 SQL 定位问题出问题之后别急着改代码先用几条校验 SQL 把数据摸清楚。表行数、NULL 分布、外码孤儿记录这三类检查能覆盖大部分“结果不对”的场景。-- 三张表各有多少行 SELECT 学生 AS 表名, COUNT(*) AS 行数 FROM dbo.学生200905 UNION ALL SELECT 课程, COUNT(*) FROM dbo.课程200905 UNION ALL SELECT 选修, COUNT(*) FROM dbo.选修200905; -- 分数为 NULL 的记录影响 AVG 和 COUNT 的口径 SELECT * FROM dbo.选修200905 WHERE [分数] IS NULL; -- 外码孤儿选修表里有没有学生表不存在的学号 SELECT sc.[学号] FROM dbo.选修200905 sc LEFT JOIN dbo.学生200905 s ON sc.[学号] s.[学号] WHERE s.[学号] IS NULL;第三段“外码孤儿”查询在外码约束存在时应该永远返回空返回非空说明约束被禁用了或者数据是绕过约束插进去的。课程实验里一般不会出现但养成这个检查习惯真实项目里能提前发现数据质量问题。5.3 建库阶段的三个隐性约束除了上面五个现象建库阶段还有三个不报错但会影响后续的隐性约束值得单独记一笔。第一个是排序规则。库级排序规则决定了字符串比较和排序行为建库时如果不指定继承服务器默认。中文姓名排序、LIKE匹配在不同排序规则下可能有细微差别。课程实验不用改但如果换机器后第 8 题结果不一样先查排序规则。第二个是NULL与空字符串的区别。VARCHAR列里和NULL是两回事参与IS NULL判断为假参与COUNT(列)会被计数。备注列如果允许为空插入时到底写NULL还是要在报告里统一。第三个是标识列。实验里的学号、课程号都是业务主键不是自增列所以建表时不要顺手加IDENTITY(1,1)。加了之后插入数据必须省略该列和报告里的 INSERT 语句对不上会报“列名或所提供值的数目与表定义不匹配”。6. 组合查询的等价改写与结果验收UNION、INTERSECT、EXCEPT 怎么对拍实验三的后三道题本质是同一个需求用两种写法实现正好拿来练“等价改写”的手感。第 4 题查被 0602001 或 0602002 选修的课程号UNION和IN两种写法-- UNION 写法两个结果集合并去重 SELECT [课程号] FROM dbo.选修200905 WHERE [学号]0602001 UNION SELECT [课程号] FROM dbo.选修200905 WHERE [学号]0602002; -- IN 写法一次扫描 SELECT DISTINCT [课程号] FROM dbo.选修200905 WHERE [学号] IN (0602001,0602002);第 5 题查两人同时选修的课程号INTERSECT和EXISTS两种写法。INTERSECT自带去重EXISTS写法要确认外层去重否则同一课程号可能出现多次。-- INTERSECT交集 SELECT [课程号] FROM dbo.选修200905 WHERE [学号]0602001 INTERSECT SELECT [课程号] FROM dbo.选修200905 WHERE [学号]0602002; -- EXISTS相关子查询 SELECT DISTINCT a.[课程号] FROM dbo.选修200905 a WHERE a.[学号]0602001 AND EXISTS ( SELECT 1 FROM dbo.选修200905 b WHERE b.[学号]0602002 AND b.[课程号]a.[课程号] );第 6 题查被 0602001 选但没被 0602002 选的课程号EXCEPT和NOT EXISTS两种写法。EXCEPT是差集注意顺序A EXCEPT B是“在 A 不在 B”反过来结果不同。NOT EXISTS写法里子查询的关联条件不能漏漏了就成了“0602002 什么都没选”时返回全部。-- EXCEPT差集顺序敏感 SELECT [课程号] FROM dbo.选修200905 WHERE [学号]0602001 EXCEPT SELECT [课程号] FROM dbo.选修200905 WHERE [学号]0602002; -- NOT EXISTS 等价写法 SELECT DISTINCT a.[课程号] FROM dbo.选修200905 a WHERE a.[学号]0602001 AND NOT EXISTS ( SELECT 1 FROM dbo.选修200905 b WHERE b.[学号]0602002 AND b.[课程号]a.[课程号] );组合查询的硬性约束是列数、列顺序、数据类型要一致。UNION两边一个查课程号、一个查学号列数对上了但类型都是 CHARSQL Server 不会报错结果却是错的。我的验收习惯是每组两种写法各跑一遍把结果贴到两个临时表里做EXCEPT双向比对两边都为空才算等价-- 等价性对拍两个结果集互相 EXCEPT都为空则一致 SELECT [课程号] FROM dbo.选修200905 WHERE [学号]0602001 EXCEPT SELECT [课程号] FROM dbo.选修200905 WHERE [学号]0602002 EXCEPT SELECT [课程号] FROM dbo.选修200905 WHERE [学号]0602001 AND [课程号] NOT IN (SELECT [课程号] FROM dbo.选修200905 WHERE [学号]0602002);这段对拍 SQL 本身不追求业务正确只用来验证“两种写法结果是否相同”。从那以后我每次交实验报告前都会把关键题目的两种写法跑一遍对拍结果不一致就回头查 NULL 和去重而不是盯着屏幕肉眼比对几十行——肉眼比对漏一行答辩时被指出来是真的尴尬。希望帮到你。本文还有配套的精品资源点击获取
企业数字化 ERP 产品动态
相关推荐
学生成绩管理系统数据库大作业:ER图到SQL统计查询实战 简介:这份北邮研一数据库大作业文档面向高校数据库课程学习者与课程设计备赛者,围绕学生成绩管理系统的需求分析、概念建模和关系数据库实现展开,适合需要参考完整设计流程、实验报告结构及SQL建表思路的本科生和研一学生。压缩包内含1个docx… · 2026/11/15 2:36:40
SAP BTP Subaccount区域选择与迁移实战指南 1. 理解SAP BTP中的Subaccount概念在SAP BTP(Business Technology Platform)架构中,Subaccount是最基础的组织单元。它相当于一个独立的工作空间,用于隔离不同的项目、团队或环境。每个Subaccount都拥有自己的资源配额、用户权限和… · 2026/11/14 12:34:47
供应链与产业链:概念辨析、核心逻辑与实操应用指南 供应链和产业链这两个词,在日常工作和媒体报道里出现的频率极高,但真正能把它们讲清楚、用对的人并不多。我自己在做产业研究和企业咨询的这些年里,见过太多人把这两个概念混着用——有人把供应商管理当成产业链管理,有人把产业链… · 2026/11/3 9:38:15
特种工延期复核怎么报名?过期证书怎么处理别再踩坑 特种工延期复核怎么报名?过期证书怎么处理别再踩坑 证书过期了不知道怎么复审延期,是不是你最近正在经历的糟心事?特种作业操作证是从事相关工作的“通行证”,一旦过期,轻则影响上岗,重则可能引发安全事故。现在就来聊聊【特种工延期复核】怎么报名,以… · 2026/11/15 2:36:05
电工证福利怎么领取通过率揭秘:河南学员避坑全攻略 电工证福利怎么领取通过率揭秘:河南学员避坑全攻略 怕考不过白交培训费?这年头谁不想省点钱、省点事,顺利拿到电工证?特别是河南地区的学员,很多人对【电工证福利怎么领取】这个话题特别关注,但真正了解的不多,甚至一不小心就掉进培训机构的“福利陷阱… · 2026/11/15 2:36:05
洋县哪里有考电工证 良心建议告诉你怎么考 洋县哪里有考电工证 良心建议告诉你怎么考 你是不是正愁找不到工作,因为 急需特种作业证才能上岗找工作 ?别急,今天我就从一个干了多年应急管理相关工作的老手角度, 良心建议 你看看洋县哪里能考电工证,怎么考,考了之后能干嘛,能不能升职加薪。… · 2026/11/15 2:36:05
工地太忙根本没时间复习考试,行政局电工证电子证书怎么搞定? 工地太忙根本没时间复习考试,行政局电工证电子证书怎么搞定? 你是不是也经常在工地来回跑,手上活儿干不完,脑袋里还得想着考证的事? 行政局电工证 这玩意儿,要是没考下来,别说干项目,连进场都难。现在考个证都得花时间, 电子证书… · 2026/11/15 2:36:05
建筑水电工证办理值不值得考?实操考试心里没底怕挂科 建筑水电工证办理值不值得考?实操考试心里没底怕挂科 实操考试心里没底怕挂科,这几乎是所有准备考【建筑水电工证办理】的人共同的焦虑。尤其是在河南,像商丘这样的地市,建筑行业用工量大、施工项目多,但持证上岗率却参差不齐。很多人在报名前都会反复问… · 2026/11/15 2:35:04
长春初级电工证怎么考?通过率揭秘全在这里 长春初级电工证怎么考?通过率揭秘全在这里 之前考的证在外省能不能转过来?这几乎是每个想从事电工工作的人都会问的问题。特别是那些在外地打拼多年、手里握着一张“外地电工证”的人,到了长春想继续干这一行,第一反应就是“我的证能用吗?”。今天我们就… · 2026/11/15 0:00:50
国内电工证报考条件与考试时间 低压电工证怎么考?学历不高也能报名,官方报名入口全在这 你是不是经常听说“电工证是入行门槛”,但又担心自己学历不够、经验不足,根本报不了名?别急,今天就带你从零了解【国内电工证】的报考流程、考试内容,还有怎么在 官方报名入口… · 2026/11/15 0:00:50
电工作业时如何断电?电子证书到底能不能全国通用? 电工作业时如何断电?电子证书到底能不能全国通用? 实操考试心里没底怕挂科,特别是电工作业时如何断电这一操作,不仅关系到你能不能拿到证书,更关系到以后能不能顺利上岗。很多人觉得这不过就是“断个电”,但其实这里面的门道可不少,稍有不慎就可能出错… · 2026/11/15 0:00:50
长春初级电工证怎么考?通过率揭秘全在这里 长春初级电工证怎么考?通过率揭秘全在这里 之前考的证在外省能不能转过来?这几乎是每个想从事电工工作的人都会问的问题。特别是那些在外地打拼多年、手里握着一张“外地电工证”的人,到了长春想继续干这一行,第一反应就是“我的证能用吗?”。今天我们就… · 2026/11/15 0:00:50
国内电工证报考条件与考试时间 低压电工证怎么考?学历不高也能报名,官方报名入口全在这 你是不是经常听说“电工证是入行门槛”,但又担心自己学历不够、经验不足,根本报不了名?别急,今天就带你从零了解【国内电工证】的报考流程、考试内容,还有怎么在 官方报名入口… · 2026/11/15 0:00:50
电工作业时如何断电?电子证书到底能不能全国通用? 电工作业时如何断电?电子证书到底能不能全国通用? 实操考试心里没底怕挂科,特别是电工作业时如何断电这一操作,不仅关系到你能不能拿到证书,更关系到以后能不能顺利上岗。很多人觉得这不过就是“断个电”,但其实这里面的门道可不少,稍有不慎就可能出错… · 2026/11/15 0:00:50
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/15 1:51:20
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/13 6:11:51
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/13 22:31:30