在 SQL Server 中,约束(Constraint) 是用于确保数据库中数据的完整性、一致性和有效性的规则。它们可以防止无效数据进入表中,保证数据质量,让数据库的结构和内容更合理、可靠。以下从常见约束类型、作用、语法和使用场景等方面详细讲解:
一、常见约束类型及详细说明
1. 主键约束(PRIMARY KEY
)
- 作用:
- 唯一标识表中的每一行数据,相当于表的 “身份证”。
- 保证主键列的值不重复、不为空(
NOT NULL
) 。 - 数据库会自动基于主键建立索引,大幅提升查询效率(比如按主键查询某行数据时,能快速定位)。
- 语法示例:
-- 方式1:创建表时直接定义主键 CREATE TABLE Student ( ID INT PRIMARY KEY, -- ID 列设为主键,自动非空、唯一 Name VARCHAR(50) ); -- 方式2:通过 CONSTRAINT 自定义约束名(更灵活,方便管理) CREATE TABLE Student ( ID INT, Name VARCHAR(50), CONSTRAINT PK_StudentID PRIMARY KEY (ID) -- PK_StudentID 是自定义约束名,PK 是主键缩写 );
- 特点:
- 一张表只能有 1 个主键(但可以是 “复合主键”,即多个列组合成主键,比如
(ID, ClassID)
,需保证组合值唯一 )。 - 主键列的数据类型通常选
INT
、BIGINT
或带唯一性的字符类型(如CHAR(10)
),方便快速检索。
- 一张表只能有 1 个主键(但可以是 “复合主键”,即多个列组合成主键,比如
2. 唯一约束(UNIQUE
)
- 作用:
保证某一列或多列的组合值唯一,防止重复数据,但允许列值为空(NULL
)(不过如果列本身设了NOT NULL
,就既非空又唯一 )。
常用于 “辅助唯一标识” 的场景(比如学生表中 “手机号” 列,要求不重复,但不是表的主键 )。 - 语法示例:
CREATE TABLE Student ( ID INT PRIMARY KEY, Phone VARCHAR(20) UNIQUE, -- 直接定义,自动生成默认约束名 Email VARCHAR(50), CONSTRAINT UQ_StudentEmail UNIQUE (Email) -- 自定义约束名 UQ_StudentEmail,UQ 是唯一约束缩写 );
- 特点:
- 一张表可以有多个唯一约束(比如同时约束手机号、邮箱等 )。
- 唯一约束也会隐式创建索引,提升查询唯一性列的效率,但索引类型和主键的索引有区别(主键是聚集索引,唯一约束默认是非聚集索引,可手动调整 )。
3. 外键约束(FOREIGN KEY
)
- 作用:
建立表与表之间的 “关联关系”,保证从表(子表)中引用的列值,在主表(父表)中必须存在,维护数据的 “引用完整性”。
比如:Score
表(子表)的StudentID
列,必须关联Student
表(父表)的ID
列,防止出现 “成绩表引用了不存在的学生 ID” 的情况。 - 语法示例:
-- 先建主表 Student CREATE TABLE Student ( ID INT PRIMARY KEY, Name VARCHAR(50) ); -- 再建从表 Score,定义外键关联 Student 表的 ID CREATE TABLE Score ( ScoreID INT PRIMARY KEY, StudentID INT, Score INT, CONSTRAINT FK_Score_StudentID FOREIGN KEY (StudentID) REFERENCES Student(ID) -- FK_Score_StudentID 是自定义约束名,FK 是外键缩写;REFERENCES 指定关联的主表和列 );
- 特点:
- 外键列的数据类型必须和主表关联列一致(比如主表
ID
是INT
,外键列也得是INT
)。 - 删除或更新主表数据时,需注意 “级联操作”(可设置
ON DELETE CASCADE
或ON UPDATE CASCADE
,让从表数据自动同步变更,避免破坏关联 )。
- 外键列的数据类型必须和主表关联列一致(比如主表
4. 检查约束(CHECK
)
- 作用:
自定义规则,限制列值必须满足某个条件,保证数据符合业务逻辑。
比如:学生成绩必须在0 - 100
之间,性别只能是男
或女
等。 - 语法示例:
CREATE TABLE Student ( ID INT PRIMARY KEY, Name VARCHAR(50), Gender CHAR(2), Score INT, -- 直接定义,检查 Gender 只能是 '男' 或 '女' CONSTRAINT CK_Student_Gender CHECK (Gender IN ('男', '女')), -- 自定义约束名 CK_Student_Score,检查 Score 在 0-100 之间 CONSTRAINT CK_Student_Score CHECK (Score >= 0 AND Score <= 100) );
- 特点:
- 条件可以是简单的逻辑判断(如范围、枚举),也可以是复杂的表达式(但太复杂会影响性能,需谨慎 )。
- SQL Server 对
CHECK
约束的 “严格性” 有特殊点:默认情况下,CHECK
约束不阻止NULL
值(除非列本身设了NOT NULL
)。比如列设了CHECK (Score >= 0)
,但Score
允许NULL
,就可以插入NULL
值。
5. 默认约束(DEFAULT
)
- 作用:
给列设置 “默认值”,当插入数据时,如果没显式指定该列的值,数据库自动填入默认值。
比如:学生表的 “入学时间”,默认填当前系统时间;“状态” 列默认填正常
。 - 语法示例:
CREATE TABLE Student ( ID INT PRIMARY KEY, Name VARCHAR(50), EnrollDate DATETIME DEFAULT GETDATE(), -- 调用 GETDATE() 函数,默认填当前时间 Status VARCHAR(10) DEFAULT '正常' -- 默认填 '正常' );
- 特点:
- 默认值可以是固定值(如
'正常'
),也可以是函数(如GETDATE()
获取当前时间 )。 - 仅在 “插入数据时未显式赋值” 时触发,更新数据时不会自动重置为默认值。
- 默认值可以是固定值(如
二、约束的创建、修改与删除
1. 创建约束的时机
- 建表时直接定义:最常见,在
CREATE TABLE
语句里,和列定义写在一起(如前面的示例 )。 - 建表后添加约束:用
ALTER TABLE
语句,适合表已存在,后续补充约束的场景。
示例(给已存在的Student
表加唯一约束):ALTER TABLE Student ADD CONSTRAINT UQ_Student_Phone UNIQUE (Phone);
2. 修改约束
SQL Server 没有直接 “修改约束” 的语法,通常的做法是:
- 删除旧约束 → 重新创建新约束。
示例(修改CHECK
约束,把成绩范围从0-100
改成0-150
):-- 1. 删除旧约束 ALTER TABLE Student DROP CONSTRAINT CK_Student_Score; -- 2. 重新创建新约束 ALTER TABLE Student ADD CONSTRAINT CK_Student_Score CHECK (Score >= 0 AND Score <= 150);
3. 删除约束
用 ALTER TABLE ... DROP CONSTRAINT ...
语法,指定约束名即可。
示例:
ALTER TABLE Student
DROP CONSTRAINT UQ_Student_Phone; -- 删除唯一约束 UQ_Student_Phone
三、约束的作用与价值
- 保证数据完整性:
防止无效数据(如重复学号、负数成绩、不存在的外键关联等 )进入数据库,让数据 “干净、可信”。 - 简化业务逻辑:
把数据校验规则交给数据库约束,不用在应用程序里反复判断,减少代码冗余。 - 提升查询效率:
主键、唯一约束自动创建的索引,能加速查询,让数据库操作更高效。 - 文档化规则:
约束定义清晰记录了数据规则,其他人维护数据库时,能快速理解表的设计意图。
四、约束使用的注意事项
- 命名规范:
约束名最好带前缀(如PK_
、UQ_
、FK_
、CK_
),方便区分类型,也便于后续管理(比如PK_StudentID
一看就知道是学生表的主键 )。 - 性能平衡:
过多、过复杂的约束(尤其是CHECK
约束和复合外键 )会增加数据库的计算负担,插入、更新数据时变慢。需在 “数据完整性” 和 “性能” 之间找平衡。 - 外键的级联操作:
删除或更新主表数据时,若不设置级联(ON DELETE CASCADE
等 ),可能因外键关联报错。需根据业务需求,决定是否自动同步删除 / 更新从表数据。
五、练习
练习一:基础约束综合题(建表 + 主键 / 唯一 / 检查 / 默认)
题目:
创建 Employee
表,包含以下字段和约束:
EmpID
:整数,主键,自增(从 1 开始,每次 +1)EmpName
:长度 50 的字符串,非空,且值唯一Gender
:固定长度字符串(2 位),只能是男
或女
Salary
:数值类型(总长度 10,小数位 2),范围3000-50000
HireDate
:日期类型,默认值为系统当前日期
答案 & 解析:
CREATE TABLE Employee (
EmpID INT IDENTITY(1,1) PRIMARY KEY, -- 主键 + 自增(1 开始,步长 1)
EmpName VARCHAR(50) NOT NULL UNIQUE, -- 非空 + 唯一约束
Gender CHAR(2) NOT NULL,
Salary DECIMAL(10,2) NOT NULL,
HireDate DATE DEFAULT GETDATE(), -- 默认当前日期
CONSTRAINT CK_Employee_Gender CHECK (Gender IN ('男', '女')), -- 检查性别
CONSTRAINT CK_Employee_Salary CHECK (Salary >= 3000 AND Salary <= 50000) -- 检查工资范围
);
考点:
- 主键自增(
IDENTITY
)、唯一约束(UNIQUE
)的组合 - 检查约束(
CHECK
)的多条件应用 - 默认约束(
DEFAULT
)结合系统函数(GETDATE()
)
练习二:外键约束与级联操作
题目:
现有 Department
表(主表)和 Employee
表(从表),要求:
Department
表结构:DeptID
(主键,整数)、DeptName
(非空,唯一,长度 50)
Employee
表结构:EmpID
(主键,整数)、EmpName
(非空)、DeptID
(外键,关联Department
表的DeptID
)- 当删除
Department
表中的部门时,自动删除该部门下的所有员工(级联删除)
答案 & 解析:
-- 1. 创建主表 Department
CREATE TABLE Department (
DeptID INT PRIMARY KEY,
DeptName VARCHAR(50) NOT NULL UNIQUE
);
-- 2. 创建从表 Employee,定义外键 + 级联删除
CREATE TABLE Employee (
EmpID INT PRIMARY KEY,
EmpName VARCHAR(50) NOT NULL,
DeptID INT,
CONSTRAINT FK_Employee_DeptID
FOREIGN KEY (DeptID)
REFERENCES Department(DeptID)
ON DELETE CASCADE -- 级联删除:删部门时自动删员工
);
考点:
- 外键约束(
FOREIGN KEY
)的基本用法 - 级联操作(
ON DELETE CASCADE
)的应用场景 - 主表与从表的关联逻辑
练习三:复合约束(复合主键 + 多列唯一)
题目:
创建 CourseScore
表,记录学生课程成绩,要求:
StudentID
(长度 12 的字符串)、CourseID
(长度 6 的字符串)、Score
(数值,0-100)- 约束:
StudentID + CourseID
组合为复合主键(保证一人一门课只有一条成绩)Score
列值必须 ≥0 且 ≤100- 额外要求:同一门课的成绩,不能有重复的学生(即
CourseID + StudentID
唯一,和复合主键逻辑一致,强化理解)
答案 & 解析:
CREATE TABLE CourseScore (
StudentID NCHAR(12) NOT NULL,
CourseID NCHAR(6) NOT NULL,
Score NUMERIC(5,1) NOT NULL,
-- 复合主键:(StudentID, CourseID)
CONSTRAINT PK_CourseScore PRIMARY KEY (StudentID, CourseID),
-- 检查成绩范围
CONSTRAINT CK_CourseScore_Score CHECK (Score >= 0 AND Score <= 100),
-- 多列唯一(和复合主键效果一致,练习语法)
CONSTRAINT UQ_CourseScore UNIQUE (StudentID, CourseID)
);
考点:
- 复合主键(多列组合主键)的定义
- 多列唯一约束(
UNIQUE
用于多列) - 检查约束对数值范围的限制
练习四:约束修改与删除(进阶操作)
题目:
针对已存在的 Employee
表(结构如下),完成以下操作:
CREATE TABLE Employee (
EmpID INT PRIMARY KEY,
EmpName VARCHAR(50) NOT NULL,
Salary DECIMAL(10,2) NOT NULL,
HireDate DATE
);
- 添加约束:
EmpName
列值唯一Salary
列范围5000-60000
HireDate
列默认值为系统当前日期
- 删除
EmpName
列的唯一约束(假设约束名为UQ_Employee_EmpName
) - 修改
Salary
列的检查约束,将范围改为4000-50000
(需先删旧约束,再建新约束)
答案 & 解析:
-- 1. 添加约束(分步执行)
ALTER TABLE Employee
ADD CONSTRAINT UQ_Employee_EmpName UNIQUE (EmpName); -- 唯一约束
ALTER TABLE Employee
ADD CONSTRAINT CK_Employee_Salary CHECK (Salary >= 5000 AND Salary <= 60000); -- 检查约束
ALTER TABLE Employee
ADD CONSTRAINT DF_Employee_HireDate DEFAULT GETDATE() FOR HireDate; -- 默认约束
-- 2. 删除唯一约束
ALTER TABLE Employee
DROP CONSTRAINT UQ_Employee_EmpName;
-- 3. 修改检查约束(先删旧,再建新)
ALTER TABLE Employee
DROP CONSTRAINT CK_Employee_Salary;
ALTER TABLE Employee
ADD CONSTRAINT CK_Employee_Salary CHECK (Salary >= 4000 AND Salary <= 50000);
考点:
- 用
ALTER TABLE
动态添加 / 删除约束 - 检查约束的 “修改” 逻辑(需先删除旧约束)
- 默认约束的添加语法(
DEFAULT ... FOR 列名
)
练习五:约束冲突场景判断(选择题变形)
题目:
现有 Student
表结构:
CREATE TABLE Student (
SID INT PRIMARY KEY,
SName VARCHAR(50) NOT NULL UNIQUE,
Age INT CHECK (Age >= 18 AND Age <= 30),
Gender CHAR(2) CHECK (Gender IN ('男', '女'))
);
判断以下插入操作是否会触发约束冲突(填 “是” 或 “否”):
操作语句 | 是否冲突 | 冲突类型(主键 / 唯一 / 检查 / 外键) |
---|---|---|
INSERT INTO Student VALUES (1, '张三', 20, '男'); |
否 | - |
INSERT INTO Student VALUES (1, '李四', 22, '女'); |
是 | 主键冲突(SID 重复) |
INSERT INTO Student VALUES (2, '张三', 21, '男'); |
是 | 唯一约束冲突(SName 重复) |
INSERT INTO Student VALUES (3, '王五', 17, '男'); |
是 | 检查约束冲突(Age < 18) |
INSERT INTO Student VALUES (4, '赵六', 25, '未知'); |
是 | 检查约束冲突(Gender 不在允许值) |
考点:
- 各类约束的冲突场景判断(主键重复、唯一列重复、检查条件不满足等)
- 实际操作中对约束生效逻辑的理解