首页/新闻资讯/正文详情

学生成绩管理系统数据库大作业:ER图到SQL统计查询实战

发布时间:2026/11/15 2:36:40 来源:云帆数科 栏目:资讯中心
学生成绩管理系统数据库大作业:ER图到SQL统计查询实战
简介这份北邮研一数据库大作业文档面向高校数据库课程学习者与课程设计备赛者围绕学生成绩管理系统的需求分析、概念建模和关系数据库实现展开适合需要参考完整设计流程、实验报告结构及SQL建表思路的本科生和研一学生。压缩包内含1个docx文件约503KB以章节化文档形式组织需求分析、数据库设计、ER图设计、逻辑结构设计和程序设计等内容说明Course、Student、Sc、Teacher四张表的字段定义、主外键约束、组合主键及一对多联系并覆盖成绩维护、教师信息管理、不及格学生名单统计、无教学任务教师查询等功能。文档还整理局部与全局ER图、数据字典、函数依赖和建库建表语句示例便于读者对照梳理从业务需求到关系模式的推导过程也可用于答辩准备和查漏补缺。已有607人学习可作为课程大作业的结构化参考。1. 一份数据库大作业能跑不算完从 ER 图到四条统计需求评审课程设计时最常见的场面是INSERT 能跑、SELECT 能出数老师随口问一句“查一下没有教学任务的老师”屏幕就卡住了。这份数据库大作业资源围绕学生成绩管理系统展开从需求分析、ER 图、四张表course / student / sc / teacher一路做到视图、存储过程、触发器和统计查询附带可执行的 SQL 脚本MySQL 5.5 加 Navicat 就能跑起来。它适合正在赶课程设计、又不想只交一份“能跑就行”作业的研一同学也适合 SQL 写得零散、想系统补一遍关系模型的人。下面按我拆包复现的顺序讲表怎么定、范式怎么验、三个数据库对象怎么写、统计查询怎么不出错最后是几个真会翻车的地方。2. 需求到表结构四张表的主键、外键与字段长度怎么定2.1 把四条业务句子翻译成三个实体加一个联系原始需求文档里列了四件事授课教师录入成绩、学生自查成绩、教务处统计不及格名单、教务处查没有教学任务的教师。把这四句话拆开实体只有三个——学生、课程、教师成绩不是实体而是学生和课程之间的多对多联系附带一个属性 degree。这个判断决定了后面所有设计如果把成绩做成独立实体、给它一个自增 id 主键那么“一个学生一门课只能有一条成绩”这条业务约束就丢了重复录入拦不住统计平均分时还得先 DISTINCT。我的做法是把 sc 的主键定为 (sno, cno) 复合主键。复合主键在这里不只是为了少一个字段它本身就是约束同一学生对同一课程插第二条记录数据库直接报主键冲突比在应用层写判断可靠得多。教师和课程之间是一对多一个教师可以讲多门课所以把 teacher.tno 放到 course 表里做外键而不是反过来在 teacher 表里存课程列表——后者违反第一范式也没法建索引。课程表里那个 tno 字段是整份设计里最容易被追问的地方。原始文档的数据需求一节把它写成“选课人数”逻辑设计一节又写成“教师号”两处打架。我的处理是tno 一律解释为任课教师号选课人数属于派生数据用视图现算不落表。理由在 3.2 节展开。2.2 字段类型与长度char 还是 varchar以及一个必须对齐的细节主键统一用 CHAR 定长学号 CHAR(9)、课程号 CHAR(5)、教师号 CHAR(3)。定长主键在 InnoDB 的聚簇索引里占的空间可预期等值比较也快学号这种长度固定的编号没有理由用 VARCHAR。姓名、专业、班级这些长度不一的用 VARCHAR别为了“整齐”全设成 CHAR(20)utf8mb4 下 CHAR 会按最大长度预留白占页空间。原始文档里有一处硬伤student.sno 定义成 CHAR(9)sc.sno 定义成 CHAR(10)。这两个字段要建外键长度和字符集必须完全一致否则 CREATE TABLE 阶段直接报 1215 或 1005运气差一点能建上但连接时发生隐式转换索引失效、查询变慢排查起来很费时间。我复现时把两边统一成 CHAR(9)。成绩字段用 DECIMAL(4,1)总四位、一位小数取值范围到 999.9配合 CHECK 限制在 0 到 100。这里有个版本相关的坑要提前说MySQL 5.5 会解析 CHECK 子句但不执行写上去只是个注释真正拦住非法分数得靠触发器或在应用层校验CHECK 真正生效要到 8.0.16 之后。原始文档的运行环境写的是 MySQL 5.5所以别指望 CHECK 兜底。还有一个语义细节值得单独讲degree 允许为 NULL表示“选了课但还没出分”这和考 0 分是两回事。这个区别会直接影响统计——AVG(degree) 会跳过 NULLCOUNT(degree) 和 COUNT(*) 结果不同。如果业务上不分“未录入”和“零分”那就把字段设成 NOT NULL DEFAULT 0别留这种模糊地带。2.3 建表脚本顺序、外键动作与字符集建表顺序有讲究被引用的表先建。teacher 和 student 要在 course、sc 之前course 又要在 sc 之前。字符集在建库时就定死别等到插入中文才发现乱码。-- 显式指定字符集避免中文姓名、专业出现问号 CREATE DATABASE score_mgmt DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE score_mgmt; -- 教师表被引用方先建 CREATE TABLE teacher ( tno CHAR(3) NOT NULL COMMENT 教师编号, tname VARCHAR(8) DEFAULT NULL COMMENT 教师姓名, tsex CHAR(2) DEFAULT NULL COMMENT 男/女, tdept VARCHAR(16) DEFAULT NULL COMMENT 所属院系, PRIMARY KEY (tno) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 学生表学号长度 9和后面 sc 表严格对齐 CREATE TABLE student ( sno CHAR(9) NOT NULL COMMENT 学号, sname VARCHAR(8) DEFAULT NULL, ssex CHAR(2) DEFAULT NULL COMMENT 男/女, smajor VARCHAR(20) DEFAULT NULL, sclass VARCHAR(10) DEFAULT NULL, PRIMARY KEY (sno) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 课程表tno 是任课教师教师被删时课程保留、任课置空 CREATE TABLE course ( cno CHAR(5) NOT NULL COMMENT 课程号, cname VARCHAR(20) NOT NULL COMMENT 课程名, tno CHAR(3) DEFAULT NULL COMMENT 任课教师号, PRIMARY KEY (cno), CONSTRAINT fk_course_teacher FOREIGN KEY (tno) REFERENCES teacher(tno) ON UPDATE CASCADE ON DELETE SET NULL ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 成绩表复合主键天然防重复录入 CREATE TABLE sc ( sno CHAR(9) NOT NULL, cno CHAR(5) NOT NULL, degree DECIMAL(4,1) DEFAULT NULL COMMENT NULL 表示未出分, PRIMARY KEY (sno, cno), CONSTRAINT fk_sc_student FOREIGN KEY (sno) REFERENCES student(sno) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT fk_sc_course FOREIGN KEY (cno) REFERENCES course(cno) ON DELETE RESTRICT ON UPDATE CASCADE, CONSTRAINT ck_sc_degree CHECK (degree 0 AND degree 100) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;外键动作的选择反映的是业务判断不是随手写。学生删除时 sc 跟着 CASCADE因为人走了成绩没有留存意义课程删除我用 RESTRICT避免误删一门课把整届成绩带走真要删先手动清 sc。course.tno 用 SET NULL教师离职课程还在只是暂时没有任课人这正好对应“查没有教学任务的教师”那条需求的反向场景。如果库里已经有脏数据加外键会失败。先跑一遍孤儿记录排查再建约束-- 有输出说明存在对不上号的成绩记录先清理再加外键 SELECT s.sno, s.cno FROM sc s LEFT JOIN student st ON st.sno s.sno LEFT JOIN course c ON c.cno s.cno WHERE st.sno IS NULL OR c.cno IS NULL;2.4 造测试数据两百条记录才看得出索引有没有用三五十条数据看不出执行计划的差别评审时也不好演示分页和统计。我一般用存储过程批量铺数据命名规则统一后面排查时一眼能认出来。DELIMITER // CREATE PROCEDURE seed_student(IN n INT) BEGIN DECLARE i INT DEFAULT 1; WHILE i n DO INSERT INTO student(sno, sname, ssex, smajor, sclass) VALUES (CONCAT(2021, LPAD(i, 5, 0)), CONCAT(学生, i), IF(i % 2 0, 男, 女), 计算机科学与技术, 计科2101); SET i i 1; END WHILE; END // DELIMITER ; CALL seed_student(200);n 是要生成的学生数循环里 LPAD(i,5,0) 把序号补成五位拼上前缀正好九位学号。成绩用笛卡尔积加随机数一次灌进去-- 每名学生给三门课各生成一条 50 到 100 之间的成绩 INSERT INTO sc(sno, cno, degree) SELECT s.sno, c.cno, ROUND(50 RAND() * 50, 1) FROM student s CROSS JOIN course c WHERE c.cno IN (C01, C02, C03);CROSS JOIN 在这里是笛卡尔积200 名学生乘 3 门课得到 600 条成绩WHERE 负责筛出目标课程。ROUND(...,1) 保留一位小数和 DECIMAL(4,1) 对齐否则插入时会被静默截断后面核对总数会对不上。3. 逻辑结构设计函数依赖、范式校验与 course 表里的语义冲突3.1 函数依赖从 ER 图里抽不是从字段表里猜四个关系模式的函数依赖集写出来是这样关系模式函数依赖候选码studentsno → sname, ssex, smajor, sclasssnocoursecno → cname, tnocnoteachertno → tname, tsex, tdepttnosc(sno, cno) → degree(sno, cno)判断第二范式看有没有非主属性只依赖组合码的一部分。sc 里唯一的非主属性是 degree它必须同时知道学生和课程才能确定完全依赖满足 2NF。判断第三范式看有没有非主属性通过另一个非主属性传递依赖候选码。course 里 cno 决定 tno但 tno 不再决定 cname链条断了满足 3NF。反例比正例更能说明问题。假设偷懒把 course 写成 (cno, cname, tno, tname, tdept)那么 cno → tno → tname、tdept 就构成传递依赖。后果是同一个教师讲三门课姓名和院系要存三份教师换院系时三处都得改漏一处就出现同一教师两个院系的数据不一致。这就是要把 teacher 拆出去的全部理由答辩时能这么讲比背“第三范式消除了传递依赖”有说服力得多。3.2 一个字段两种解释选课人数为什么不落表回到 course.tno 的语义冲突。如果真按“选课人数”来用它会立刻遇到更新异常学生退课要减一加课要加一任何一条 sc 记录变动都得同步维护这个计数器中途程序崩一次数据就永久对不上。更麻烦的是它没法自证——没人知道那个 3 到底是不是真实人数。派生数据用视图算永远和明细一致CREATE OR REPLACE VIEW v_course_stat AS SELECT c.cno, c.cname, c.tno, t.tname, COUNT(sc.sno) AS stu_cnt, ROUND(AVG(sc.degree), 1) AS avg_degree FROM course c LEFT JOIN teacher t ON t.tno c.tno LEFT JOIN sc ON sc.cno c.cno GROUP BY c.cno, c.cname, c.tno, t.tname;两个 LEFT JOIN 是关键用 INNER JOIN 的话没人选的课直接消失统计报表里就少一行教务处对账时会被问住。COUNT(sc.sno) 而不是 COUNT(*)因为左连接产生的 NULL 行不该计入人数这也是 NULL 语义在这里的第二个用处。avg_degree 同理AVG 自动跳过没出分的记录算出来的是“已出分学生的平均分”这个口径要在文档里写清楚别让人误以为是全班平均。3.3 数据字典写成表别写成散文数据字典的作用是让人不看建表语句也能理解结构所以必须表格化。我习惯把四张表合成一张按表名分组表字段类型允许 NULL键说明studentsnoCHAR(9)否主键学号studentsnameVARCHAR(8)是—姓名studentssexCHAR(2)是—男/女studentsmajorVARCHAR(20)是—专业studentsclassVARCHAR(10)是—班级coursecnoCHAR(5)否主键课程号coursecnameVARCHAR(20)否—课程名coursetnoCHAR(3)是外键任课教师号scsnoCHAR(9)否组合主键、外键学号sccnoCHAR(5)否组合主键、外键课程号scdegreeDECIMAL(4,1)是—0-100NULL 为未出分teachertnoCHAR(3)否主键教师号teachertnameVARCHAR(8)是—姓名teachertsexCHAR(2)是—男/女teachertdeptVARCHAR(16)是—所属院系注意原始文档里 teacher 表的 tdept 在数据需求一节叫“教师地址”在实体抽象一节叫“系别”。这种一名多义在答辩时会被追问统一成“所属院系”并在文档里只保留一种叫法。3.4 ER 图到关系模式的三条转换规则以及怎么验证转对了一对一、一对多、多对多的转换规则各一条一对一可以把任一方主键放到另一方做外键一对多把“一”端主键放到“多”端多对多必须单独建联系表主键取两端主键的组合。这份设计里 course.tno 走的是第二条sc 走的是第三条没有一对一关系。转完之后不要靠肉眼检查直接查 information_schema 对一遍-- 核对字段类型和可空性和设计文档逐行对 SELECT TABLE_NAME, COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, COLUMN_KEY FROM information_schema.COLUMNS WHERE TABLE_SCHEMA score_mgmt ORDER BY TABLE_NAME, ORDINAL_POSITION; -- 核对三个外键是否真的建上了 SELECT CONSTRAINT_NAME, TABLE_NAME, COLUMN_NAME, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME FROM information_schema.KEY_COLUMN_USAGE WHERE TABLE_SCHEMA score_mgmt AND REFERENCED_TABLE_NAME IS NOT NULL;第二条查询如果只返回两行说明 course.tno 的外键漏了——这是很常见的情况因为建表时把外键写在后面单独 ALTER中途报错就容易漏。设计文档里写的约束和库里实际存在的约束不一致是评审最容易挑出来的问题之一。4. 视图、存储过程、触发器三个加分项的正确打开方式4.1 视图 v_student三表连接固化成查询入口需求里“查询选修某门课的学生的学号、姓名、成绩”每次都写三表连接太啰嗦视图就是干这个的。CREATE OR REPLACE VIEW v_student AS SELECT s.sno, s.sname, s.sclass, c.cname, sc.degree FROM student s JOIN sc ON sc.sno s.sno JOIN course c ON c.cno sc.cno; -- 之后所有按课程名的查询都走视图 SELECT * FROM v_student WHERE cname 数据库原理 AND degree 60;视图在 MySQL 里默认走 MERGE 算法查询时会被展开成等价的三表连接不会先物化出临时表所以性能上基本没有额外代价。只有视图里出现 GROUP BY、聚合函数、UNION 这些结构时才会退化成 TEMPTABLE 算法先算完再查这时候索引就用不上了。上面这个视图是纯连接属于 MERGE可以放心用。视图能不能更新是另一个必须搞清楚的边界。单表、不含 DISTINCT、不含聚合、不含子查询的视图通常可更新一旦涉及多表 JOIN往视图里 INSERT 大概率报 1393 或 1471。原始文档里那个 view_sc 就是单表视图所以能插进去CREATE OR REPLACE VIEW view_sc AS SELECT sno, cno, degree FROM sc WHERE degree IS NOT NULL WITH CHECK OPTION;WITH CHECK OPTION 的作用是拦住“插进去之后查不出来”的记录往这个视图插一条 degree 为 NULL 的数据会被拒绝因为不满足 WHERE 条件。没有这个子句的话插入成功但视图里看不见初学者会以为数据丢了。4.2 存储过程IN/OUT 参数与 DELIMITER 的必要性存储过程体内部有分号而客户端默认拿分号当语句结束符不改 DELIMITER 的话CREATE PROCEDURE 会在第一个分号处被截断报语法错误。这不是玄学是 mysql 客户端的解析行为Navicat 的查询窗口也一样。DELIMITER // -- 入参班级名用等值匹配别用 LIKE %...% CREATE PROCEDURE proc_class_students(IN p_class VARCHAR(10)) READS SQL DATA BEGIN SELECT sno, sname, smajor FROM student WHERE sclass p_class ORDER BY sno; END // -- 入参学号出参该生已出分的课程门数 CREATE PROCEDURE num_sc(IN p_sno CHAR(9), OUT p_cnt INT) READS SQL DATA BEGIN SELECT COUNT(*) INTO p_cnt FROM sc WHERE sno p_sno; END // DELIMITER ; CALL proc_class_students(计科2101); CALL num_sc(202100001, cnt); SELECT cnt AS course_cnt;原始文档里写的是 WHERE sclass LIKE %4412%两个问题前导通配符让 B 树索引彻底失效几千行数据就是全表扫语义上也不严谨‘4412’ 会匹配到 ‘44120’班级号一旦出现包含关系就误伤。能等值匹配就别用 LIKE真要模糊查把通配符放右边让最左前缀生效。OUT 参数在调用时要传用户变量 开头call 完再 SELECT 出来。INOUT 用得少一般是计数器累加才用。存储过程建完可以用 SHOW PROCEDURE STATUS 确认注意它有库级权限要求换个账号连可能看不到。4.3 触发器删除学生时把学号姓名写进审计表需求里没写审计但加上去答辩会加分成本也就十几行。先建一张结构相同的空表接数据再挂 AFTER DELETE 触发器。CREATE TABLE delstudent ( sno CHAR(9) NOT NULL, sname VARCHAR(8) DEFAULT NULL, del_time DATETIME DEFAULT CURRENT_TIMESTAMP ); DELIMITER // CREATE TRIGGER trg_student_after_delete AFTER DELETE ON student FOR EACH ROW BEGIN INSERT INTO delstudent(sno, sname) VALUES (OLD.sno, OLD.sname); END // DELIMITER ; -- 验证删一个学生看审计表有没有记录 DELETE FROM student WHERE sno 202100199; SELECT * FROM delstudent WHERE sno 202100199;OLD 指被删除前的行NEW 指插入或更新后的行DELETE 触发器里没有 NEW。选 AFTER 而不是 BEFORE是因为审计要的是“已经删掉了”这个事实如果选 BEFORE触发器里抛异常还能拦下删除那是另一种用法。这里有个隐藏行为必须知道student 上挂着 ON DELETE CASCADE 到 sc但级联删除不会激活 sc 表上的触发器。MySQL 文档里写得很清楚级联的外键动作不触发 trigger。也就是说如果你在 sc 上也写了删除审计指望删学生时自动记录那条记录永远不会出现。要做这种跨表审计只能在 student 的触发器里显式删 sc代价是放弃了级联的简洁二选一。5. 统计类查询怎么写不及格名单、无教学任务教师与索引命中5.1 不及格名单JOIN 写法与 IN 子查询的版本差异需求原文是“查询 C03 课程不及格的学生信息”。最直观的写法是先查 sc 再回表SELECT s.sno, s.sname, s.sclass, sc.degree FROM sc JOIN student s ON s.sno sc.sno WHERE sc.cno C03 AND sc.degree 60 ORDER BY sc.degree;等价的 IN 子查询写法也能出结果SELECT sno, sname FROM student WHERE sno IN (SELECT sno FROM sc WHERE cno C03 AND degree 60);两种写法在 MySQL 5.5 上性能差别很明显。5.6 之前没有半连接优化IN 子查询往往被改写成对外层每一行执行一次子查询数据量一大就慢得离谱5.6 之后优化器会把 IN 转成半连接两者执行计划就接近了。原始文档写的是 MySQL 5.5所以我统一用 JOIN 写法。这不是风格问题是版本决定的最优解。如果只要人数不要明细直接聚合别把明细拉回来再数SELECT cno, COUNT(*) AS fail_cnt, ROUND(AVG(degree),1) AS avg_fail FROM sc WHERE cno C03 AND degree 60 GROUP BY cno;注意这里的 avg_fail 是“不及格学生内部的平均分”和全课平均分不是一个口径。报表上两个数并排放一定要标清楚否则教务处会以为是算错了。5.2 没有教学任务的教师NOT IN 的 NULL 陷阱这是整份作业里最值得单独讲的一条查询。原始需求是“查询没有教学任务的老师名单”很多人第一反应是 NOT IN-- 错误写法只要 course.tno 里存在一个 NULL整条查询返回空 SELECT tno, tname FROM teacher WHERE tno NOT IN (SELECT tno FROM course);问题出在 SQL 的三值逻辑。NOT IN 等价于对每一行做 tno 子查询里每个值 的比较一旦子查询结果里含 NULL比较结果就是 UNKNOWN 而不是 TRUE整个条件永远不成立。而 course.tno 恰恰允许 NULL我们前面设了 ON DELETE SET NULL只要有一门课的任课教师为空这条查询就返回零行——不是报错是静默返回空集最难发现。两种稳妥写法-- 写法一NOT EXISTS语义最清晰NULL 不影响 SELECT t.tno, t.tname FROM teacher t WHERE NOT EXISTS (SELECT 1 FROM course c WHERE c.tno t.tno); -- 写法二LEFT JOIN 找空便于同时带出其他字段 SELECT t.tno, t.tname, t.tdept FROM teacher t LEFT JOIN course c ON c.tno t.tno WHERE c.cno IS NULL;写法二里判断的是 c.cno IS NULL 而不是 c.tno IS NULL因为 c.cno 是 course 的主键不可能为 NULL用它做“没匹配上”的标记才可靠。如果写成 c.tno IS NULL会把“有这门课但任课教师为空”的教师也误判成没有教学任务。反过来“统计各科不及格学生名单并按课程分组”要用左连接保证没人不及格的课程也出现在结果里否则名单看起来像漏了几门课SELECT c.cno, c.cname, COUNT(sc.sno) AS fail_cnt FROM course c LEFT JOIN sc ON sc.cno c.cno AND sc.degree 60 GROUP BY c.cno, c.cname ORDER BY fail_cnt DESC;注意 degree 60 写在 ON 里而不是 WHERE 里。写进 WHERE 的话左连接产生的 NULL 行会被过滤掉退化成内连接没人不及格的课程直接消失。5.3 索引什么时候该加加了为什么没走sc 表的主键是 (sno, cno)InnoDB 里这是个复合聚簇索引能高效支撑“按学号查成绩”但“按课程号查”用不上——复合索引只支持最左前缀。而统计类需求几乎全是按 cno 过滤所以必须补一个-- 课程号在前、分数在后同时服务等值过滤和范围过滤 CREATE INDEX idx_sc_cno_degree ON sc(cno, degree); -- 按班级查学生也用得上 CREATE INDEX idx_student_class ON student(sclass);为什么是 (cno, degree) 而不是 (degree, cno)查询条件是 cno 等值、degree 范围等值列必须放在范围列左边否则范围一出现后面的列就没法用于索引定位了。加完索引用 EXPLAIN 验证别凭感觉EXPLAIN SELECT s.sno, s.sname FROM sc JOIN student s ON s.sno sc.sno WHERE sc.cno C03 AND sc.degree 60;结果里重点看四列列该看到什么看到什么说明有问题typeref 或 rangeALL 表示全表扫keyidx_sc_cno_degreeNULL 表示索引没被选中rows明显小于总行数接近总行数说明选择度差ExtraUsing index conditionUsing filesort、Using temporary 要留意数据量只有几百行时优化器可能算出来全表扫更便宜key 显示 NULL 是正常的不代表索引白建。这时候可以临时关掉索引看看差别或者干脆灌到几万行再测。评审时拿一份有对比数据的执行计划比空口说“我加了索引”有用得多。6. 交付前排查五个必查的翻车点与外键级联的隐藏行为下面五条是我实际复现时真撞上的按现象、原因、解决记下来。外键建不上报 1215 或 1005。原因是 sc.sno 定义成 CHAR(10) 而 student.sno 是 CHAR(9)类型或长度不一致时 InnoDB 拒绝创建外键。解决是把两边的类型、长度、字符集、排序规则全部对齐改完先跑孤儿记录排查再重建约束。CHECK 约束写了却不生效插入了 120 分的成绩也没被拦。原因是 MySQL 5.5 解析 CHECK 但直接忽略这个特性到 8.0.16 才真正执行。解决办法是用 BEFORE INSERT / BEFORE UPDATE 触发器校验分数区间或者把校验放到应用层别在文档里写“已由 CHECK 保证”。中文姓名显示成问号。原因多半是建库时没指定字符集或者连接串的 character_set_client 和表不一致写入时就已经损坏了改表字符集也救不回来。解决办法是建库就定 utf8mb4连接层统一 SET NAMES utf8mb4。删了学生sc 里对应的成绩没了但审计表没记录。原因是级联外键动作不激活触发器sc 上的 DELETE 触发器根本不会执行。解决是在 student 的 AFTER DELETE 里显式删 sc 并写审计或者接受“审计只覆盖手动删除”这个范围并在文档里说明。视图能查不能插报 1471。原因是视图里带了三表 JOIN不满足可更新视图的条件。解决办法是插入走基表视图只承担查询职责确实需要写入口就单独建一个单表视图加 WITH CHECK OPTION。交之前我会把下面这段跑一遍三分钟能省掉大半返工-- 行数对账任何一张表为 0 都说明数据没进去 SELECT student AS t, COUNT(*) AS cnt FROM student UNION ALL SELECT course, COUNT(*) FROM course UNION ALL SELECT sc, COUNT(*) FROM sc UNION ALL SELECT teacher,COUNT(*) FROM teacher; -- 孤儿记录与越界分数两个数都必须是 0 SELECT COUNT(*) AS orphan FROM sc s LEFT JOIN student st ON st.sno s.sno LEFT JOIN course c ON c.cno s.cno WHERE st.sno IS NULL OR c.cno IS NULL; SELECT COUNT(*) AS bad FROM sc WHERE degree 0 OR degree 100;最后说个习惯。早些年我做完课设只测“正常路径”演示时数据漂亮一到答辩老师随手加个边界条件就露馅——那次被问“没有任课教师的课怎么统计”我那个 NOT IN 返回空集的查询当场卡住回来后把三值逻辑翻来覆去看了一晚上。从那以后我每次把 SQL 脚本交出去之前都会先把上面三段自检跑一遍再打开视图核对统计口径确认 NULL 的处理和我嘴上说的一致。希望帮到你。本文还有配套的精品资源点击获取

相关推荐

SAP BTP Subaccount区域选择与迁移实战指南
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

学生信息管理系统源码解析:从跑通到二次开发实战
学生信息管理系统源码解析:从跑通到二次开发实战

简介:这份资源是面向学校教务人员、教师及编程初学者的学生信息管理小程序配套资料,围绕学生信息的录入、查找、删除、修改、排序、人数统计与全量显示等日常管理需求展开,帮助使用者快速上手一套可运行、可参考的轻量级管理工具。压缩包共4个… · 2026/11/15 0:28:11

特种工延期复核怎么报名?过期证书怎么处理别再踩坑
特种工延期复核怎么报名?过期证书怎么处理别再踩坑

特种工延期复核怎么报名?过期证书怎么处理别再踩坑 证书过期了不知道怎么复审延期,是不是你最近正在经历的糟心事?特种作业操作证是从事相关工作的“通行证”,一旦过期,轻则影响上岗,重则可能引发安全事故。现在就来聊聊【特种工延期复核】怎么报名,以… · 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: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驱动调试
双极步进电机控制实战: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 步进电机驱动实战解析
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工业级双极步进电机控制方案
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

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码