ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

SQL Server知识地图:从边界参数到索引视图的完整学习笔记

SQL Server知识地图:从边界参数到索引视图的完整学习笔记

简介:SQL Server 学习笔记面向数据库管理与开发初学者,以简明条目梳理 SQL Server 的核心概念与常用操作,涵盖创建(Create)、删除(Drop)、修改(Alter)等数据库对象操作,以及数据库结构、系统数据库、文件组、约束类型和关系模型等知识点,可作为日常查阅与备考复习的速查手册。资源共 1 个 Word 文档,文件格式为 doc,整体约 499KB,内容组织紧凑,便于按需翻阅与打印学习。已有 440 人浏览学习。笔记进一步铺开数据库授权、创建数据库语句、候选码、主码、外部码,以及主键约束、外键约束、默认值约束、用户自定义约束(Check)和唯一性约束(Unique)等细节,并补充 C/S 体系、分布式管理框架、数据文件与日志文件的作用,还介绍了 Master、Model、Tempdb、Msdb 等系统数据库的用途,可帮助读者快速建立关系数据库整体认知,为后续 SQL Server 实操打下坚实基础。

1. SQL Server 知识地图:先看体系再看语句

SQL Server 学习笔记很多人第一反应是背语句,但这份笔记最值得先看的是它的「边界参数」和「对象体系」:一个库最大能到 1TB,一张表最多 20 亿行、1024 列,索引分成 1 个簇索引加 249 个非簇索引,触发器每表只有 3 个。把这些数字和对象关系记在脑子里,再去看 Create、Drop、Alter、Select 这些语句,会发现所有操作都是在「关系模型」这张地图上做增删改查。这条路径适合两类人:一类是系统学 SQL Server 的开发者,另一类是考前突击复习的备考者。下面按「全局参数 → 建表约束 → 视图索引 → 存储过程游标 → 查漏补缺」的顺序把整份笔记拆开。

2. 核心对象与系统库:建库前必须看懂的边界参数

2.1 SQL 语句家族:DDL、DML、DCL 的分工

SQL 语句先按作用对象分三类记。定义类 Create、Drop、Alter 处理对象本身:建表、删索引、改视图结构都归它管;操作类 Select、Insert、Update、Delete 处理对象里的数据;控制类 Grant、Revoke 处理权限。最容易混的是 Drop 和 Delete:Drop 是把表、视图这类对象整个删掉,Delete 只删行,表结构还在。复习时可以用一个判断句自查——「我要删除的是结构还是数据」,结构走 DDL,数据走 DML。

DCL 在 SQL Server 里落地是一套「登录-用户-角色」流程:先 Sp_addlogin 建登录名和密码,再 Sp_adduser 把登录名映射成数据库用户,最后 Sp_addrole 建角色。授权本质是给用户指定基于该登录所拥有的数据库操作权限,不是直接在表上写 allow 语句。这个顺序很多人第一次接触会漏掉映射那一步:只在服务器层建了登录,没进数据库层建用户,结果登录后看不到任何库。

另外,SQL Server 的体系是 C/S 结构,提供 ODBC、VB、VC、Access 等编程接口,支持 Web 分析、数据仓库、XML 和分布式分区视图,还提供分布式管理框架。关系数据库模型分两层:概念模型描述现实事物,数据模型包含数据结构、数据对象、数据操作和约束条件。这两层是后面所有建表设计的理论基础,也是简答题的高频考点。

2.2 对象的极限参数:一张表看清 SQL Server 的边界

对象上限备注
数据库大小1MB ~ 1TB默认情况下足够业务使用
每库表数量20 亿几乎不构成设计瓶颈
每表列数量1024真正约束表设计的是列数
每表列的最大字符8060多列总宽度的隐藏上限
每表索引1 个簇类 + 249 个非簇类非簇索引最多支持 16 列
每表触发器3 个INSERT、UPDATE、DELETE 各一
存储过程1024 个参数支持 32 级嵌套
用户连接32767 个
锁定及打开对象20 亿
同时打开数据库327687 个笔记原文数据

