news 2026/10/27 3:01:32

数据库系统原理实验:SQL数据定义、查询与完整性权限知识地图

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
数据库系统原理实验:SQL数据定义、查询与完整性权限知识地图

简介:这份资源是太原理工大学软件工程专业《数据库系统原理》课程的实验报告,面向正在学习数据库概论、需要完成交互式 SQL 实验的本科生,也适合准备数据库课程复习与实验复盘的同学参考。报告围绕 Microsoft SQL Server 2016 环境展开,覆盖数据定义、索引与视图创建、数据更新三类核心内容,包含 Student、Course、Sc、Employee 等表的建表语句,主键与外键约束设置,ALTER TABLE 修改表结构,唯一索引、聚簇索引的建立,以及 IS_Student 视图的定义和多行插入等完整实验过程与结果记录。资源包内为 1 个 docx 文档,压缩包约 1.69MB,篇幅紧凑,可直接对照实验要求复现操作。目前已有 311 人学习下载,读者可从中获得可参照的 SQL 语句模板、实验步骤组织方式与结果截图思路,便于快速完成实验报告撰写并理解数据完整性与用户鉴别等知识点。

1. 结构线与数据线:数据库系统原理实验的整张地图

数据库系统原理的实验报告,把几十条 SQL 排完,最容易产生一种错觉:每条都看得懂,合上报告自己写就卡壳。把整份实验拆成两条线来看会清楚很多。结构线是CREATE TABLE、ALTER TABLE、CREATE INDEX、CREATE VIEW,决定数据能长成什么样、查得快不快;数据线是INSERT、UPDATE、DELETE加上五大类查询,决定怎么把数据准确地取出来。两条线最后在数据完整性和权限控制上汇合——约束、触发器、GRANT与REVOKE才是防止数据被写坏的保障。这份笔记按这个顺序,把实验里出现过的语法重新排一遍,适合正在系统学的人当作知识地图,也适合考前来回翻,重点标在“哪里容易错”而不是“语法长什么样”。

2. 数据定义三件套:建表、改表与索引视图的落点

数据定义这一块看着最机械,其实每一步都在替后面挖坑或填坑。表建错一个类型,后面插入就可能被截断;约束没起名字,后面想删都不知道删谁;索引建反了顺序,查询照样全表扫描。系统复习时不必背语法手册,只要把“表、索引、视图”三件事各自的作用边界记住,实验里绝大多数报错都能对上号。

2.1 CREATE TABLE:每一列都在决定后面会不会翻车

建表语句的结构永远是“列名 + 数据类型 + 约束”三段式,难度不在写,而在选。学号用CHAR(8)而不是INT,是因为它不参与算术运算,还可能有前导零;姓名用VARCHAR比CHAR更省空间,因为CHAR(10)存两个字的名字会补八个空格;工资这类金额用NUMERIC(8,2)而不是FLOAT,是为了避免二进制浮点在求和时飘出小数尾巴。这些选择在实验报告里只有一行,但在真实场景里决定了数据能不能对得上账。

CREATE TABLE Student ( Sno CHAR(8) PRIMARY KEY, -- 主键:唯一且非空,天然带索引 Sname CHAR(10) NOT NULL, -- 姓名不允许为空 Ssex CHAR(2) CHECK (Ssex IN ('男','女')), -- 列级检查约束 Sage INT, Sdept CHAR(20), Sclass CHAR(4) NOT NULL, Stotal SMALLINT DEFAULT 0 -- 未显式赋值时默认为 0 ); CREATE TABLE Course ( Cno CHAR(4) CONSTRAINT PK_Course PRIMARY KEY, -- 命名主键,方便后续引用 Cname CHAR(40), Cpno CHAR(4), -- 先修课号,指向本表 Ccredit SMALLINT ); CREATE TABLE SC ( Sno CHAR(8), Cno CHAR(4), Grade SMALLINT CONSTRAINT SC_CHECK CHECK (Grade > 0 AND Grade < 100), PRIMARY KEY (Sno, Cno), -- 复合主键必须写成表级约束 CONSTRAINT FK_SC_Sno FOREIGN KEY (Sno) REFERENCES Student(Sno), CONSTRAINT FK_SC_Cno FOREIGN KEY (Cno) REFERENCES Course(Cno) );

