SQL Server数据库实验全攻略:从建表到查询的完整实操
简介太原理工大学软件工程专业《数据库概论》实验报告面向数据库初学者、正在完成SQL实验或备考数据库原理的在校生。文档基于SQL Server 2016平台完整记录了交互式SQL语句使用、数据完整性、用户鉴别与数据控制等实验过程含创建/修改/删除表、索引、视图以及增删改查等核心知识点。内容预览可见实验二包含Student、Course、Sc等基本表的建表语句涵盖主键、外键等约束设置并给出普通索引、唯一索引和聚簇索引的创建示例以及插入、修改、删除、多行插入等数据操作语句便于直接对照练习、补全个人实验报告或考前快速梳理。压缩包内仅有1个docx文件大小约1.69MB正文结构清晰可直接打开查看或按学校模板调整。已有311人学习下载适合需要实验参考或复习数据库操作要点的人群。1. 软件工程数据库实验的正确打开方式从太原理工实验报告里拆出的 SQL Server 实操数据库概论课最卡人的地方不是概念而是你明知道要写 SQL却不知道第一步敲什么。这份太原理工大学软件工程专业的实验报告把 SQL Server 2016 上的建表、增删改查、查询、视图、完整性约束串成了一条可复现的完整链路。它不是那种只给三行建表语句的敷衍模板光 Student 一个表就经历了创建、改字段、加约束、建索引、插数据、查数据、删数据全流程。我拆这份资源时最大的感受是每一个语法点都有能直接执行的语句对应适合正在做数据库实验、准备课程设计、或者单纯想把 SQL 基础语句过一遍的人。后面所有代码我都按 SQL Server 2016 的语法习惯做了标注照着敲能跑通。2. 建表与约束CREATE TABLE 的三表结构和五种完整性约束2.1 先搭骨架Student、Course、SC 三表的主键与外键关系学生选课系统那张经典的 ER 图落到 SQL Server 里就是三张表Student 存学生、Course 存课程、SC 存选课关系。这份报告里三张表的建表语句值得逐行看因为它们把主键、外键、联合主键一次演示全了CREATE TABLE Student( Sno char(8) primary key, -- 学号主键 Sname char(10), Ssex char(2), Sage int, Sdept char(20) ); CREATE TABLE Course( Cno char(4) primary key, -- 课程号主键 Cname char(40), Cpno char(4), -- 先修课课程号 Ccredit int ); CREATE TABLE SC( Sno char(8), Cno char(4), Score int, primary key(Sno, Cno), -- 联合主键一个学生选一门课对应一条记录 foreign key(Sno) references Student(Sno), foreign key(Cno) references Course(Cno) );这段代码的逻辑是Student 和 Course 各自用单列主键SC 表不能单独用 Sno 或 Cno 做主键因为一个学生会选多门课、一门课也会有多个学生选只有(Sno, Cno)的组合才能唯一确定一条选课记录。foreign key(Sno) references Student(Sno)声明了 SC 和 Student 之间的引用关系数据库会强制保证 SC 里的 Sno 一定存在于 Student 表中这是后面所有完整性验证的基础。参数上要注意 char 和 int 的取舍Sno 用 char(8) 是因为学号虽然看起来像数字但它不参与算术运算而且可能包含前导零用定长字符类型更安全。Sage 用 int是因为年龄参与Sage1这类算术表达式Ccredit 学分同理。如果以后要存小数成绩才需要考虑 numeric(4,1) 这类精确小数类型char 和 int 混用是新手建表时最容易犯的错。三张表建完后报告里用SELECT * FROM Student依次检查表是否创建成功。这一步建议照着做SQL Server 的图形界面SSMS里表结构有延迟刷新刚执行完 CREATE TABLE 立刻在左侧树里找经常看不到新表直接跑一条 SELECT 反而是最快的确认方式顺便还能验证表里目前是空的。2.2 约束的增删改CHECK、UNIQUE、DEFAULT 与 ALTER TABLE实验三把数据完整性单独拎出来做了验证这一部分的约束写法比实验二更完整。重点看 Student 表的 CHECK 和 DEFAULT以及 SC 表的 CHECK 约束CREATE TABLE Student( Sno CHAR(8) PRIMARY KEY, Sname CHAR(8) NOT NULL, Ssex CHAR(2) CHECK(Ssex in (男,女)), -- 性别只能填男或女 Sage SMALLINT, Sdept CHAR(20), Sclass CHAR(4) NOT NULL, Stotal smallint DEFAULT 0 -- 默认总学分 0 ); CREATE TABLE SC( Sno CHAR(8) FOREIGN KEY (Sno) REFERENCES Student(Sno), Cno CHAR(4), Grade SMALLINT CONSTRAINT SC_CHECK CHECK(Grade 0 AND Grade 100), PRIMARY KEY (Sno, Cno), CONSTRAINT FK_SC FOREIGN KEY (Cno) REFERENCES Course(Cno) );CHECK(Ssex in (男,女))是列级约束直接拦截违反性别取值范围的插入CONSTRAINT SC_CHECK CHECK(Grade 0 AND Grade 100)是显式命名的约束Grade 必须落在 0 到 100 之间。给约束起名是个好习惯后面要修改或删除约束时能直接按名字定位不用靠系统生成的随机约束名去猜。DEFAULT 0 则保证插入学生记录时不写 Stotal 也能有默认值。约束不只靠建表时一次性定义这份报告还演示了用 ALTER TABLE 对已有表补约束的常规操作ALTER TABLE Course ADD UNIQUE(Cname); -- 课程名不能重复 ALTER TABLE Course ADD FOREIGN KEY (Cpno) REFERENCES Course(Cno); -- 先修课必须是已存在的课程 ALTER TABLE SC DROP CONSTRAINT SC_CHECK; -- 删掉旧约束 ALTER TABLE SC ADD CONSTRAINT SC_CHECK CHECK(Grade 0 AND Grade 100); -- 换成允许0分ALTER TABLE Course ADD FOREIGN KEY (Cpno) REFERENCES Course(Cno)是一条自引用外键Cpno 存的是先修课的课程号它指向 Course 表自己的 Cno。这种写法在课程表里很常见但有个隐含前提——插入数据时先修课必须先于当前课程存在否则会违反外键约束。在 SQL Server 里执行时还要注意 Course 表里不能有孤儿数据否则 ALTER 加约束会失败。修改约束时先 DROP 再 ADD 是常规做法。SQL Server 没有直接改 CHECK 约束内容的语句只能删掉重建。实验里把Grade 0 AND Grade 100改成Grade 0 AND Grade 100就是为了让 0 分成绩也能录入这在语义上更贴近真实评分规则。如果表里已经存在违反新约束的数据ADD 操作会直接报错所以正式环境改约束前必须先做数据清洗。2.3 索引创建的三种形态普通索引、唯一索引与聚簇索引索引部分虽然只有四条语句但覆盖了三种最常见的索引形态差异在于约束强度和底层存储顺序CREATE INDEX iCname ON Course(Cname); -- 普通索引允许重复值 CREATE UNIQUE INDEX iSname ON Student(Sname); -- 唯一索引姓名不能重复 CREATE CLUSTERED INDEX iSnoCno ON Sc(Sno, Cno DESC); -- 聚簇索引数据物理排序 CREATE UNIQUE INDEX iCno ON Course(Cno); -- 与主键重复的唯一索引普通索引只加速查询不限制数据取值唯一索引在加速之外强制列值不重复在 Student 表这种以 Sno 为主键的表上给 Sname 建唯一索引意味着两个同名学生会被数据库拒绝这在真实场景里未必是好事。聚簇索引影响的是数据页的物理排列顺序一张表只能有一个聚簇索引排序字段的顺序和方向决定了数据实际落盘的组织方式。注意一张表只能创建一个聚簇索引。SC 表已经以 (Sno, Cno) 建了聚簇索引如果以后再想按 Score 建聚簇索引必须先删掉旧的否则会报错。实验报告里CREATE UNIQUE INDEX iCno ON Course(Cno)和 Course 表的主键约束其实功能重叠了主键本身会自动创建唯一索引再单独建一个纯粹多余还会增加写入维护开销。实验二里这批 SQL 存在不少这种为了演示而堆语句的痕迹照着跑没问题但理解每一条的用途边界才不会被带偏。索引不是越多越好写入频繁的表上冗余索引会拖慢 INSERT 和 UPDATE。3. 数据操作实战INSERT、UPDATE、DELETE 的语法细节与批量处理3.1 插入数据的四种方式单行、指定列、子查询与 SELECT INTO实验二的 3.2 节把插入数据拆成了四种场景从最基础的单行插入到用子查询批量搬数据一层层往上加难度。最基本的单行插入长这样INSERT INTO Student VALUES(20100001,李勇,男,20,CS,1001); INSERT INTO Student VALUES(20100003,刘洋,女,NULL,NULL,1001); -- 年龄和系别留空VALUES列表里的字段数量和顺序必须和建表时完全一致。第二个例子里的NULL是显式声明该列为空Sage 和 Sdept 没有 NOT NULL 约束所以允许这么干。这里容易出错的地方是字符类型字段的引号不能丢数字类型不能加引号——20会被隐式转成 20但一旦遇到 CHAR 字段和数字比较就会触发隐式转换索引可能失效。第二种方式是只指定部分列实验三里插入学生时就只给了五个字段INSERT INTO Student(Sno, Sname, Ssex, Sclass) VALUES(20100102,张盛,男,1008);这种写法的好处是没写的列走默认值Stotal 变成 0Sage 和 Sdept 是 NULL。推荐在实际项目里全部用这种显式列名的方式写 INSERT因为表结构一旦变过比如加了一列VALUES全列的写法会直接崩显式列名则不受影响。第三种方式最有价值用SELECT当数据源批量插入。报告里把计算机系的学生一次性搬进新表CREATE TABLE cs_Student( 学号 char(8), 姓名 char(8), 年龄 smallint ); INSERT INTO cs_Student SELECT Sno, Sname, Sage FROM Student WHERE Sdept CS;INSERT INTO ... SELECT不关心数据是不是手工敲的只要 SELECT 返回的列数与目标表匹配即可。常用于做临时表、备份子集、或者把旧表数据迁移到新表。注意这里目标表的列名可以随意用了中文字段名也不影响靠的是位置对应而不是名字对应所以 SELECT 里的列顺序必须写对错一位数据就串了。第四种是SELECT ... INTO它把查询结果直接落成一张新表连 CREATE TABLE 都省了SELECT * INTO tmpSC FROM SC;这条语句在实验里用于复制选课表做删除练习的原表实际操作中做数据备份或生成报表临时表也很好用。但它是物理复制不复制约束和索引tmpSC只是一张裸表不能指望它取代正式表更不能用它做外键关联的参照。3.2 修改数据的常见场景单条件更新、全表更新与关联子查询UPDATE 这条语句看起来简单实际写起来最容易漏条件。报告里的几个更新语句正好覆盖了三个典型场景。先看单条记录更新UPDATE Student SET Sage 22 WHERE Sno 20100001;WHERE Sno 20100001是定位条件SET Sage 22是赋值动作。这里最常出的幺蛾子是忘写 WHERE一旦忘写就是全表更新Sage 全被改成 22。报告紧接着的UPDATE Student SET Sage Sage 1就是故意演示全表更新——所有人年龄加一岁这种操作在真实生产库上是要被 DBA 拦下来的。UPDATE 和 DELETE 的 WHERE 条件最好先用等价的 SELECT 验证一遍确认影响行数符合预期再执行。带子查询的更新是这批语句里的技术点UPDATE SC SET Score Score 5 WHERE CS (SELECT Sdept FROM Student WHERE Student.Sno SC.Sno);SC 表本身没有系别字段要找到计算机系学生的选课记录必须通过 Sno 关联到 Student 表再判断 Sdept。子查询(SELECT Sdept FROM Student WHERE Student.Sno SC.Sno)对 SC 的每一行执行一次返回的是该学号对应的系。如果子查询返回多行SQL Server 会直接报错所以这里 Student.Sno 得是主键保证一对一。还有一种写法等效但更直观用 EXISTS 做条件。实际工作中我更习惯用 EXISTS因为它对命中多条记录不敏感逻辑更稳。报告里还演示了按姓名定位学号的更新UPDATE SC SET Score 80 WHERE Cno 2 AND Sno (SELECT Sno FROM Student WHERE Sname 刘晨);如果数据库里有两个刘晨子查询会返回多行这条语句会直接报错。这类问题在实验数据里遇不到但真实环境里人名重复是常态靠姓名做定位条件天然不安全这也是为什么业务表里要有唯一主键。3.3 删除数据的正确姿势临时表隔离与按条件批量删除删除部分是 DELETE 语句的完整演练同时也演示了一个非常重要的施工习惯先复制临时表再在临时表上做删除避免误删主表数据。报告里的顺序很有代表性DELETE FROM Student WHERE Sno 20100022; -- 直接删主表 DELETE FROM SC WHERE Cno 1 AND Sno 20100001; -- 按两列条件删 SELECT * INTO tmpSC FROM SC; -- 复制临时表 DELETE FROM tmpSC WHERE CS (SELECT Sdept FROM Student WHERE Student.Sno tmpSC.Sno); -- 子查询条件删除 DELETE FROM tmpSC; -- 清空临时表DELETE FROM tmpSC不带 WHERE 会清空整个表但它和TRUNCATE TABLE tmpSC不一样。DELETE 逐行操作会写事务日志、触发触发器速度慢TRUNCATE 直接释放数据页速度快且不触发逐行触发器。在实验环境里两者结果相同但在生产环境清空大表时用 TRUNCATE 还是 DELETE 得想清楚涉及事务日志空间和能否回滚的问题。删除操作里最有技巧性的是那条子查询条件删除删除计算机系学生的全部选课记录但 tmpSC 里没有系别字段所以用CS (SELECT Sdept FROM Student WHERE Student.Sno tmpSC.Sno)关联到 Student 表去判断。条件写成CS 子查询而不是子查询 CS两种写法语义相同但把常量放左边能避免在某些数据库方言里被优化器错误处理。删除的坑集中在主外键关系上。直接DELETE FROM Student WHERE Sno 20100001会失败因为 SC 表里还有这个学号的选课记录外键约束会拦住这次删除。这也是实验三里专门验证的部分后面避坑那章会展开这里先记住一个顺序原则先删子表再删父表。4. 查询语法全解单表过滤、分组统计、连接查询与嵌套子查询4.1 单表查询WHERE 条件、LIKE 模糊匹配、IN 集合与空值判断3.3 节的数据查询部分是整个实验二的重头戏从最简单全表查询一路写到集合运算。单表查询里最值得抠的是那些容易被忽略的子句组合比如按条件过滤的同时控制输出列SELECT Sname, Sage FROM Student WHERE Sdept CS; SELECT Sname, Sdept, Sage FROM Student WHERE Sage BETWEEN 20 AND 23; SELECT Sname, Sage FROM Student WHERE Sdept IN (IS, MA, CS); SELECT Sname, Sno, Sage FROM Student WHERE Sname LIKE 刘%; SELECT Sno, Cno FROM SC WHERE Score IS NULL;BETWEEN 20 AND 23是闭区间等价于Sage 20 AND Sage 23。IN (IS,MA,CS)等价于多个 OR 条件的简写。LIKE 刘%里的%是通配符匹配任意长度的字符序列所以能找出所有姓刘的学生。空值判断必须用IS NULL不能写成Score NULL—— NULL的结果永远是 UNKNOWN查询结果恒为空这是 SQL 新手最容易踩的坑后面避坑章节会专门说。表达式进目标列也是这个实验的看点SELECT Sname, 2004 - Sage AS YearOfBirth, LOWER(Sdept) FROM Student;2004 - Sage是把年龄换算成出生年份AS YearOfBirth给计算结果起了个别名LOWER(Sdept)把系名转成小写。SQL Server 允许在 SELECT 列表里放任意表达式不限于原表列这在报表查询里很常用。还有两种等价的列别名写法2000-Sage BIRTHDAY和DEPARTMENT LOWER(Sdept)前一种靠空格后一种用等号SQL Server 两种都认但可读性上AS最清晰建议统一用 AS。去重和排序也有讲究。SELECT DISTINCT Sno FROM Sc去掉重复学号和裸SELECT Sno一对比选修了课程的学生数就直接出来了。排序时ORDER BY Sdept, Sage DESC是先按系别升序同一系里再按年龄降序——DESC只作用于紧挨着的 Sage不作用于整条排序规则这个边界经常被误解。4.2 分组统计聚合函数、GROUP BY 与 HAVING 的配合方式聚合查询的核心是区分行级过滤和组级过滤。报告里的分组统计把这两者的边界演示得很清楚SELECT COUNT(*) FROM Student; -- 学生总人数 SELECT COUNT(DISTINCT Sno) FROM Sc; -- 选修了课程的学生人数 SELECT MAX(Score) FROM Sc; -- 最高分 SELECT SUM(Score) AS 总分, AVG(Score) AS 均分, MAX(Score) AS 最高分 FROM SC WHERE Cno 2; -- 2号课程汇总 SELECT Cno AS 课程号, COUNT(*) AS 人数, AVG(Score) AS 均分, MAX(Score) AS 最高分 FROM SC GROUP BY Cno; -- 每门课的统计 SELECT Cno AS 课程号, COUNT(*) AS 人数, AVG(Score) AS 均分, MAX(Score) AS 最高分 FROM SC GROUP BY Cno HAVING AVG(Score) 90; -- 只保留均分大于90的课程查询报告里有处小笔误SC 表建表时成绩列叫 Score分组统计里却写成了 AVG(Grade)。SQL Server 里列名不存在的写法会直接报列名 Grade 无效这也算实验报告里一个能用来练排错的素材照着敲如果报错优先检查列名拼写。WHERE Cno 2是在分组之前过滤行GROUP BY Cno按课程号把数据切成多个小组HAVING AVG(Score) 90是在分组完成后筛选组。WHERE 不能对聚合结果做条件判断HAVING 才是干这个的。COUNT(DISTINCT Sno) 统计的是去重后的选课人数同一个学生选多门课只算一次这个去重是聚合函数最常见的隐蔽行为。4.3 连接查询内连接、左外连接与复合条件连接的字段规划连接查询部分有几段代码在语法上是同一件事的三种写法正好可以对比学习。老式写法把连接条件写在 WHERE 里SELECT Student.Sno, Sname, Ssex, Sage, Sdept, Cno, Grade FROM Student, SC WHERE Student.Sno SC.Sno;这种写法现在依然能跑但 WHERE 里混了连接条件和过滤条件表一多就难读。推荐用显式 JOIN 语法SELECT Student.Sno, Sname, Ssex, Sage, Sdept, Cno, Grade FROM Student JOIN SC ON (Student.Sno SC.Sno); SELECT Student.Sno, Sname, Ssex, Sage, Sdept, Cno, Grade FROM Student LEFT OUTER JOIN SC ON (Student.Sno SC.Sno);JOIN 内连接只保留两表匹配的行没选课的学生不会出现在结果里。LEFT OUTER JOIN 左外连接保留左表 Student 的全部行右表 SC 没有匹配的选课记录时Cno、Grade 显示为 NULL。对外连接不敏感的人常在这里出问题——用了 LEFT JOIN 却不理解 NULL 来自哪里导致后续过滤条件把 NULL 行又滤掉了最终结果和内连接没区别。复合条件连接则展示了一个实用模式连接条件写 JOIN ON 里过滤条件写 WHERE 里各司其职SELECT Student.Sno, Sname FROM Student JOIN SC ON (Student.Sno SC.Sno) WHERE SC.Cno 2 AND SC.Grade 90;多表查询则把三张表串起来了。表和表之间靠外键搭桥Student 通过 Sno 连 SCSC 通过 Cno 连 Course最后 SELECT 同时取 Student.Sname 和 Course.CnameSELECT Student.Sno, Sname, Cname, Grade FROM Student, SC, Course WHERE Student.Sno SC.Sno AND SC.Cno Course.Cno;多表连接最实用的一条经验每张参与连接的表WHERE 里至少写一个连接条件否则会出现笛卡尔积——结果行数变成三张表行数的乘积数据量一大查询就卡死。比如报告里那条查选修了数据库系统原理课程的语句类似查询在真实环境里要先确认连接条件写全再用筛选条件缩小范围最后看执行计划确认没有全表扫描。4.4 嵌套查询与集合操作IN、EXISTS、UNION、INTERSECT 的适用边界嵌套查询这块实验报告用了四条典型语句IN 引出的子查询适合返回集合比较运算符合子查询适合找超过平均分的学生ALL 修饰符适合跨系比较EXISTS 适合存在性判断-- 与刘晨同系的学生 SELECT Sno, Sname, Sdept FROM Student WHERE Sdept IN (SELECT Sdept FROM Student WHERE Sname 刘晨); -- 找出每个学生超过他平均成绩的课程号 SELECT Sno, Cno FROM SC x WHERE Grade (SELECT AVG(Score) FROM SC y WHERE y.Sno x.Sno); -- 其他系中比计算机系所有学生年龄都小的学生 SELECT Sname, Sage FROM Student WHERE Sage ALL (SELECT Sage FROM Student WHERE Sdept CS) AND Sdept CS; -- 选修了 1 号课程的学生姓名 SELECT Sname FROM Student WHERE EXISTS (SELECT * FROM SC WHERE Sno Student.Sno AND Cno 1);相关子查询里外层行和内层查询通过y.Sno x.Sno关联每一行外层数据都会触发一次内层查询数据量大时性能要留意。Sage ALL (...)等价于小于计算机系所有人里最小的那个年龄也可以写成Sage (SELECT MIN(Sage) ...)后者往往更快。EXISTS 子查询只关心有没有行返回SELECT *在这里不消耗额外开销优化器不会真的取所有列。集合操作放在最后一部分UNION、INTERSECT、EXCEPT 分别对应并、交、差要求两个 SELECT 的列数和类型一一对应SELECT * FROM Student WHERE Sdept CS UNION SELECT * FROM Student WHERE Sage 19; SELECT * FROM Student WHERE Sdept CS INTERSECT SELECT * FROM Student WHERE Sage 19; SELECT * FROM Student WHERE Sdept CS EXCEPT SELECT * FROM Student WHERE Sage 19;UNION 默认去重想保留重复行要用 UNION ALL。INTERSECT 取两边都有的行EXCEPT 取左边有右边没有的行。这个实验里的集合操作因为是同一张表上的逻辑组合用 AND/OR 也能写出来但集合写法更接近集合论语义在跨表场景时远比 AND/OR 组合条件简洁。注意 EXCEPT 和 NOT IN 在遇到 NULL 时行为不同EXCEPT 对 NULL 的处理更符合直觉。5. 避坑指南约束冲突、空值比较与分组查询的五个翻车现场实验三最值钱的部分就是最后那组刻意执行错误语句的验证。每一个报错背后都对应着一类真实业务里会反复出现的问题。我照着这份报告跑 SQL 时把五类最常见的坑重新走了一遍按现象、原因、解决的顺序整理成下面的排查笔记。5.1 主键重复插入了现存学号SQL Server 直接拒绝现象执行INSERT INTO Student VALUES(20100001,李斌,男,20,CS,1001,0)时SQL Server 报错提示违反了 PRIMARY KEY 约束插入被整体回滚Student 表里没有任何变化。原因Sno 是主键20100001 已经存在主键的唯一性约束生效。主键约束是表级完整性里最硬的一条任何重复键值都会被拒之门外。解决插入前先确认学号不存在或者用MERGE做存在则更新、不存在则插入的合并逻辑。实验环境里反复跑 INSERT 脚本时最稳妥的办法是开头先执行一次DELETE FROM Student清空表再按顺序插入。真实项目里则要靠序列或业务规则保证主键来源唯一而不是撞了再处理。5.2 外键约束SC 表插入了不存在的外键值或 NULL现象INSERT INTO SC VALUES(20100001, NULL, 78)时SQL Server 报外键冲突或不允许 NULL 的错误。另一种情况是插入一个 Student 表里不存在的学号同样被拒。原因SC 表的 Cno 是外键同时又是联合主键的一部分NULL 无法通过主键约束指向不存在的课程号也无法通过外键约束。解决往子表插数据之前先用 SELECT 确认父表里有对应的主键值。比如SELECT COUNT(*) FROM Course WHERE Cno 1数量为 0 就说明课程不存在。生产环境里这种校验通常由应用层在写库前完成数据库外键只作为最后一道防线两层的职责不同不能互相替代。5.3 空值判断Score NULL 查不到任何记录现象查询缺少成绩的学生的学号和课程号写成WHERE Score NULL结果集为空改成WHERE Score IS NULL才出数据。原因SQL 的三值逻辑里NULL 不等于任何值包括 NULL 自身。Score NULL的计算结果是 UNKNOWNWHERE 子句只保留结果为 TRUE 的行UNKNOWN 全部被过滤掉。解决判断空值一律用IS NULL/IS NOT NULL。需要把 NULL 当成业务值处理时用COALESCE(Score, 0)把空值替换成 0 再参与运算。顺便说一句聚合函数遇到 NULL 会直接跳过该行AVG(Score)不会因为某行成绩为空就返回错误但统计结果可能和你的预期不一致看聚合结果前先确认列里没有 NULL。5.4 GROUP BY 与 SELECT 列不匹配现象分组统计时 SELECT 里写了非分组列比如SELECT Sno, Cno, COUNT(*) FROM SC GROUP BY CnoSQL Server 报错提示选择列表中的列无效。原因GROUP BY 之后每个分组只剩一行Sno 不在分组键里数据库无法确定该显示哪一个 Sno 的值。这是语法层面就能拦截的错误说明 SQL Server 的语义检查很严。解决SELECT 里要么写分组键要么写聚合函数不能混合普通列。确实需要每个组的明细值时改用窗口函数ROW_NUMBER() OVER (PARTITION BY Cno)既能分组又能带出明细列。窗口函数和 GROUP BY 解决的是两类问题前者不压缩行数后者压缩行数用错了语义就变了。5.5 删除父表记录被外键拦住现象DELETE FROM Student WHERE Sno 20100001失败提示与 SC 表的外键冲突但删除没有选课记录的学生却能成功。原因外键约束保证了引用完整性SC 表里还有该学号的选课记录删除 Student 会破坏 SC 的引用关系。数据库宁可拒绝执行也不让你制造孤儿数据。解决先删子表再删父表顺序不能反。要么在创建外键时声明ON DELETE CASCADE让数据库自动级联删除但生产环境用级联删除要非常谨慎一次误删可能连坐一大片数据。我的习惯是实验环境可以开 CASCADE 图省事生产库一律保持默认的 NO ACTION让应用层去控制删除顺序。排查这类问题有个通用顺序先看完整错误信息里的约束名再去系统视图sys.foreign_keys里查这个约束对应哪两张表最后定位到具体是哪个主键值被引用。这套流程比对着报错瞎猜快得多。6. 进阶把报告改成脚本库视图和索引的复用比想象中值钱拿到一份完整的实验报告最笨的用法是照着敲一遍然后交差。更值钱的用法是把它拆成一个可复用的脚本骨架下次做课程设计接上真实数据就能跑。我的习惯是按职责拆四份文件01_schema.sql 放建表和约束02_data.sql 放插入和初始化数据03_query.sql 放查询案例04_views.sql 放视图和索引。这份报告恰好把这四类内容都覆盖了照着分成四段就能用。视图部分是这个资源里很容易被低估的一块。建视图的核心价值是隐藏复杂连接把高频查询固化成虚拟表CREATE VIEW IS_Student AS SELECT Sno, Sname, Sage FROM Student WHERE Sdept IS; CREATE VIEW S_G(Sno, Gavg) AS SELECT Sno, AVG(Score) FROM SC GROUP BY Sno; CREATE VIEW XK_VIEW AS SELECT Student.Sno, Student.Sname, Student.Sage, Course.Cno, Course.Cname, SC.Score FROM Student, SC, Course WHERE Student.Sno SC.Sno AND SC.Cno Course.Cno;第一个视图把信息系学生这个高频条件固化下来以后SELECT * FROM IS_Student就等价于带 WHERE 条件的单表查询。第二个视图直接输出每个学生的平均成绩省得每次都写 GROUP BY。第三个视图把三表连接的整体打包成了一个宽表看起来很方便但这里要注意原始报告里写的是SELECT Student.*, Course.*, Grade FROM ...执行时会报列名 Sno 被多次指定需要手动去重列名才能建成功。这正好说明视图不是无脑 SELECT * 就行列名冲突在视图定义阶段就会被暴露。索引的进阶应用在于理解它对查询计划的改变。实验里建的索引都属于最基础的形态真实库里还要考虑组合索引的列顺序——(Sno, Cno)和(Cno, Sno)能加速的查询模式完全不同。这份报告没讲到覆盖索引和 INCLUDE 列但理解到索引是给查询器用的路标这一层就已经能解释大部分慢查询了。从那以后我每做一个数据库相关的实验或小项目都会强制走一遍建表脚本化、数据初始化脚本化、查询视图化的流程同一句 SELECT 只要写第二遍就收进视图。这个习惯帮我省下的时间远比当初照着报告敲一遍多。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