这些数字不建议死记,而是用来建立「边界感」。比如说,学索引时知道每表只有 1 个簇索引,就会理解为什么经常搜索的列不能全都建成簇索引,必须挑一个最核心的排序维度。再比如触发器只有 3 个,和 INSERT、UPDATE、DELETE 一一对应,就不会问「能不能给 SELECT 建触发器」这种问题。

还有两个容易被忽略的「隐藏参数」:每库可以有 20 亿个表,但这个数字不是鼓励你建那么多表,而是告诉你 SQL Server 对表数量几乎不设限,真正限制设计的是列数 1024 和行宽 8060。设计表时如果一列 varchar(8000) 再加几个字段,就可能触发行宽上限,这是实际开发里比表数量更先撞上的墙。

2.3 系统数据库与物理文件:Master 到 ldf 的全路径

六个系统数据库的分工,是数据库管理员的常识题。Master 保存登录账号和服务器配置;Model 是模板库,所有新建数据库都从它复制系统表;Tempdb 专门放临时表,用户退出或连接终止时自动消失;Msdb 默认 12MB,管调度作业、警报和操作员信息;Pubs 和 Northwind 是样例数据库,不用来存业务数据。复习时把它们想象成「管理后台」和「样板间」:Master 是后台权限中心,Model 是装修模板,Tempdb 是杂物间,Msdb 是定时任务调度室。

存储层面,数据库被映射成操作系统文件。主数据文件 .mdf 包含启动信息和主要数据;次要文件 .ndf 用来延伸,数据量超过主文件或要分散到多个磁盘时用;日志文件 .ldf 记录事务日志,是事务机制和可恢复性的核心。文件组把多个文件打包成一个管理单元,方便分配和统一管理。这里要记住一个关系:一个数据库至少有一个 .mdf 和一个 .ldf,.ndf 是可选的。

事务日志要单独拎出来理解:它记录的是一个「不可分割的数据库操作的日志记录」,不是数据本身。这意味着如果一次事务执行到一半失败,系统靠日志恢复现场,而日志文件顺序写、不随机写,这也是为什么日志文件和数据文件要分开存放。创建数据库的语句结构也在这个环节顺带记下来:Create database 数据库名,On 定义数据文件(文件名、存储路径、大小、可以增长到的最大值、增长增量值),Log on 定义事务日志文件,参数格式和数据文件一一对位。一般用户建库时可以不指定方括号里的可选项,关键字大小写不敏感,逻辑名可以用中文。

3. 建表与约束:从术语到五大约束的完整链路

3.1 关系术语对照表

先建立关系术语和日常说法的映射:

术语含义对应说法
关系一张表学生表
元组表中的一行一条记录
属性表中的一列一个字段
主码唯一确定一个元组的属性或属性组学号
候选码能唯一标识元组的属性集学号、身份证号都是候选码
外部码联系两张表的属性学生表中的班级编号
域属性的取值范围性别(男、女)
分量元组中的一个属性值某行的学号值
关系模式对关系的描述学生(学号、姓名、性别)

实体是客观存在、可以相互区分的事物,属性是实体的某一特性,码是唯一标识实体的属性集,域是属性取值范围,实体型用实体名加属性集合表示(顾客(编号、姓名、性别、年龄)),实体集是同型实体的集合。联系分实体内部和实体之间两种。这些概念在选择题里经常互换着考,核心就是「码选哪个、属性属于谁、域限制什么」。

关系的五个条件也值得背熟:每一列的数据类型必须相同;没有完全相同的行;列与列之间的字段名不能重复;没有组合列,即一列不能分割成两列或更多列。这里最容易考的是「既有订单号又有多个商品名的表为什么不是关系」——因为它有组合列,违反第五条。每张表就代表一个关系,这句话看似简单,实际已经在约束你的表结构设计:凡是出现「一行里塞多个同类值」的设计,都不是标准关系。

3.2 五大约束:主键、外键、默认、check、unique