SC表的主键是(Sno, Cno)组合,这条约束同时表达了两件事:一个学生一门课只能选一次,以及这两个字段联合起来才能唯一确定一行。复合主键只能在表级写,写在某一列后面是没有意义的。Student的主键用列级写法,效果等价,只是少了一层显式命名。参数上还有一个细节:CHECK (Grade > 0 AND Grade < 100)用的是开区间,意味着 0 分和 100 分都插不进去,如果业务上允许满分,条件就得改成>= 0 AND <= 100。这类边界在实验里不会报错,只会在某次插入满分成绩时突然拦下来。

注意:CHAR是定长补空格,VARCHAR是变长。用CHAR(10)存两字姓名,取出来看着一样,但LEN()函数返回的是去掉尾部空格后的长度,DATALENGTH()返回的才是实际字节数,两个函数对不上时先怀疑是不是定长类型在作怪。

2.2 ALTER TABLE 与 DROP TABLE:改结构比建结构更容易出事

修改表结构有四类动作:加列、改列类型、加约束、删约束。四类的风险等级完全不同。加列最安全,但新列如果声明了NOT NULL又没给DEFAULT,表中已有数据就会导致语句失败,因为旧行没法填这个必填项。改列类型最危险,把INT改成SMALLINT,超出范围的值会被截断或直接报错;把CHAR(10)缩短成CHAR(4),长名字直接丢失后半截。加约束要看现有数据是否满足,不满足就加不上。删约束反而最简单,前提是你知道约束的名字。

-- 1. 加列:新列允许为空,已有行不受影响 ALTER TABLE Student ADD Sclass CHAR(4); -- 2. 改类型:范围变窄,已有数据可能溢出 ALTER TABLE Student ALTER COLUMN Sage SMALLINT; -- 3. 加唯一约束:Cname 不允许重复 ALTER TABLE Course ADD UNIQUE (Cname); -- 4. 加自引用外键:先修课号必须指向本表已存在的课程号 ALTER TABLE Course ADD FOREIGN KEY (Cpno) REFERENCES Course(Cno); -- 5. 删除匿名约束再重建为命名约束 ALTER TABLE SC DROP CONSTRAINT SC_CHECK; ALTER TABLE SC ADD CONSTRAINT SC_CHECK CHECK (Grade >= 0 AND Grade <= 100);

第 4 条是这一节的重点:Course表的Cpno指向自己的Cno,这叫自引用外键。它意味着插入课程时必须先插先修课,否则外键找不到参照行。实验数据里“数据库系统原理”的先修课是 5 号“数据结构”,如果先插 1 号课再插 5 号课,第一条插入就会失败。调整顺序或者先把Cpno置空再回填,是两种常见解法。

第 5 条展示的是命名约束的价值。DROP CONSTRAINT后面必须跟约束名,如果建表时没起名字,系统会生成一串类似CK__SC__Grade__1A14E395的随机名,你只能去系统视图里查。命名约束的另一个好处是报错信息可读:违反SC_CHECK比违反一串哈希值好定位得多。

DROP TABLE只有一句,但顺序错了就删不掉。SC通过外键引用Student和Course,直接删父表会被拒绝,必须先删SC,或者先解除外键约束。这个规则在实验里体现为一条很朴素的教训:删表从“引用别人的那张”开始删。

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);

聚簇索引是这三种里最需要理解的。它不只是“更快的索引”,而是把数据行本身按索引键的顺序重排,因此一张表只能有一个,通常默认落在主键上。SC表建了(Sno, Cno DESC)的聚簇索引,意味着数据按学号升序、同学生内部按课程号降序物理排列,查某个学生的选课记录时,相关行在磁盘上是挨着的,一次页读取就能拿到。复合索引的列顺序也不是随便排的:把Sno放前面,是因为查询绝大多数以学号为入口;如果反过来,只按学号查就用不上这个索引。

唯一索引和唯一约束容易混。约束是逻辑层面的规则,索引是物理层面的结构,建唯一约束时系统会自动建一个唯一索引,但反过来建唯一索引并不等于声明了约束——至少在可读性和可移植性上,该用约束的地方还是写约束。

提示:索引不是越多越好。每建一个索引,插入、更新、删除都要多维护一份结构。实验里表小看不出差别,真实表上写多读少的场景要克制。

2.4 视图:既是查询的简写,也是权限的边界