Tab Out 快速上手:新手必会的9个关闭标签页技巧,从单关到一键清场

Tab Out 快速上手:新手必会的9个关闭标签页技巧,从单关到一键清场

【免费下载链接】tab-out Keep tabs on your tabs. Turn your "New tabs" page into a mission control, so you can close them easily. Built for people who open too many tabs and never close them. 项目地址: https://gitcode.com/gh_mirrors/ta/…

2026/10/11 22:43:28 阅读更多 →
同包名APK多分区预置:编译能过但运行冲突如何解决?

同包名APK多分区预置:编译能过但运行冲突如何解决?

最近做系统定制,被问得最多的问题之一就是:把相同包名的两个 APK 预置到系统里去,放在不同路径下,编译到底能不能过?第一次被问的时候我还真愣了一下,因为答案真的太让人纠结了——编译大概率能过&#xff…

2026/10/11 22:43:28 阅读更多 →
业务同义词词库自动化维护:基于大模型挖掘用户提问中的方言与行业简称

业务同义词词库自动化维护:基于大模型挖掘用户提问中的方言与行业简称

一线业务人员用自然语言向 ChatBI 提问时,最致命的永远不是复杂的聚合函数,而是“黑话”与“地方话”。 华东的大区经理在群里吼:“看下上个月‘金标生抽’在‘苏宁小店’的‘出货量’”,华北的城市督导敲过来的是:“查…