创建表时常用的约束有五种,先看完整语法骨架:

CREATE TABLE 表名 ( 列名 数据类型 [NOT NULL] [DEFAULT 默认值], ... [CONSTRAINT 约束名] PRIMARY KEY (列1[, 列2]), [CONSTRAINT 约束名] FOREIGN KEY (列名) REFERENCES 外表(主键列), [CONSTRAINT 约束名] CHECK (检查条件), [CONSTRAINT 约束名] UNIQUE (列名) )

主键约束对应基本表的主码,用来唯一标识每行,主键值不能为空,且每行都有非空唯一值。外键约束在两张表之间建立连接:一张表做增删改查时,要注意与之关联的另一张表的对应数据。默认值约束是系统在用户未指定时自动指派的值。check 约束由用户建立规范,输入值不满足规范时系统拒绝执行,用来保证域完整性。唯一性约束保证定义列中的输入值不可重复,重复输入会被拒绝。

这个环节最值得对比的是主键约束和唯一性约束:两者都要求唯一,但主键不允许空值、一张表只能有一个主键;unique 约束可以多个,且允许一个空值(在 SQL Server 中默认唯一索引只允许一个 NULL)。外键约束涉及两张表,所以增删改查时要先想清楚父子表的操作顺序。default 约束是「没填时自动填」,check 约束是「填了要过体检」,前者兜底,后者拦截。

数据完整性四条规则和约束的对应关系也要清楚:实体完整性靠主键,域完整性靠 default 和 check,参考完整性靠外键,用户自定义完整性靠用户定义的约束和规则。这四条是数据库设计题的答题框架——拿到表先问「行怎么唯一、列的值范围怎么控制、表间关系怎么维护」。

3.3 数据类型:char 与 varchar、整型、money 与 datetime

SQL Server 常用类型按用途分四组。字符型里,char(n) 是固定长度,n 取 1 到 8000,实际存储就是 n 字节,超过会被截掉,字符常量写在单引号或双引号中;varchar(n) 是可变长度,存储大小按输入字符的实际字节数计算。整型有 int、bigint、smallint、tinyint 四档,按取值范围选。Numeric(p, s) 表示定点小数,p 是总位数,s 是小数位。Money 是货币类型,值不需要单引号,但要带货币符号。Datetime 表示日期和时间。Table 类型用于存储一组数据并作为函数结果集返回,它的定义类似建表,只支持主键、唯一性和空值约束。

char 和 varchar 的选择是个经典考点。char(50) 不管存几个字符都占 50 字节,varchar(50) 存多少字符占多少字节。看起来 varchar 更省空间,但固定长度的列更新时不需要重新组织页面,所以短且长度稳定的字段(如性别、状态码)用 char,长度差异大的字段用 varchar。笔记里特别提醒:char(n) 超过 n 时直接截断,不会报错,所以业务上要提前约定字段长度上限。

插入数据时的规则是:插入的列值必须与表中列的个数和顺序匹配,否则插入失败;如果创建表时列允许为空,插入时可以写 null 代替。这个「顺序匹配」是初学者最常见的翻车点——省略列名直接 insert into 表 values(...),一旦表结构调整,insert 立刻报错,而且报错信息往往只提示「列名或所提供值的数目与表定义不匹配」,根本看不出是哪一列错位。

3.4 修改表结构:add、drop、check constraint、alter column

修改表语句按用途分成四类,先看骨架:

-- 1. 增加属性或约束 ALTER TABLE 表名 ADD 新列名 新列类型 ALTER TABLE 表名 ADD CONSTRAINT 新约束名 约束定义 -- 2. 删除属性或约束 ALTER TABLE 表名 DROP COLUMN 列名 ALTER TABLE 表名 DROP CONSTRAINT 约束名 -- 3. 打开/关闭约束 ALTER TABLE 表名 CHECK CONSTRAINT ALL ALTER TABLE 表名 NOCHECK CONSTRAINT ALL -- 4. 改变列类型或空值设置 ALTER TABLE 表名 ALTER COLUMN 列名 新类型 [NULL | NOT NULL]