视图在实验报告里往往只出现一次,容易被当成“语法糖”跳过。实际上它承担两个完全不同的职责:把常用的复杂查询包起来复用,以及把一张表的某几行某几列暴露出去而不暴露其余部分。后一个职责是第 4 章权限控制的基础。

-- 单表视图:只暴露信息管理系的学生,且只给三列 CREATE VIEW IS_Student AS SELECT Sno, Sname, Sage FROM Student WHERE Sdept = 'IS'; -- 分组视图:每个学生的平均分,列名显式指定 CREATE VIEW S_G (Sno, Gavg) AS SELECT Sno, AVG(Grade) FROM SC GROUP BY Sno; -- 多表连接视图:把三张表拼成一张宽表 CREATE VIEW XK_VIEW AS SELECT Student.Sno, Sname, Cno, Cname, Grade FROM Student, SC, Course WHERE Student.Sno = SC.Sno AND SC.Cno = Course.Cno;

视图本身不存数据(除索引视图外),每次查询都会把定义里的SELECT展开执行,所以IS_Student里查不到Sdept之外的系,是定义在起作用,不是数据被过滤掉了。S_G这种带GROUP BY的视图不能直接更新,因为一行平均分对应多行原始记录,系统无法反推该改哪一条。XK_VIEW涉及三表连接,更新限制更多,实务里基本只当只读查询用。

列名映射是另一个易漏点。S_G (Sno, Gavg)把AVG(Grade)这个表达式的结果命名成Gavg,如果不写这两个列名,系统会生成一个没有名字的列,后续查询引用时非常别扭。视图定义里的列名列表和SELECT列表是一一对应的,数量必须一致。

3. 查询的四层递进:单表、分组、连接与嵌套子查询

查询是整份实验里篇幅最大的部分,也是最容易“看懂但写不出”的部分。按递进关系切成四层比较好消化:单表管筛选和投影,分组把行压缩成组,连接把多张表拼宽,嵌套把一次查询的结果当成另一次查询的输入。四层的语法各有各的坑,但底层逻辑一致:先确定从哪些表取数,再确定保留哪些行,最后确定输出哪些列。

3.1 单表查询:目标列、条件、排序的固定套路

单表查询的骨架只有三块:SELECT后面写要哪些列,WHERE后面写留哪些行,ORDER BY后面写怎么排。看似简单,实验里被扣分的地方往往就藏在这三块里。

-- 目标列带表达式与别名:三种别名写法等价 SELECT Sname, 2004 - Sage AS YearOfBirth, -- AS 写法 LOWER(Sdept) AS Department FROM Student; -- DISTINCT 作用于整行,不是只作用于第一列 SELECT DISTINCT Sno FROM SC; -- 去重后的选课学生学号 SELECT Sno FROM SC; -- 一个学生选几门课就出现几次 -- 范围、集合、模糊、空值四类条件 SELECT Sname, Sdept, Sage FROM Student WHERE Sage BETWEEN 20 AND 23; SELECT Sname, Ssex FROM Student WHERE Sdept IN ('IS', 'MA', 'CS'); SELECT Sname, Sno FROM Student WHERE Sname LIKE '刘%'; SELECT Sno, Cno FROM SC WHERE Grade IS NULL; -- 多列排序:先按系升序,同系内按年龄降序 SELECT * FROM Student ORDER BY Sdept, Sage DESC;

BETWEEN 20 AND 23是闭区间,20 和 23 都包含在内,对应的NOT BETWEEN 21 AND 24则是开区间的补集,这两个写法的边界很容易在考试里被拿来设陷阱。LIKE '刘%'的百分号匹配任意长度字符,下划线_匹配单个字符,如果要匹配真正的下划线得用ESCAPE声明转义符。IS NULL不能写成= NULL,因为空值参与的任何比较结果都是“未知”,既不真也不假,WHERE只保留结果为真的行,写等号会一行都查不出来。

别名的三种写法也值得记一下:AS 别名、空格直接跟别名、以及 T-SQL 方言里的别名 = 表达式。前两种跨数据库通用,第三种只在特定平台上成立,考试写前两种更稳。

3.2 分组统计:WHERE 过滤行,HAVING 过滤组

聚集函数把多行压成一个值,GROUP BY决定按什么维度压。这一节的核心不是函数名,而是“谁先执行”。