2026/10/11 22:43:28 阅读更多 →

最新新闻

Q-learning改进版全解析:目标网络、经验回放与Double Q实战

Q-learning改进版全解析:目标网络、经验回放与Double Q实战

简介:这份资源是基于Q-learning改进的强化学习算法实现,开发工具为MATLAB,面向路径规划与人工智能学习者,适合机器人导航、网格寻路、游戏AI等场景下的最优策略求解问题。ZIP压缩包共包含21个文件,以19个.m脚本为核心&…

2026/10/11 23:37:44 阅读更多 →
eladmin代码生成器深度解析:从元数据读取到自定义模板实践

eladmin代码生成器深度解析:从元数据读取到自定义模板实践

eladmin这套后台管理框架,最吸引我的是那个看起来不起眼的代码生成器。很多人把它当黑盒用——在界面上点几下,下载一个 zip,解压、拷贝,前后端代码就齐了。但一旦你想改改生成出来的代码风格,或者想给模板加点自己的东…

2026/10/11 23:37:44 阅读更多 →
二叉树对比面试题100道:概念、遍历与数据结构选型

二叉树对比面试题100道:概念、遍历与数据结构选型

我最近在系统刷算法面试题,发现一个特别明显的现象:二叉树这道菜,几乎每家都在考,但很少直接甩一句“请你求一下二叉树深度”,更多是“递归求深度和层序遍历求深度有什么区别”“堆和二叉搜索树都是二叉树,…