删除列的坑是最多的:被复制列、用在索引中的列、用在 check / foreign key / unique / primary key 中的列、有默认值关联的列、绑定默认对象的列、绑定规则的列都不能直接删。换句话说,先删依赖,再删列,否则系统拒绝执行。实际删列前先用系统视图查一遍列上有哪些约束和索引,再执行 drop constraint 和 drop column,顺序不能反。

打开和关闭约束适合大数据量导入场景:先 nocheck 关掉约束,导入完再 check 打开。注意「关闭约束」不影响数据本身,只在插入和更新时跳过检查,所以关闭期间写入的脏数据不会自动被清理,重新打开约束也不校验存量数据——这是很多人忽略的边界。修改表数据用 update,set 指明被修改的列,from 引入其他表的数据,where 指定更新条件。不带 where 就是全表更新,这是测试环境事故的高发地,写 update 前先 select 一遍确认影响行数。

3.5 三大范式与 BCNF:建表前最后一道设计检查

范式是判断表设计是否合理的标准。第一范式要求所设计表的每个分量必须是不可分的数据项,即每一列不许是集合、序列等非原子属性。经典反例是「商品编号」列里存了一组商品类别:goods_id(goods_type_1, goods_type_2, goods_type_3),查询「类别为生活用品的商品编号」时结果为空。把非原子属性转成原子属性有两种办法:一是拆成多列或多行,二是把一张表分解为多张表。分量是否需要继续划分,与具体业务有关,不是越细越好。

第二范式在第一范式之上,要求每个非主属性完全依赖于码,不允许存在部分依赖。典型场景是用户编号和用户购买商品编号组成联合主键,但「用户购买商品名称」只依赖商品编号,不依赖用户编号。这样一旦主键某一列为空,就会出现插入异常;重复存入数据又造成更新困难。建表时应把依赖拆分到不同表里,让每个表的非主属性都完整依赖自己的主码。

第三范式处理传递依赖:如果一组关系中存在 a→b、b→c(c 不属于 b),且 b 推不出 a,就不满足第三范式。翻译成大白话就是「非主属性不能通过另一个非主属性间接依赖主码」。BCNF 是修正的第三范式,解决某些特殊情况下第三范式仍存在的冗余问题。复习时用一句话串联:一范式管「列不可再分」,二范式管「非主属性完全依赖主码」,三范式管「非主属性不能传递依赖主码」。

4. 视图与索引:查询优化的两把钥匙

4.1 视图的建立与限制:with check option、encryption、schemabinding

视图是一张「逻辑表」,本质是保存了一个查询定义。建立视图的完整语法:

CREATE VIEW 视图名 [(列名清单)] WITH ENCRYPTION | SCHEMABINDING | VIEW_METADATA AS SELECT 查询语句 WITH CHECK OPTION

各参数作用不同:With check option 规定对视图插入、修改时必须遵循视图定义查询语句里的条件;With encryption 要求存储视图语句文本时加密,防止别人看到定义;With schemabinding 把视图绑定到架构上,基表结构就不能随便改;With view_metadata 让描述结果集时返回视图名而不是基表名。其中 check option 最实用,它保证通过视图改数据时,数据范围始终落在视图筛选条件之内。

实际建视图时,还可以在视图名后面重写列名。比如 create view goods_view(商品名称, 商品类别) as select goods_name, goods_type_name from goods_1,这样视图使用者看到的就是业务语言而不是物理字段名。多表视图也一样,把两张表通过等值连接合成一张逻辑表,用户只查视图,不碰底层表。

定义视图的查询表达式有限制:定义视图的用户必须有对基表或视图的 select 权限;查询里不能含 into、order by、compute、compute by 这些关键字;不能引用临时表或表变量。为什么视图不能带 order by?因为视图是逻辑表,行序对使用者无意义,排序应该在调用视图的查询里做。为什么不能引用临时表?因为临时表在用户退出时消失,视图定义还依赖它就变成悬空引用。