-- 聚集函数的基础用法 SELECT COUNT(*) FROM Student; -- 统计行数,包含空值行 SELECT COUNT(DISTINCT Sno) FROM SC; -- 统计选课学生人数,去重 SELECT MAX(Grade) FROM SC; -- 最高分 -- 分组统计:每门课的选课人数、均分、最高分 SELECT Cno AS 课程号, COUNT(*) AS 人数, AVG(Grade) AS 均分, MAX(Grade) AS 最高分 FROM SC GROUP BY Cno; -- HAVING 过滤分组结果,条件是聚合值 SELECT Cno, AVG(Grade) AS 均分 FROM SC GROUP BY Cno HAVING AVG(Grade) > 90; -- 多表分组:每个班的每门课的统计 SELECT Student.Sclass, SC.Cno, COUNT(DISTINCT SC.Sno), AVG(Grade), MAX(Grade) FROM Student, SC WHERE Student.Sno = SC.Sno GROUP BY Student.Sclass, SC.Cno;

逻辑执行顺序是FROM→WHERE→GROUP BY→HAVING→SELECT→ORDER BY。这个顺序解释了三件事:WHERE里不能用聚集函数,因为分组还没发生;HAVING里可以用聚集函数,也可以引用分组列;SELECT里出现的非聚合列必须出现在GROUP BY中,否则系统不知道该取组内哪一行的值。

COUNT(*)和COUNT(列名)的差别在空值上:COUNT(*)数行数,空值行也算;COUNT(Grade)只数Grade非空的行。实验里插入了一条成绩为NULL的选课记录,用这两个写法统计出来的数字会差一,这个差异正是考点。

AVG同样跳过空值,分母是非空行数而不是总行数。如果想把空值当 0 参与平均,得写AVG(COALESCE(Grade, 0))。这个细节在算“全班平均分”时影响很大:缺考的人到底算不算进分母,业务上要先定清楚。

注意:HAVING里不能引用SELECT中定义的列别名,因为HAVING在SELECT之前执行。别名只能在ORDER BY里用。

3.3 连接查询:WHERE 连接、JOIN ON、自连接与外连接

连接的本质是把两张表按某个条件拼在一起。写法有三种,语义有两种(内、外),再加上一种自己连自己的特殊形态。

-- 写法一:连接条件写在 WHERE 里(隐式内连接) SELECT Student.Sno, Sname, Cno, Grade FROM Student, SC WHERE Student.Sno = SC.Sno; -- 写法二:连接条件写在 ON 里(显式内连接),与写法一等价 SELECT Student.Sno, Sname, Cno, Grade FROM Student JOIN SC ON Student.Sno = SC.Sno; -- 写法三:左外连接,保留左表全部学生,没选课的 Grade 为 NULL SELECT Student.Sno, Sname, Cno, Grade FROM Student LEFT OUTER JOIN SC ON Student.Sno = SC.Sno; -- 自连接:给同一张表起两个别名,查间接先修课 SELECT a.Cno AS 课程号, b.Cpno AS 间接先修课 FROM Course a, Course b WHERE a.Cpno = b.Cno; -- 三表连接:学生、选课、课程拼成宽表 SELECT Student.Sno, Sname, Cname, Grade FROM Student, SC, Course WHERE Student.Sno = SC.Sno AND SC.Cno = Course.Cno;

连接条件漏写是新手最容易出的问题。FROM Student, SC后面不写WHERE,得到的是两张表的笛卡尔积,7 个学生乘 7 条选课记录就是 49 行,看着有数据,全是错的。判断方法很简单:结果行数明显大于任何一张表的行数,先检查连接条件。

三表连接时,连接条件要写两条,把三张表串成一条链。少写一条同样退化成笛卡尔积,只是规模小一点,更不容易发现。字段名在多表里重复时要加表名前缀,Student.Sno和SC.Sno是两个不同的列,不写前缀系统会报“列名不明确”。

自连接的关键是别名。同一张表在FROM里出现两次,必须用两个不同的别名区分,否则系统无法知道Cpno指的是哪一次的列。上面这条语句的含义是:课程 a 的先修课号,正好是课程 b 的课程号,那么 b 的先修课就是 a 的间接先修课。这类“自己跟自己比”的查询在树形结构、上下级关系里非常常见。

外连接的坑在条件位置。LEFT OUTER JOIN ... ON ...里,写在ON后面的条件只影响匹配,左表所有行都保留;如果把右表的过滤条件写到WHERE里,左表中匹配不上的行会因为条件不成立被整行剔除,外连接就退化成内连接了。

