SQL Server选课库实验全链路:建表约束索引查询与避坑

发布时间:2026/10/31 11:40:28
SQL Server选课库实验全链路:建表约束索引查询与避坑 简介围绕太原理工大学软件工程专业《数据库概论》课程实验整理的 docx 实验报告适合正在学习数据库系统原理、需要完成交互式 SQL 与数据完整性相关实验的学生参考。压缩包共 1 个 docx 文件大小约 1.69MB内容以 Microsoft SQL Server 2016 为实验平台涵盖基本表的创建、修改与删除索引和视图的建立以及插入、更新等数据操作并附有可对照的 SQL 语句示例。资源还涉及主键、外键、唯一约束和聚簇索引等知识点便于读者理解表结构设计、约束设置与查询验证流程。目前已有 311 人学习可用于课程实验报告撰写、SQL 语法复习和上机操作查漏补缺。1. 从一份课程实验报告说起一套选课库的完整 SQL 链路很多做软件工程的团队里新人第一份活往往是照着需求建两张表、写几条查询结果CREATE TABLE一把过加上外键就弹 547 错误查半天不知道是自己插数据的顺序错了。这份数据库课程的实验报告恰好是一条从建库、建表、约束、索引、视图到查询的完整链路覆盖交互式 SQL、数据完整性、数据控制三块内容跑在 SQL Server 上脚本量不大但五脏俱全。它适合三类人正在上数据库课、需要把实验从头跑一遍的学生转后端不久、SQL 只会SELECT *的开发者以及想给团队整理一份 SQL 入门练习素材的人。报告本身是按实验步骤写的散、重复、字段名前后不统一直接抄会翻车所以下面按「能跑起来 → 看懂结构 → 能改」的顺序拆。2. 环境与跑通SQL Server 2016 SSMS 上把脚本跑到出结果2.1 装什么、怎么连原报告指定的平台是 Microsoft SQL Server 2016配套 SSMSSQL Server Management Studio做查询编辑。放到今天装 2016、2017、2019 的 Developer 版都能跑语法层面这份脚本用到的EXCEPT、INTERSECT、MERGE之外的东西在 2005 之后就有了不存在版本门槛。安装时选「数据库引擎服务 客户端工具」身份验证模式建议选混合模式本地练习用 Windows 身份验证也行省得记密码。装完打开 SSMS服务器名这一栏是新手第一个卡点。默认实例填localhost或.Express 版是命名实例得写localhost\SQLEXPRESS如果装了多个实例又忘了名字去服务列表里看SQL Server (实例名)那一行。连上之后左侧对象资源管理器能看到「数据库」节点说明实例通了。建库语句很简单但我一般会显式指定排序规则避免后面中文比较出玄学问题-- 00_init.sql建库并指定中文排序规则 IF DB_ID(TestDB) IS NULL CREATE DATABASE TestDB COLLATE Chinese_PRC_CI_AS; -- CI 不区分大小写AS 区分重音 GO USE TestDB; GODB_ID()返回 NULL 表示库不存在这个判断让脚本可以重复执行不报错GO不是 T-SQL 语句是 SSMS 和 sqlcmd 的批处理分隔符它前面的语句被打包成一个批次发给服务器。参数上Chinese_PRC_CI_AS决定了CS和cs在比较时是否相等也影响ORDER BY对中文的排序结果做课设时如果发现WHERE Sdeptcs也能查到数据就是排序规则在起作用。2.2 把报告里的散装 SQL 拆成 5 个可重复执行的脚本原报告是一段一段贴 SQL 的中间夹着「检查表是否创建成功」这种说明文字直接整段粘进查询窗口会因为中文说明报语法错。可行的做法是按职责切成五个文件用编号控制执行顺序project/ ├── sql/ │ ├── 00_init.sql # 建库、切库 │ ├── 01_schema.sql # 建表、约束、索引、视图 │ ├── 02_seed.sql # 初始化数据注意插入顺序 │ ├── 03_query.sql # 各类查询练习只读 │ └── 99_teardown.sql # 清库方便重来 └── run.sh # 用 sqlcmd 串起来执行run.sh里用sqlcmd批量跑免去在 SSMS 里一个个点执行#!/usr/bin/env bash # -S 服务器实例 -E Windows 身份验证 -b 出错即退出 -i 输入文件 SQLCMDsqlcmd -S localhost -E -b $SQLCMD -i sql/00_init.sql || exit 1 $SQLCMD -d TestDB -i sql/01_schema.sql || exit 1 $SQLCMD -d TestDB -i sql/02_seed.sql || exit 1 $SQLCMD -d TestDB -i sql/03_query.sql || exit 1 echo 全部脚本执行完成-b这个参数值得强调不加它sqlcmd 遇到错误只打印消息、退出码仍然是 0CI 里就会「明明报错却显示成功」加上之后第一条失败就中断|| exit 1再兜一层。-d TestDB指定默认库省得每个文件里都写USE。如果换成 SQL 身份验证把-E换成-U sa -P 密码即可。2.3 验证跑通了没有三条自检 SQL脚本跑完不报错不等于数据对。我习惯执行完立刻跑三条核对表在不在、行数对不对、约束生没生效。-- 自检 1三张核心表的行数正常应该是 7 / 7 / 7 左右 SELECT Student AS 表名, COUNT(*) AS 行数 FROM Student UNION ALL SELECT Course, COUNT(*) FROM Course UNION ALL SELECT SC, COUNT(*) FROM SC; -- 自检 2外键和 CHECK 约束是否都处于启用状态 SELECT name AS 约束名, is_disabled AS 是否禁用 FROM sys.foreign_keys UNION ALL SELECT name, is_disabled FROM sys.check_constraints; -- 自检 3有没有因为插入失败留下的半截数据 SELECT Sno, Sname, Sdept FROM Student WHERE Sdept IS NULL;第一段用UNION ALL把三次COUNT拼成结果集比开三个窗口看快第二段查系统视图is_disabled 1说明约束被NOCHECK关掉了这种情况下数据能随便插但完整性形同虚设很多人翻车就在这第三段用来发现「插入语句被约束拦掉一半」的残留。如果行数对不上基本是02_seed.sql中途报错被-b掐断重跑前先执行99_teardown.sql否则主键冲突会比第一次报得更早。3. 结构与约束拆解三个基本表加四类约束撑起的完整性3.1 表结构与依赖关系整份报告围绕四张表Student学生、Course课程、SC选课、Employee员工只用于练ALTER和DROP。前三张构成经典的选课模型Student和Course是多对多SC作为中间表把两边连起来主键是(Sno, Cno)复合主键两个外键分别指向父表。Course表上还有一个Cpno指向自己的Cno表示先修课这是全篇最容易埋坑的地方。表名主键外键关键约束StudentSno无性别 CHECK、总分 DEFAULT 0CourseCnoCpno → Course.Cno自引用Cname 唯一SC(Sno, Cno)Sno → Student、Cno → Course成绩 CHECK 0~100Employee编号无仅演示 ALTER / DROP3.2 建表语句逐段读原报告的建表语句能跑但有两处可以立刻改好一是姓名用CHAR(8)在非 Unicode 字符集下中文占两个字节8 个字节能存 4 个汉字超出直接报「字符串或二进制数据将被截断」二是约束是匿名的出问题时报错信息里只有系统生成的名字排查很痛苦。我一般写成下面这样-- 01_schema.sql 片段父表先建子表后建 CREATE TABLE Student ( Sno CHAR(8) NOT NULL, Sname NVARCHAR(20) NOT NULL, -- 用 NVARCHAR 存姓名避免字节截断 Ssex CHAR(2) NOT NULL, Sage SMALLINT NULL, Sdept CHAR(20) NULL, Sclass CHAR(4) NOT NULL, Stotal SMALLINT NOT NULL CONSTRAINT DF_Student_Stotal DEFAULT 0, CONSTRAINT PK_Student PRIMARY KEY (Sno), CONSTRAINT CK_Student_Ssex CHECK (Ssex IN (男,女)) ); CREATE TABLE Course ( Cno CHAR(4) NOT NULL, Cname NVARCHAR(40) NOT NULL, Cpno CHAR(4) NULL, Ccredit SMALLINT NULL, CONSTRAINT PK_Course PRIMARY KEY (Cno), CONSTRAINT UQ_Course_Cname UNIQUE (Cname), CONSTRAINT FK_Course_Cpno FOREIGN KEY (Cpno) REFERENCES Course(Cno) );PRIMARY KEY默认会建一个聚集索引意味着这张表的物理存储顺序就按主键排UNIQUE约束在 SQL Server 里同样靠唯一索引实现所以它既保证值不重复也顺手加速了按课程名查询。CHECK里写IN (男,女)插入M会被拒绝但插入NULL不会——这点后面避坑章单独说。DEFAULT 0只在INSERT语句完全没提这一列时生效显式写NULL是会覆盖默认值的。SC表是复合主键加双外键建表顺序必须在两张父表之后CREATE TABLE SC ( Sno CHAR(8) NOT NULL, Cno CHAR(4) NOT NULL, Score SMALLINT NULL, CONSTRAINT PK_SC PRIMARY KEY (Sno, Cno), CONSTRAINT FK_SC_Sno FOREIGN KEY (Sno) REFERENCES Student(Sno), CONSTRAINT FK_SC_Cno FOREIGN KEY (Cno) REFERENCES Course(Cno), CONSTRAINT CK_SC_Score CHECK (Score 0 AND Score 100) );复合主键(Sno, Cno)的直接效果是同一个学生同一门课只能有一条记录重复插入报 2627违反主键Score允许 NULL 是为了表达「选了课但还没出成绩」这一点和CHECK配合时会露出一个语义漏洞后面会讲。外键默认行为是NO ACTION父表被引用的行不能直接删想级联删得显式写ON DELETE CASCADE课设阶段不建议开容易一键清空自己没备份的数据。3.3 索引与视图三个索引里有两个会翻车报告里列了四个索引其中三个是合理的一个在多数情况下会直接报错-- 普通索引加速按课程名模糊/等值查询 CREATE INDEX iCname ON Course(Cname); -- 唯一索引等价于给 Sname 加唯一约束重名会插入失败 CREATE UNIQUE INDEX iSname ON Student(Sname); -- 下面这条在 SC 已有主键聚集索引时会报错 -- Cannot create more than one clustered index on table SC CREATE CLUSTERED INDEX iSnoCno ON SC(Sno, Cno DESC);前两条没问题。第三条的问题在于PK_SC建表时已经默认生成了一个聚集索引一张表只能有一个聚集索引所以这句必然失败。要验证「按Sno升序、Cno降序」的访问路径得改成非聚集索引CREATE NONCLUSTERED INDEX ...或者在建主键时就声明PRIMARY KEY NONCLUSTERED (Sno, Cno)把唯一的聚集索引名额留给别的列。原报告这句能「写完」大概率是没实际执行抄的时候要留意。视图部分报告建了IS_Student信息系学生、S_G每个学生的平均分、XK_VIEW选课全信息三个典型的用途是把多表连接藏起来CREATE VIEW S_G (Sno, Gavg) AS SELECT Sno, AVG(CAST(Score AS DECIMAL(5,2))) FROM SC GROUP BY Sno;CAST不能省。Score是SMALLINTAVG对整数列做聚合时结果还是整数80 和 90 平均出来是 85 没问题但 80 和 85 会得到 82 而不是 82.5做成绩统计时会被这个截断坑到。视图列名后面括号里显式声明(Sno, Gavg)比依赖AVG(...)自动生成的怪名字更可控。3.4 数据完整性验证约束和触发器怎么验报告第三部分专门做完整性检查思路是用「故意违规的语句」去撞约束看报错是否符合预期。这套验证方法很值得留着我一般固化成一张检查清单-- 预期报 2627主键重复 INSERT INTO Student VALUES (20100001,学生甲,男,20,CS,1001,0); -- 预期报 547外键引用了不存在的课程 INSERT INTO SC VALUES (20100001,99,80); -- 预期通过CHECK 对 NULL 不生效 INSERT INTO SC VALUES (20100001,1,NULL); -- 预期报 547被 SC 引用的学生不能直接删 DELETE FROM Student WHERE Sno 20100001;触发器报告里只提了机制没给代码实际场景中最常见的是维护Student.Stotal总分。用行级逐条加减在批量插入时会算错稳妥写法是聚合重算CREATE TRIGGER trg_sc_sync_total ON SC AFTER INSERT, UPDATE, DELETE AS BEGIN SET NOCOUNT ON; -- inserted / deleted 是伪表批量操作时可能有多行必须按集合处理 UPDATE s SET Stotal ISNULL((SELECT SUM(Grade) FROM SC WHERE SC.Sno s.Sno), 0) FROM Student s WHERE s.Sno IN (SELECT Sno FROM inserted UNION SELECT Sno FROM deleted); END;AFTER表示语句成功后才触发inserted存新行、deleted存旧行UPDATE相当于先删后插两张伪表都有数据。SET NOCOUNT ON挡掉「n 行受影响」的干扰消息客户端拿结果集时更干净。注意触发器里的UPDATE不会再递归触发自己默认递归触发器关闭但如果你在同一张表上挂多个触发器执行顺序是不保证的别把业务逻辑拆到两个触发器里。4. 查询模块拆解从单表过滤到集合运算的六层递进4.1 单表查询与结果集整形报告里单表查询占了十几条覆盖列选择、表达式计算、列别名、去重、范围、集合、模糊、空值、多重条件、排序。这些语法本身不难值得说的是两处容易被忽略的行为差异-- DISTINCT 作用于整行不是只作用于 Sno SELECT Sno FROM SC; -- 一个学生选 3 门课就有 3 行 SELECT DISTINCT Sno FROM SC; -- 去重后一人一行 -- 别名两种写法都合法但混用会让结果集列名不可预期 SELECT Sname, YEAR(GETDATE()) - Sage AS 出生年份, 系名 LOWER(Sdept) FROM Student ORDER BY Sdept ASC, Sage DESC;DISTINCT的语义是「对最终结果集的所有列做去重」所以SELECT DISTINCT Sno, Cno跟只选Sno的结果完全不同这也是COUNT(DISTINCT Sno)和COUNT(*)差距的来源。别名部分AS 出生年份和系名 LOWER(Sdept)是两种等价写法返回给客户端的列名一个叫「出生年份」、一个叫「系名」做报表导出时建议统一用AS别混。出生年份这里报告写的是2004 - Sage把年份写死了隔年就错。改成YEAR(GETDATE()) - Sage才是能长期用的写法代价是结果每次运行都在变做自动化断言时得用固定日期替换。4.2 分组统计GROUP BY 与 HAVING 的边界分组这块的关键是分清WHERE、GROUP BY、HAVING的执行顺序WHERE在分组前过滤行HAVING在分组后过滤组聚合函数只能出现在SELECT列表和HAVING里不能出现在WHERE里。SELECT Cno AS 课程号, COUNT(*) AS 选课人数, AVG(CAST(Score AS DECIMAL(5,2))) AS 平均分, MAX(Score) AS 最高分 FROM SC WHERE Score IS NOT NULL -- 先剔除没出成绩的行避免拉低平均分 GROUP BY Cno HAVING AVG(CAST(Score AS DECIMAL(5,2))) 80; -- 再筛掉平均分不达标的课WHERE Score IS NOT NULL这一步很关键AVG会自动忽略 NULL但COUNT(*)不会所以如果既要人数又要平均分NULL 行会让「人数」偏大而「平均分」基于另一批行两个指标口径不一致。要么都排除 NULL要么人数用COUNT(Score)。HAVING里重复写了一遍AVG(...)表达式是因为 SQL Server 不允许在HAVING里引用SELECT列表的别名这点和 MySQL 不同跨库迁移时容易栽。4.3 连接查询从 WHERE 连接写到自连接连接是这份报告里信息量最大的部分从老式的WHERE连接条件到JOIN ... ON再到外连接、自连接、三表连接基本覆盖了日常写 SQL 的全部形态。-- 写法一连接条件放 WHERE老写法等价于内连接 SELECT st.Sno, st.Sname, sc.Cno, sc.Score FROM Student st, SC sc WHERE st.Sno sc.Sno; -- 写法二LEFT JOIN 保留没有选课的学生 SELECT st.Sno, st.Sname, sc.Cno, sc.Score FROM Student st LEFT JOIN SC sc ON st.Sno sc.Sno WHERE sc.Cno IS NULL; -- 只留没选课的反连接写法 -- 写法三自连接找先修课名 SELECT c.Cno AS 课程号, c.Cname AS 课程名, p.Cname AS 先修课名 FROM Course c LEFT JOIN Course p ON c.Cpno p.Cno;写法一和写法二在内连接场景下结果一样但一旦漏写WHERE里的连接条件就退化成笛卡尔积——Student7 行乘SC7 行得 49 行数据量小时看不出来上了生产就是灾难。所以我一般直接写JOIN ... ON把连接条件和过滤条件在语法上分开漏写的概率低很多。写法二里WHERE sc.Cno IS NULL是「反连接」的标准写法用来找「没有匹配上的行」注意条件必须放在WHERE而不是ON放到ON里就变成「左表全保留右表按条件匹配」语义完全不同这个区别是面试高频也是实战高频。写法三是自连接Course表自己连自己别名c和p分别代表「课程」和「先修课」。用LEFT JOIN而不是INNER JOIN是为了让没有先修课的课程Cpno IS NULL也能出现在结果里。4.4 嵌套查询与集合运算ALL、EXISTS 与 EXCEPT 的坑嵌套查询报告列了IN、比较运算符、ALL、EXISTS四类。语法都会写坑在 NULL 和空集上。-- 找出每门课里成绩高于该课平均分的学生 SELECT sc.Sno, sc.Cno, sc.Score FROM SC sc WHERE sc.Score (SELECT AVG(x.Score) FROM SC x WHERE x.Cno sc.Cno); -- EXISTS 只关心「有没有」列清单写 1 即可 SELECT st.Sname FROM Student st WHERE EXISTS (SELECT 1 FROM SC WHERE SC.Sno st.Sno AND SC.Cno 1); -- ALL 遇到子查询里的 NULL 会整体返回空集 SELECT Sname, Sage FROM Student WHERE Sage ALL (SELECT Sage FROM Student WHERE Sdept CS AND Sage IS NOT NULL) AND Sdept CS;第一段是相关子查询外层每行都把sc.Cno传进去算一次平均分逻辑直白但效率一般数据量大时改成JOIN一个分组子查询会快不少。第二段的EXISTS语义是「子查询有没有返回行」SELECT 1和SELECT *性能上没有区别优化器会忽略列清单但写1意图更清楚。第三段的ALL是重点Sage ALL (子查询)要求「比子查询结果里的每一个值都小」只要子查询结果里混进一个 NULL比较结果就变成UNKNOWN整个WHERE过滤掉所有行、返回空集而且不报错。这种「静默返回空」是最难查的一类问题所以我在子查询里强制加了Sage IS NOT NULL。集合运算部分UNION、INTERSECT、EXCEPT在 SQL Server 里都支持注意它们默认去重要保留重复得写UNION ALL-- 计算机系学生 并上 年龄不超过 19 岁的学生 SELECT * FROM Student WHERE Sdept CS UNION SELECT * FROM Student WHERE Sage 19; -- 计算机系 减去 年龄不超过 19 岁的剩下的是 CS 系里 19 岁以上的 SELECT * FROM Student WHERE Sdept CS EXCEPT SELECT * FROM Student WHERE Sage 19;EXCEPT的结果里不会出现重复行且参与运算的两边列数、类型必须一一对应列名以第一个SELECT为准。用SELECT *做集合运算风险很大一旦表结构变了加一列两边的列就对不上脚本直接报错练习时用*无所谓写进正式脚本建议把列名写全。5. 避坑与排查六个让脚本跑不动的现场记录5.1 现象建第二个聚集索引报 1913执行CREATE CLUSTERED INDEX iSnoCno ON SC(...)报「表 SC 上已存在聚集索引」。原因是建表时PRIMARY KEY (Sno, Cno)默认就建了聚集索引一张表只能有一个。解决办法有两个把这条改成NONCLUSTERED或者在PRIMARY KEY后面加NONCLUSTERED关键字把聚集索引的位置让出来。判断当前表上有没有聚集索引查sys.indexes WHERE type 1最快。5.2 现象插入课程数据报 547 外键冲突按报告顺序先插Cno1Cpno5、再插5Cpno7如果建表时就带了自引用外键第一条就挂。原因是自引用外键要求被引用的课程必须已经存在。解决办法是按依赖层级分批插先插Cpno IS NULL的高数、数据处理、C语言再插依赖它们的数据结构 → 数据库 → 管理信息系统最后插依赖数据处理的操作系统。或者先全部插 NULL最后用一条UPDATE补Cpno。5.3 现象CHECK 约束拦不住空成绩CK_SC_Score CHECK (Score 0 AND Score 100)看着很严但INSERT INTO SC VALUES (20100001,1,NULL)能成功。原因是 SQL 的三值逻辑里NULL 0结果是UNKNOWNCHECK只在结果为FALSE时拒绝UNKNOWN视为通过。想让成绩必填得把列声明成NOT NULL或者在CHECK里显式写Score IS NOT NULL AND Score BETWEEN 0 AND 100。这个行为在几乎所有关系库里一致不是 SQL Server 的怪癖。5.4 现象照着报告抄查询报「列名 Grade 无效」报告前后两部分的字段名不一致建表时叫Score查询里大量写成Grade跨实验抄的时候必然报 207。原因是两份实验脚本各自独立写的没有统一命名。解决办法是写脚本前先定一份字段命名表或者干脆在建表后加同义词兜底CREATE SYNONYM不推荐掩盖问题。更稳的做法是把所有列名集中在一个常量文件里查询脚本引用同一套命名。5.5 现象删除语句执行成功但影响 0 行DELETE FROM Student WHERE Sno 201000022提示「0 行受影响」而表里明明有个20100022。原因是Sno定义成CHAR(8)传进去的字符串是 9 位类型转换时被截成 8 位后与目标值不相等。同类问题还有CHAR类型比较时尾部空格被忽略、VARCHAR与NVARCHAR隐式转换导致索引失效。排查手段是先SELECT * FROM Student WHERE Sno LIKE %2010002%看实际存的值再核对长度。5.6 现象视图创建语句报「引号未闭合」报告里CREATE VIEW IS_Student AS SELECT ... WHERE SdeptIS少了右引号和右括号复制到 SSMS 直接语法错。这类问题在 PDF 转文本、跨文档复制时特别常见单引号、括号、分号都容易丢。解决办法是先格式化再执行SSMS 里CtrlK, CtrlD或者用脚本做一次括号配对检查。我现在拿到任何来路不明的 SQL第一件事是全选格式化格式化过不了的基本都有隐藏字符。6. 二次开发把散装实验脚本改成可复用的练习工程这份报告最大的价值不是那几十条 SQL而是它把「建表 → 约束 → 造数 → 查询 → 验证」串成了一条闭环改造方向就是把这个闭环工程化让它能重复跑、能自动验、能换库跑。第一步是目录分层。把00_init到99_teardown这套脚本固定下来再补一个04_assert.sql专门做断言每个查询后面跟一条「期望行数 / 期望值」的校验比如SELECT CASE WHEN COUNT(*) 7 THEN PASS ELSE FAIL END FROM Student;把结果收集起来任何一条 FAIL 就退出非零这样脚本改坏了立刻能发现。第二步是把种子数据从INSERT硬编码换成MERGE让脚本可以重复执行而不撞主键MERGE INTO Student AS t USING (VALUES (20100001, N学生甲, 男, 20, CS, 1001)) AS s(Sno, Sname, Ssex, Sage, Sdept, Sclass) ON t.Sno s.Sno WHEN MATCHED THEN UPDATE SET Sname s.Sname, Sage s.Sage WHEN NOT MATCHED THEN INSERT (Sno, Sname, Ssex, Sage, Sdept, Sclass) VALUES (s.Sno, s.Sname, s.Ssex, s.Sage, s.Sdept, s.Sclass);第三步是方言适配。这套脚本原样搬到 MySQL 或 PostgreSQL 会挂一片我在团队里维护过一张对照表改之前先对照一遍能省掉一半时间场景SQL ServerMySQL 8PostgreSQL取前 N 行SELECT TOP 5LIMIT 5LIMIT 5当前时间GETDATE()NOW()NOW()空值替换ISNULL(x, 0)IFNULL(x, 0)COALESCE(x, 0)字符串拼接CONCAT()||自增列IDENTITY(1,1)AUTO_INCREMENTGENERATED ... AS IDENTITY集合差EXCEPTEXCEPT8.0.31EXCEPT最后一件事是把它接到 CI 里。SQL Server 有 Linux 版镜像用容器起一个实例sqlcmd跑00 → 04断言全 PASS 就算过。这样每次改表结构至少能保证「建表、造数、核心查询」这条链路没断而不是等上线前手工点一遍。血泪经验是早些年我图省事直接在生产库上跑ALTER TABLE ... ADD CONSTRAINT补外键结果历史脏数据把语句卡住锁表十几分钟。从那以后我每次动表结构都强制走一遍「备份 → 在副本上跑 → 跑断言 → 再上」哪怕只是一条加索引的语句。这份实验报告里的ALTER TABLE Student ALTER COLUMN Sage SMALLINT就是典型例子——原列是INT改小类型前如果不检查最大值超过 32767 的数据会直接报算术溢出。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询