4.2 视图更新条件:为什么有些视图改不了数据

视图能不能更新,不是看视图名,而是看它的查询定义。可更新视图必须满足:from 子句至少引用一个表;选择列表中没有派生列;选择列表中没有聚合函数,也不包含 top、group by、union 或 distinct。一句话总结:视图里一旦出现「计算结果」或「合并结果」,就无法把更新反向映射回基表的一行。

测试方法很简单:如果视图只是对单表做筛选和投影(去掉一些列、筛掉一些行),那它一定可更新;如果视图里有计算列、分组或集合运算,就别指望它能 update。修改视图结构的简化语法是:

ALTER VIEW 视图名 AS SELECT 查询语句 [WITH CHECK OPTION]

with check option 在修改视图时同样可用,强制视图上执行的所有数据修改语句都符合定义视图查询语句设置的准则。删除视图数据用 delete from 视图名,但前提是视图可更新且只引用一个基表;涉及两张表时删除会失败。更新视图数据的完整语句和 update 基本表一致,只是把表名换成视图名:

UPDATE 视图名 SET 列名 = 表达式 WHERE 条件

4.3 索引:簇索引与非簇索引的根本区别

索引是加速检索表中记录的树状结构,能独立于原表存储。判断簇索引和非簇索引的标准是:索引顺序与表的物理存储顺序是否相同。簇索引与表的物理顺序一致,一个表只能有一个簇索引;非簇索引按完全不同的结构排序,它先通过索引找到数据存储位置,再回表取数据。

这个「回表」动作是关键。簇索引的叶子节点就是数据本身,查到索引就查到数据;非簇索引的叶子节点存的是指向数据行的指针,查到索引还得再去数据页取一次。所以同一张表,簇索引的查询路径更短,但建簇索引会改变物理存储顺序,代价是插入和更新时可能要移动数据页。这就是为什么经常范围查询的主键列适合簇索引,频繁更新的列不适合。

4.4 索引创建时机:哪些列该建、哪些列别建

一般的创建原则:在经常搜索的列上建;在需要排序的列上建;在主键或外键上建;在经常用 where 的列上建。创建语句:

CREATE UNIQUE CLUSTERED INDEX goods_index ON goods_1 (goods_id) CREATE INDEX goods_index2 ON goods_1 (goods_id, goods_type_id)

参数说明:Unique 表示唯一性索引,不允许两行索引值相同;Clustered 表示簇索引,每表只有一个;Asc/Desc 控制排序方向。第二个语句是复合索引,多列一起建索引时,最左列决定索引的检索起点,所以查询条件里如果只用 goods_type_id 而不用 goods_id,这个复合索引可能帮不上忙。

还要区分索引和约束:主键约束会自动创建唯一簇索引,但 unique 约束创建的是唯一非簇索引。建表时加了 primary key,其实相当于同时建了一个索引,这也是主键列查询快的原因。非簇索引不是越多越好,每张表的非簇索引维护成本随增删改同步产生,索引数量过多时写入性能会明显下降。一般优先满足最频繁的查询路径,别的查询能用复合索引覆盖就不另建。

5. 存储过程、游标与数据操纵:易错点全盘点

5.1 存储过程:参数、嵌套与封装限制

存储过程把一批 SQL 语句打包成一个可调用的对象,里面可以包含查询、插入、删除或更新,调用时一次性执行。它和视图的最大区别是:视图是查,存储过程是「做」,能做任何语句。创建语法:

CREATE PROCEDURE modify_process @bid_user varchar(10), @bid_date datetime, @bid_number int, @bid_goods_id varchar(10), @goods_price money AS UPDATE goods SET goods_number = goods_number - @bid_number WHERE goods_id = @bid_goods_id INSERT INTO user_history VALUES (@bid_user, @goods_price * @bid_number, @bid_date) GO EXECUTE modify_process '某用户', '2024-01-01', '0001', 100, 2