3.4 嵌套查询与集合查询:子查询返回什么决定用哪个谓词

嵌套查询的判断标准只有一个:子查询返回的是单值、一列多行,还是布尔存在性。返回值形态决定了外层该用比较运算符、IN、ANY/ALL还是EXISTS。

-- 子查询返回一列多行:用 IN SELECT Sno, Sname, Sdept FROM Student WHERE Sdept IN (SELECT Sdept FROM Student WHERE Sname = '某甲'); -- 子查询返回一列多行且带量词:比 CS 系所有学生年龄都小 SELECT Sname, Sage FROM Student WHERE Sage < ALL (SELECT Sage FROM Student WHERE Sdept = 'CS') AND Sdept <> 'CS'; -- 相关子查询:子查询里引用了外层的 Sno,逐行代入执行 SELECT Sno, Cno FROM SC x WHERE Grade >= (SELECT AVG(Grade) FROM SC y WHERE y.Sno = x.Sno); -- EXISTS:只关心子查询有没有行,不关心返回什么列 SELECT Sname FROM Student s WHERE EXISTS (SELECT 1 FROM SC WHERE SC.Sno = s.Sno AND Cno = '1'); -- 集合运算:并、交、差,要求两边列数和类型一致 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;

< ALL等价于“小于最小值”,> ANY等价于“大于最小值”,这两个转换在选择题里经常出现。反过来< ANY是“小于最大值”,> ALL是“大于最大值”,记混了整道题就废了。

相关子查询和非相关子查询的区别在执行方式上。非相关子查询先算出一个固定集合,外层再拿它做比较,整条语句里子查询只执行一次。相关子查询引用了外层的列,外层每处理一行就要把子查询重算一遍,逻辑上更直观,性能上更贵。上面那条“超过自己平均分”的查询必须用相关子查询,因为每个学生的平均分不一样,没法预先算成一个集合。

EXISTS的语义是“子查询是否至少返回一行”,因此SELECT 1、SELECT *在里面的效果完全相同,写成SELECT 1只是表明不关心具体内容。NOT EXISTS常用来表达“没有选任何课的学生”这类否定条件,比NOT IN安全,因为NOT IN遇到子查询结果里含空值时会整体失效。

集合查询的三个关键字里,UNION默认去重,要去重就得排序,代价比UNION ALL高;INTERSECT和EXCEPT也都隐含去重语义。使用前提是两边的列数、列顺序、数据类型兼容,列名以第一个SELECT为准。

4. 完整性防线:六类约束、触发器与 GRANT/REVOKE

前面三章解决的是“怎么把数据写进去、查出来”,这一章解决的是“怎么保证写进去的东西是对的、只有该看的人能看到”。数据完整性靠约束和触发器守,访问控制靠用户、角色和授权语句守,两者合起来才是一套完整的防线。

4.1 六类约束:谁在什么时机拦住你

约束类型关键字作用对象违反时的表现典型场景
主键PRIMARY KEY列或列组合插入重复值或空值报错学号、课程号
外键FOREIGN KEY ... REFERENCES列插入不存在的参照值报错,删除被引用行报错选课表引用学生表
唯一UNIQUE列插入重复值报错,允许多个空值课程名不重名
检查CHECK (条件)列或表条件为假报错,未知则放行性别只能是男女、成绩在区间内
非空NOT NULL列插入或更新为空报错姓名、班级
默认DEFAULT 值列不违反,只在省略时自动填充总学分默认 0

六类里最容易误解的是检查约束对空值的态度。CHECK (Grade > 0 AND Grade < 100)遇到Grade为NULL时,条件结果是“未知”,系统会放行,所以这条约束拦不住空成绩。如果要连空值一起拦,得额外加NOT NULL,或者把条件写成Grade IS NOT NULL AND ...。

外键的删除行为也值得单独记。默认情况下,父表里被引用的行不允许删除,必须先删子表的引用行。实验里删除一个还有选课记录的学生会直接被拒,正确顺序是先删SC里的记录,再删Student里的记录。某些系统支持ON DELETE CASCADE让删除自动级联,但那属于设计选择,不是默认行为。

默认值约束在实验里体现得很直观:插入学生时省略Stotal,查出来是 0 而不是NULL。区别在于“没写”和“写了空”是两回事,前者触发默认值,后者明确写入空值,默认值不生效。