2026/10/11 23:37:44 阅读更多 →
GitHub日榜项目怎么选?从热榜机制到本地AI推理工具评估实战

GitHub日榜项目怎么选?从热榜机制到本地AI推理工具评估实战

1. 日榜项目到底在选什么:从热榜机制说起很多人第一次接触 GitHub 热榜,会以为它是一个"按 star 总数排序"的榜单,其实不是。日榜的核心逻辑是增量,也就是过去 24 小时内新增 star 的速度。一个总 star 数只有几百的新项…

2026/10/11 23:37:43 阅读更多 →
ScreenToGif使用指南:免费开源录屏工具,一站式制作GIF动图

ScreenToGif使用指南:免费开源录屏工具,一站式制作GIF动图

做自媒体、写技术文档、提bug、做教程的朋友,几乎都逃不过一个需求:把屏幕上的一段操作录成 GIF。截图不够直观,录视频又太整,动图恰好卡在中间,既能在文档里内嵌,又能在聊天窗口直接播放。我用过不少工具&…

2026/10/11 23:37:43 阅读更多 →
表格大模型的回溯思考引擎:让预测可追溯、可干预、可审计

表格大模型的回溯思考引擎:让预测可追溯、可干预、可审计

1. 项目概述:这不是又一个“微调大模型”的故事,而是给结构化数据装上“回溯思考引擎”你有没有遇到过这样的场景:某公司用一个训练好的表格大模型预测客户流失概率,结果模型给出0.87的高分预警,但业务负责人盯着屏幕发…

2026/10/11 23:36:42 阅读更多 →

日新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/11 0:00:27 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/11 0:00:27 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 10:45:37 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:53 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:54 阅读更多 →