过程内部先扣库存,再写历史记录。go 是批处理结束标志,execute 是调用入口。存储过程一个最多 1024 个参数,支持 32 级嵌套。参数顺序和调用顺序对应,参数多的时候建议显式写成「参数名 = 值」的形式,避免调用时记错位置。

存储过程有个隐藏特性:因为语句已被封装,创建时不会对其引用的表做存在性验证。这意味着过程创建成功不等于运行成功,如果表名拼错,创建时不报错,第一次执行才炸。调试时先确认表名拼写,再去查过程内部逻辑。把批量操作封装进存储过程还有一个好处:应用程序只需要一次调用,就能保证多表更新的顺序一致性,省去客户端逐条拼接 SQL。

5.2 游标的完整生命周期:declare、open、fetch、close、deallocate

游标用于逐行处理结果集,适合数据量不大但需要逐行计算的场景。生命周期是固定的五步:declare 声明、open 打开、fetch 取行、close 关闭、deallocate 释放,缺一不可。

DECLARE @goods_name varchar(10), @goods_number int DECLARE shop_info CURSOR FOR SELECT goods_name, goods_number FROM goods WHERE goods_name LIKE 'S%' ORDER BY goods_name, goods_number OPEN shop_info FETCH NEXT FROM shop_info INTO @goods_name, @goods_number WHILE @@FETCH_STATUS = 0 BEGIN PRINT @goods_name + ' ' + CAST(@goods_number AS varchar(10)) FETCH NEXT FROM shop_info INTO @goods_name, @goods_number END CLOSE shop_info DEALLOCATE shop_info

@@fetch_status 是判断是否还有行的关键:取值 0 表示这次 fetch 成功,-1 表示已经取到结果集末尾,-2 表示这一行已被删除。循环里最容易漏的是「循环体内最后一次 fetch」——如果 fetch 写在循环体外,而循环体内不再 fetch,就会死循环或漏行。每次 fetch 后都要检查状态,循环体最后必须再 fetch 一次推进游标。

游标用完必须 close 再 deallocate。close 释放当前结果集,deallocate 释放游标占用的资源。只 close 不 deallocate,游标定义还在;只 deallocate 不 close,在部分场景下会提前释放未关闭的结果集。规范做法是 try-finally 式的收尾:fetch 循环结束后立刻 close,最后 deallocate。

5.3 带子查询的 update/delete 与模糊查询

更新和删除经常要参照另一张表的数据。经典场景是拍卖系统:某个用户拍下商品后,先扣库存再删竞拍记录。

UPDATE goods SET goods_number = goods_number - 1 WHERE goods_id IN ( SELECT goods_id FROM bid_record WHERE reg_name = '某用户' ) DELETE FROM bid_record WHERE reg_name = '某用户'

这里的 in 返回多值结果,因为同一个用户可能竞拍了多件商品。update 和 delete 涉及两张表时,要注意外键约束的操作顺序:先删子表(竞拍记录)再删主表(商品),否则外键约束会拦住删除。update 和 delete 的子查询结果集都要先 select 单独跑一遍,确认命中行数和预期一致,再执行修改操作。

模糊查询是前端搜索的常见实现。like 配合 % 是通配做法:like '关键字%' 匹配前缀,'%关键字%' 匹配任意位置。但 like 对前导通配符(% 在开头)没法利用索引,全表扫描是大概率事件。笔记里还记录了一个搜索场景:用户问的是「问题」,不是问题编号,不同人对同一问题的表述千差万别。解决思路是在问题表里加一个拼音字段,客户输入首拼或由程序生成首拼,再执行 like 查询。兼顾「开头匹配」和「中间包含」两种需求,可以用 union 组合:

SELECT title FROM mydb WHERE title LIKE '关键字%' UNION SELECT title FROM mydb WHERE title NOT LIKE '关键字%' AND CHARINDEX('关键字', title) >= 1

第一段走前缀匹配,第二段用 charindex 找出不是前缀但包含关键字的记录,union 自动去重。这个写法在数据量不大、标题字段没有全文索引时很实用。