4.2 命名约束与 ALTER 增删约束:报错信息里的第一手线索

建表时给约束起名字,是一项投入很小、回报很大的习惯。不命名的约束由系统自动生成名字,格式随平台变化,后续要删改就得先去系统视图里查。命名之后,DROP CONSTRAINT 约束名直接可用,报错信息里也会带上这个名字。

-- 命名约束的建表写法 CREATE TABLE SC ( Sno CHAR(8), Cno CHAR(4), Grade SMALLINT CONSTRAINT SC_CHECK CHECK (Grade > 0 AND Grade < 100), PRIMARY KEY (Sno, Cno), CONSTRAINT FK_SC_Sno FOREIGN KEY (Sno) REFERENCES Student(Sno), CONSTRAINT FK_SC_Cno FOREIGN KEY (Cno) REFERENCES Course(Cno) ); -- 修改已有约束:先删后加,两步不能合并 ALTER TABLE SC DROP CONSTRAINT SC_CHECK; ALTER TABLE SC ADD CONSTRAINT SC_CHECK CHECK (Grade >= 0 AND Grade <= 100);

修改约束没有ALTER CONSTRAINT这种写法,标准路径就是删掉重建。删之前要确认现有数据满足新条件,否则ADD CONSTRAINT会失败。如果数据确实不满足又想强行加上,部分平台支持WITH NOCHECK跳过已有数据校验,但那样约束只对新数据生效,属于埋雷操作,实验里不建议用。

加外键约束前还要确认一件事:子表里已有的值必须在父表里存在。Course表加自引用外键时,如果某门课的Cpno指向一个不存在的课程号,这条ALTER TABLE直接失败。排查方法就是先跑一遍“子表值不在父表值里”的查询。

4.3 触发器:约束表达不了的规则才轮到它上场

约束能管住单表内的条件,管不住跨表逻辑。比如“选课成绩不能超过该课程的学分乘以某个系数”,或者“删除学生时自动删除他的选课记录”,这类规则得靠触发器。

-- 在 SC 表上建一个 AFTER 触发器,插入或更新后校验成绩区间 CREATE TRIGGER trg_sc_grade ON SC AFTER INSERT, UPDATE AS BEGIN -- INSERTED 是本次操作影响的新行集合 IF EXISTS (SELECT 1 FROM INSERTED WHERE Grade IS NOT NULL AND (Grade <= 0 OR Grade >= 100)) BEGIN RAISERROR('成绩必须在 0 到 100 之间', 16, 1); ROLLBACK TRANSACTION; -- 回滚整个事务,撤销这次插入或更新 END END;

触发器的核心是两张逻辑表:INSERTED存新增或更新后的行,DELETED存删除或更新前的行。更新操作相当于先删后插,两张表里都有数据。上面的例子用EXISTS判断有没有越界行,有就抛错并回滚。

触发器和检查约束的分工要分清:单表内的简单区间用CHECK更轻量,触发器适合需要跨表查询或需要记录日志的场景。触发器写多了会让数据流变得难追踪,一次插入可能连带触发好几层逻辑,排查问题时像在黑匣子里找线索。实验阶段建一两个理解机制就够了,不必追求复杂。

4.4 用户鉴别与数据控制:GRANT、REVOKE 与视图授权

访问控制分两步:先确认“你是谁”,再决定“你能干什么”。前者是用户鉴别,靠登录账号和数据库用户两级映射完成;后者是权限控制,靠GRANT和REVOKE完成。

-- 1. 建立登录名(服务器级)和数据库用户(数据库级),两者需要映射 CREATE LOGIN demo_login WITH PASSWORD = 'Demo_Pass_2024'; CREATE USER demo_user FOR LOGIN demo_login; -- 2. 授予表级权限 GRANT SELECT, INSERT ON Student TO demo_user; -- 3. 授予视图权限,代替行级授权 GRANT SELECT ON IS_Student TO demo_user; -- 4. 收回权限 REVOKE INSERT ON Student FROM demo_user;

权限粒度从粗到细依次是:数据库级、表级、列级、行级。标准 SQL 只到列级,行级要靠视图来实现。IS_Student这个视图只包含信息管理系的学生,把它的查询权限授予某个用户,效果等价于“这个人只能看信息管理系”,而不用真的去改表结构。这是视图在权限控制里最实用的地方。

GRANT后面加WITH GRANT OPTION表示被授权者可以继续把权限转授给别人;对应的回收语句要加CASCADE,把转授出去的权限一并收回,否则会报“存在依赖权限”的错误。

角色是一组权限的集合,给用户授予角色比逐条授权省事。实验里通常只演示单用户授权,但真实场景里权限都是挂在角色上的,用户离职换岗只改角色归属,不动权限清单。

提示:REVOKE只是收回授权,不等于禁止。如果用户还通过角色间接持有同一权限,收回单条授权不会生效。排查权限问题时要顺着角色链往上找。

5. 易漏点排查:五个在实验里最容易翻车的角落

实验报告写完之后回头对答案,错的地方往往集中在同样的几个位置。下面五条是按“踩坑频率”排的,每条都给出漏在哪、为什么漏、怎么补。

易漏点一:空值的三值逻辑。漏在= NULL、NOT IN和COUNT三处。为什么会漏:直觉上“等于空”应该成立,但 SQL 里空值代表“未知”,任何比较结果都是未知,WHERE只留真值行。怎么补:判空统一写IS NULL/IS NOT NULL;子查询配NOT IN之前先确认结果集里没有空值,有就改NOT EXISTS;统计人数时想清楚要不要算空值行,COUNT(*)和COUNT(列)选一个并在注释里写明理由。

易漏点二:分组查询里出现非分组列。漏在SELECT Sname, AVG(Grade) ... GROUP BY Sno这类写法上。为什么会漏:某些平台宽松模式下会随便取组内第一行的值,语句能跑通,结果却是错的。怎么补:SELECT里除聚合函数外的每一列,都要能在GROUP BY里找到;需要带出姓名就先按学号分组算出聚合值,再和Student表连接一次。

易漏点三:外连接条件写错位置。漏在把右表的过滤条件写进WHERE。为什么会漏:内连接里ON和WHERE的条件效果一样,写顺手了就懒得区分。怎么补:记住一条规则——想让左表整行保留,右表的过滤条件必须写在ON后面;写到WHERE里,匹配不上的行会因为条件不成立被剔除,外连接退化成内连接。写完跑一遍行数,和左表行数对不上就是这个问题。

易漏点四:字段名和数据类型前后不一致。漏在同一份报告里表定义叫Score、查询里写Grade,或者建表用CHAR(8)、插入用 9 位字符串。为什么会漏:手写 SQL 时凭记忆敲列名,不回头看建表语句。怎么补:每个实验段落开始前,把三张表的列名和类型抄在纸上放旁边;字符串超长在某些平台会直接报截断错误,在另一些平台会静默截断,后者更危险。

易漏点五:删除操作的三种语义混着用。漏在分不清DELETE、TRUNCATE和DROP。为什么会漏:三个都叫“删”,实际删的东西完全不同。怎么补:DELETE删行,可带WHERE,走事务日志,能回滚,会触发触发器;TRUNCATE清空整表所有行,不能带条件,速度更快,重置自增列;DROP删的是整个表对象,连结构带数据一起没。有外键引用时,TRUNCATE和DROP都会被拦,DELETE也可能因为引用完整性失败,删之前先看清楚依赖关系。

6. 复习清单:从语法速查到上机自测的一条路径

复习这块内容,光看不动手基本等于没复习。我的做法是分成三轮:第一轮照着清单过一遍语法点,确认每个关键词能默写;第二轮把实验报告里的语句遮住结果自己重写一遍,写完对行数;第三轮专门挑错题和易漏点做针对性补漏。

模块必须能默写的关键点自测方式
数据定义建表三段式、复合主键表级写法、ALTER COLUMN与ADD CONSTRAINT不看报告写出三张表的建表语句
索引普通、唯一、聚簇的区别,复合索引列顺序说出为什么聚簇索引只能有一个
视图CREATE VIEW 名(列名) AS SELECT、分组视图不可更新写一个只暴露部分行的视图
单表查询BETWEEN闭区间、IN、LIKE通配、IS NULL、多列排序每类条件各写一条
分组统计逻辑执行顺序、WHERE与HAVING分工、COUNT(*)与COUNT(列)写出“均分大于 90 的课程”
连接查询隐式与显式内连接、左外连接、自连接别名写出“没选任何课的学生”
嵌套与集合IN、ALL/ANY、EXISTS、UNION/INTERSECT/EXCEPT把< ALL改写成聚合函数形式
完整性六类约束、命名约束、删约束再重建列举三条会拦住插入的约束
权限控制登录名与用户映射、GRANT、REVOKE、视图授权说明视图如何实现行级权限