5.4 五个最容易漏的点:每一条都是踩坑换来的

易漏点 1:删除列前没查约束,删不掉还莫名其妙。

这是每天都会遇到的问题。删除列时,只要有约束或索引在它身上,系统直接拒绝。漏的原因是报错信息只说「不能在约束上删除」,没说是哪个约束。补的方法:先查列上的约束、索引、默认值绑定,按顺序 drop constraint 再 drop column,避免「删一次、报错一次」。

易漏点 2:insert 列的个数和顺序不匹配,报错后才看表结构。

原因在省略列名列表,直接 values 硬填,表一改就错位。补法:所有 insert 都显式写明列清单,列清单比纯 values 多几个字符,但表结构调整后语句不会静默出错。可空列用 null 占位,缺列会直接失败。

易漏点 3:主键列允许为空或联合主键部分依赖,造成插入异常和更新困难。

联合主键一旦某列可以为空,主键约束就形同虚设;非主属性只依赖部分主键列,会导致重复数据和更新不一致。补法:建表时主键列强制 not null,设计阶段先把非主属性和完整主键的依赖关系画清楚,让每个表满足第二范式。

易漏点 4:视图里写 order by 或引用临时表,创建视图直接失败。

原因是不清楚视图查询表达式的三条禁令:不能含 into、order by、compute、compute by;不能引用临时表或表变量;定义视图的用户必须对基表有 select 权限。补法:把排序放到查询视图的外层,临时表换成普通表或表值函数。

易漏点 5:存储过程引用的表名拼错,创建时不报错,执行时才炸。

原因是存储过程中的 SQL 已被封装,创建阶段不对表做存在性验证。补法:写完存储过程后,先在查询窗口单独执行过程体里的 SQL,确认所有表名、列名都能解析,再创建过程。这个习惯能省掉大量「为什么过程运行失败」的排查时间。

6. 复习清单:按这张表查漏补缺

考前或项目交付前,我会强制自己过一遍这张检查表:

模块必须会自测问题
语句分类DDL / DML / DCLDrop 和 Delete 的本质区别?Grant 和 Revoke 属于哪类?
系统库Master / Model / Tempdb / MsdbTempdb 里数据什么时候消失?Msdb 管什么?
建表五大约束 + 数据类型主键和 unique 的区别?char(50) 与 varchar(50) 存储差异?
范式一、二、三范式与 BCNF传递依赖违背第几范式?联合主键部分依赖属于什么问题?
视图可更新条件为什么视图不能含 order by?with check option 作用?
索引簇索引与非簇索引一张表几个簇索引?非簇索引查找为什么回表?
存储过程参数传递与嵌套存储过程最多几个参数?表名拼错为什么创建时不报错?
游标完整生命周期fetch 后 @@fetch_status = 0 / -1 / -2 各代表什么?
数据操纵子查询 update/delete删除两张表关联数据时的顺序?

查漏补缺的具体方法是「讲题法」:拿一张空白的建表演练,把上述每一行用嘴讲一遍。比如讲主键约束时说「主键不能为空且值唯一,一张表只能有一个主键,unique 可以多个但只允许一个空值」。能不看笔记讲通,就说明这个知识点真的变成了自己的;讲不顺的地方,回到对应章节原文再抄一遍。

另外提醒一个复习顺序的问题:不要从最后一张表倒着背。正确的查漏顺序是「对象 → 建表 → 查询优化 → 程序化操作 → 设计理论」,这和实际项目的推进顺序一致。背完参数直接跳去范式,会丢掉视图和索引之间的关联——视图是逻辑层的过滤,索引是物理层的加速,两者共同决定查询性能。

从那以后,我每次建表都强制走一遍固定流程:先画实体关系图,确认主码和外码;再写约束清单,主键、外键、默认值、check、unique 逐项核对;最后才动笔写 create table。写完之后用上面那张表逐条自测,不再凭感觉说「这表应该没问题」。希望帮到你。

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

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进