清单里最值得反复过的是“分组统计”和“连接查询”两行,这两块占分最多,也最容易在执行顺序上绕晕。检验自己是否真懂,有个简单的办法:拿一道题,先说清楚FROM、WHERE、GROUP BY、HAVING、SELECT、ORDER BY六步各自处理了什么,再说出每一步之后结果集的行数大概是多少。能说清行数变化,基本就不会写错了。

上机自测时建议专门造一批“脏数据”:一条成绩为空、一条成绩刚好卡在边界值、一个学生一节课都没选、一门课没人选。这四类数据能把空值处理、边界条件、外连接、分组统计的漏洞一次性暴露出来。实验报告里给的数据太干净,跑通不代表写对。

血泪经验是:我最早复习时总想先看答案再自己写,结果考试时语法记得,逻辑顺序全乱。后来改成先写后对,写完必须跑一遍行数校验,对不上就从头查连接条件。这个习惯一直保留到现在,每次写多表查询都强制走一遍“先数行数、再看列名、最后看条件位置”的三步检查。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/27 3:01:31

Flink Task Slot深度解析:资源隔离、四维耦合与生产调优

1. 为什么“Task Slot”不是Flink里一个可有可无的配置项&#xff1f;很多人第一次接触Flink时&#xff0c;看到taskmanager.numberOfTaskSlots这个参数&#xff0c;下意识觉得它只是个“并发度调节旋钮”——调大一点&#xff0c;任务跑得快&#xff1b;调小一点&#xff0c;省…

作者头像 李华
网站建设 2026/10/27 3:01:23

AI投资泡沫与机遇:Gurley的行业洞察与策略

1. 泡沫与机遇&#xff1a;AI行业的现状透视当ChatGPT引爆全球AI热潮时&#xff0c;风险投资家Bill Gurley却发出了与众不同的声音。作为Benchmark Capital的传奇合伙人&#xff0c;他曾在互联网泡沫时期精准预判了科技行业的兴衰周期。如今面对AI投资狂潮&#xff0c;Gurley再…

作者头像 李华
网站建设 2026/10/27 3:01:14

拼多多反爬虫策略解析与逆向实战

1. 项目背景与核心挑战最近在分析主流电商平台的内容风控机制时&#xff0c;拼多多的反爬虫策略引起了我的特别注意。作为国内用户量前三的电商平台&#xff0c;拼多多在内容保护方面投入了大量资源&#xff0c;其anti-content机制每隔3-6个月就会有一次重大升级。去年第三季度…

作者头像 李华
网站建设 2026/10/27 3:01:01

FBGW频带引导加权采样模块在YOLO目标检测中的创新应用

1. 项目概述&#xff1a;FBGW频带引导加权采样模块的革新意义在目标检测领域&#xff0c;YOLO系列算法始终保持着迭代创新的节奏。这次我们聚焦于C2PSA模块的改进方案——用FBGW&#xff08;Frequency Band Guided Weighting&#xff09;频带引导加权采样模块进行替换。这个改进…

作者头像 李华
网站建设 2026/10/27 3:00:27

大模型:shuffle的原理和操作实践

shuffle&#xff08;数据混洗&#xff09;是影响模型收敛效果的关键环节。如果训练样本按固定顺序输入&#xff0c;模型会学习到样本顺序带来的虚假规律&#xff0c;造成过拟合、loss 震荡、泛化能力变差。昇思 MindSpore 与 MindSpore‑Transformers 针对普通数据集、Megatron…

作者头像 李华
网站建设 2026/10/27 3:00:27

CATIA三维文字制作:CAD文字转曲、DXF导入与凸台拉伸避坑

简介&#xff1a;《catia_文字制作.doc》是一份面向CATIA初学者、机械设计与产品建模人员的三维文字制作教程文档&#xff0c;主要解决在产品模型表面添加标识、说明文字以及制作空心字轮廓时的入门与实操需求。压缩包内仅含1个doc文件&#xff0c;大小约1.4MB&#xff0c;结构…

作者头像 李华