SQL基础⑪ | 约束

0 序言

本文主要围绕数据库约束讲起,涵盖约束的基本概念分类及各类约束(非空、唯一、主键、自增列、外键、CHECK、DEFAULT)的作用、特点、操作方法与示例。

通过学习,可掌握约束的定义逻辑、使用场景及实操技能,理解其在保证数据完整性中的关键作用。

1 约束(constraint)概述

1.1 为什么需要约束

从数据完整性来说,

  • 数据完整性(Data Integrity)指数据的精确性(Accuracy)和可靠性(Reliability),目的是防止数据库中存在不符合语义规定的数据,避免错误信息导致无效操作或错误。

我们可以从四方面保证数据完整性:

  • 实体完整性:如同一表中不能有两条完全相同无法区分的记录。
  • 域完整性:如年龄范围0-120,性别范围"男/女"。
  • 引用完整性:如员工所在部门需在部门表中存在。
  • 用户自定义完整性:如用户名唯一、密码不能为空等。

1.2 什么是约束

约束是表级的强制规定,可在创建表时(通过CREATE TABLE语句)或表创建后(通过ALTER TABLE语句)设置。

1.3 约束的分类

1.3.1 按约束数据列的限制

  • 单列约束:每个约束仅约束一列。
  • 多列约束:每个约束可约束多列数据。

1.3.2 按约束的作用范围

  • 列级约束 :作用于单个列,跟在列定义后。
    • 支持的约束类型:语法上都支持,但外键无效果。
    • 是否可起约束名:不可以。
  • 表级约束 :作用于多个列,单独定义在所有列下方。
    • 支持的约束类型:默认和非空不支持,其他支持。
    • 是否可起约束名:可以(主键无效果)。

1.3.3 按约束的作用

  • NOT NULL:非空约束,规定字段不能为空。
  • UNIQUE:唯一约束,规定字段在表中唯一。
  • PRIMARY KEY:主键约束(非空且唯一)。
  • FOREIGN KEY:外键约束。
  • CHECK:检查约束。
  • DEFAULT:默认值约束。

1.4 查看表的约束

通过查询系统库信息查看:

sql 复制代码
SELECT * FROM information_schema.table_constraints WHERE table_name = '表名称';

这里就可以看到表约束详情。

2 非空约束(NOT NULL)

2.1 作用

限定某个字段/列的值不允许为空。

关键字为:NOT NULL

2.2 特点

  • 默认情况下,所有类型的值都可为NULL(包括INT、FLOAT等)。
  • 非空约束仅作用于单个列,一个表可有多列设非空约束。
  • 空字符串''≠NULL,0≠NULL。

2.3 添加非空约束

2.3.1 建表时添加

sql 复制代码
CREATE TABLE 表名称(
  字段名 数据类型,
  字段名 数据类型 NOT NULL,
  字段名 数据类型 NOT NULL
);

-- 示例
CREATE TABLE emp(
  NAME VARCHAR(20) NOT NULL,
  sex CHAR NULL
);

CREATE TABLE student(
  sid INT,
  sname VARCHAR(20) NOT NULL,
  tel CHAR(11),
  cardid CHAR(18) NOT NULL
);
  • 插入数据示例:
sql 复制代码
-- 成功(所有非空字段均赋值)
INSERT INTO student VALUES(1,'张三','13710011002','110222198912032545');
-- 失败(cardid为NULL,违反非空约束)
INSERT INTO student VALUES(2,'李四','13710011002',NULL);
-- 成功(tel允许为空)
INSERT INTO student VALUES(2,'李四',NULL,'110222198912032546');
-- 失败(sname为NULL,违反非空约束)
INSERT INTO student VALUES(3,NULL,NULL,'110222198912032547');

可以自己动手试一下,

规则出错会弹出错误信息,

但只要满足建表是设定好的约束条件即可。

2.3.2 建表后添加

sql 复制代码
ALTER TABLE 表名称 MODIFY 字段名 数据类型 NOT NULL;

-- 示例
ALTER TABLE emp MODIFY sex VARCHAR(30) NOT NULL;
ALTER TABLE student MODIFY sname VARCHAR(20) NOT NULL;

这样就可以在已有的表格进行约束添加。

2.4 删除非空约束

sql 复制代码
-- 方式1:显式指定NULL
ALTER TABLE 表名称 MODIFY 字段名 数据类型 NULL;
-- 方式2:不指定NOT NULL(默认允许为空)
ALTER TABLE 表名称 MODIFY 字段名 数据类型;

-- 示例
ALTER TABLE emp MODIFY sex VARCHAR(30) NULL;
ALTER TABLE emp MODIFY NAME VARCHAR(15) DEFAULT 'abc' NULL;

3 唯一约束(UNIQUE)

限制某个字段/列的值在整个表中唯一

这里核心就是唯一,关键字为:UNIQUE

3.1 特点

  • 一个表可有多个唯一约束
  • 可约束单个列或多列组合(复合唯一)。
  • 允许列值为空(可多个NULL)。
  • 未命名时,默认与列名相同(单列)或与组合列中第一个列名相同(多列)。
  • MySQL会为唯一约束列自动创建唯一索引

3.2 添加唯一约束

3.2.1 建表时添加

sql 复制代码
-- 列级约束方式
CREATE TABLE 表名称(
  字段名 数据类型,
  字段名 数据类型 UNIQUE,
  字段名 数据类型 UNIQUE KEY
);

-- 表级约束方式
CREATE TABLE 表名称(
  字段名 数据类型,
  字段名 数据类型,
  字段名 数据类型,
  CONSTRAINT 约束名 UNIQUE KEY(字段名)
);

-- 示例
CREATE TABLE student(
  sname VARCHAR(20),
  tel CHAR(11) UNIQUE,
  cardid CHAR(18) UNIQUE KEY
);

CREATE TABLE t_course(
  cid INT UNIQUE,
  cname VARCHAR(100) UNIQUE,
  description VARCHAR(200)
);

-- 复合唯一(用户名和密码组合唯一)
CREATE TABLE USER(
  id INT NOT NULL,
  NAME VARCHAR(25),
  PASSWORD VARCHAR(16),
  CONSTRAINT uk_name_pwd UNIQUE(NAME,PASSWORD)
);
  • 插入数据示例(违反唯一约束):
sql 复制代码
-- 成功
INSERT INTO student VALUES(1,'张三','13710011002','101223199012015623');
INSERT INTO student VALUES(2,'李四','13710011003','101223199012015624');
-- 失败(cardid重复)
INSERT INTO student VALUES(3,'王五','13710011004','101223199012015624');
-- 失败(tel重复)
INSERT INTO student VALUES(3,'王五','13710011003','101223199012015625');

后两个语句就会报错,为什么呢?

就是因为它违反了唯一约束,所以系统就会报错。

3.2.2 建表后添加

sql 复制代码
-- 方式1:表级约束添加
ALTER TABLE 表名称 ADD UNIQUE KEY(字段列表);
-- 方式2:列级约束添加
ALTER TABLE 表名称 MODIFY 字段名 字段类型 UNIQUE;

-- 示例
ALTER TABLE USER ADD UNIQUE(NAME,PASSWORD);
ALTER TABLE USER MODIFY NAME VARCHAR(20) UNIQUE;

-- 为student表的tel和cardid添加唯一约束
CREATE TABLE student(
  sid INT PRIMARY KEY,
  sname VARCHAR(20),
  tel CHAR(11),
  cardid CHAR(18)
);
ALTER TABLE student ADD UNIQUE KEY(tel);
ALTER TABLE student ADD UNIQUE KEY(cardid);

同样的道理,就是在已有的表格里添加唯一约束。

3.3 复合唯一约束

  • 多列组合的值唯一,语法:
sql 复制代码
CREATE TABLE 表名称(
  字段名 数据类型,
  字段名 数据类型,
  字段名 数据类型,
  UNIQUE KEY(字段列表) -- 字段间用逗号分隔
);

-- 示例(选课表:学生ID和课程ID组合唯一,避免重复选课)
CREATE TABLE student(
  sid INT,
  sname VARCHAR(20),
  tel CHAR(11) UNIQUE KEY,
  cardid CHAR(18) UNIQUE KEY
);

CREATE TABLE course(
  cid INT,
  cname VARCHAR(20)
);

CREATE TABLE student_course(
  id INT,
  sid INT,
  cid INT,
  score INT,
  UNIQUE KEY(sid,cid) -- 复合唯一
);

-- 插入数据
INSERT INTO student VALUES(1,'张三','13710011002','101223199012015623');
INSERT INTO student VALUES(2,'李四','13710011003','101223199012015624');
INSERT INTO course VALUES(1001,'Java'),(1002,'MySQL');
INSERT INTO student_course VALUES(1,1,1001,89),(2,1,1002,90),(3,2,1001,88),(4,2,1002,56);

-- 失败(sid=1和cid=1001的组合已存在)
INSERT INTO student_course VALUES(5,1,1001,88);

3.4 删除唯一约束

  • 需通过删除唯一索引实现,步骤:
sql 复制代码
-- 1. 查看约束名
SELECT * FROM information_schema.table_constraints WHERE table_name = '表名';
-- 2. 删除唯一索引(索引名即约束名)
ALTER TABLE 表名称 DROP INDEX 约束名;

-- 示例
ALTER TABLE USER DROP INDEX uk_name_pwd;

-- 查看表索引(辅助确认)
SHOW INDEX FROM 表名称;

比如说这张图片,就能看到具体的约束了。

再比如说,我现在想要删除tel这个约束,

运行程序后,

4 主键约束(PRIMARY KEY)

唯一标识表中的一行记录。

关键字为:PRIMARY KEY

4.1 特点

  • 相当于唯一约束+非空约束:不允许重复,不允许为空。
  • 一个表最多只能有一个主键约束(可单列或多列组合)。
  • 复合主键:多列组合唯一,且各列均不允许为空。
  • MySQL主键名固定为PRIMARY,自定义名称无效。
  • 系统会为-primary key约束列创建主键索引,删除主键约束时索引自动删除。
  • 不建议修改主键值,可能破坏数据完整性

4.2 添加主键约束

4.2.1 建表时添加

sql 复制代码
-- 列级约束方式
CREATE TABLE 表名称(
  字段名 数据类型 PRIMARY KEY,
  字段名 数据类型
);

-- 表级约束方式
CREATE TABLE 表名称(
  字段名 数据类型,
  字段名 数据类型,
  CONSTRAINT 约束名 PRIMARY KEY(字段名)
);

-- 示例
CREATE TABLE temp(
  id INT PRIMARY KEY,
  name VARCHAR(20)
);

-- 列级约束+自增
CREATE TABLE emp4(
  id INT PRIMARY KEY AUTO_INCREMENT,
  NAME VARCHAR(20)
);

-- 表级约束示例
CREATE TABLE emp5(
  id INT NOT NULL AUTO_INCREMENT,
  NAME VARCHAR(20),
  pwd VARCHAR(15),
  CONSTRAINT emp5_id_pk PRIMARY KEY(id)
);
  • 插入数据示例(违反主键约束):
sql 复制代码
-- 成功
INSERT INTO temp VALUES(1,'张三'),(2,'李四');
-- 失败(id=1重复)
INSERT INTO temp VALUES(1,'王五');
-- 失败(id为NULL)
INSERT INTO temp VALUES(NULL,'李琦');

4.2.2 建表后添加

sql 复制代码
ALTER TABLE 表名称 ADD PRIMARY KEY(字段列表); -- 字段列表可为单列或多列(复合主键)

-- 示例
ALTER TABLE emp5 ADD PRIMARY KEY(NAME,pwd); -- 复合主键

4.3 复合主键

  • 多列组合唯一标识记录,语法:
sql 复制代码
CREATE TABLE 表名称(
  字段名 数据类型,
  字段名 数据类型,
  字段名 数据类型,
  PRIMARY KEY(字段名1,字段名2)
);

-- 示例(选课表:sid和cid组合为主键)
CREATE TABLE student(
  sid INT PRIMARY KEY,
  sname VARCHAR(20)
);

CREATE TABLE course(
  cid INT PRIMARY KEY,
  cname VARCHAR(20)
);

CREATE TABLE student_course(
  sid INT,
  cid INT,
  score INT,
  PRIMARY KEY(sid,cid) -- 复合主键
);

-- 插入数据
INSERT INTO student VALUES(1,'张三'),(2,'李四');
INSERT INTO course VALUES(1001,'Java'),(1002,'MySQL');
INSERT INTO student_course VALUES(1,1001,89),(1,1002,90),(2,1001,88),(2,1002,56);

-- 失败(sid=1和cid=1001的组合已存在)
INSERT INTO student_course VALUES(1,1001,100);

道理相同,

这里就不重复演示了。

4.4 删除主键约束

sql 复制代码
ALTER TABLE 表名称 DROP PRIMARY KEY;

-- 示例
ALTER TABLE student DROP PRIMARY KEY;
ALTER TABLE emp5 DROP PRIMARY KEY;

这里要注意,删除后,非空约束可能仍存在(若原主键列有非空设置)。

5 自增列(AUTO_INCREMENT)

使某个字段的值自动递增。

关键字为:AUTO_INCREMENT

5.1 特点和要求

  • 一个表最多一个自增列
  • 自增列必须是键列(主键或唯一键)。
  • 数据类型必须为整数类型。
  • 若指定0或NULL,值为当前最大值+1;手动指定具体值则直接赋值。

5.2 添加自增约束

5.2.1 建表时添加

sql 复制代码
CREATE TABLE 表名称(
  字段名 数据类型 PRIMARY KEY AUTO_INCREMENT,
  字段名 数据类型 UNIQUE KEY NOT NULL,
  字段名 数据类型 UNIQUE KEY,
  字段名 数据类型 NOT NULL DEFAULT 默认值
);

-- 示例
CREATE TABLE employee(
  eid INT PRIMARY KEY AUTO_INCREMENT,
  ename VARCHAR(20)
);

5.2.2 建表后添加

sql 复制代码
ALTER TABLE 表名称 MODIFY 字段名 数据类型 AUTO_INCREMENT;

-- 示例
CREATE TABLE employee(
  eid INT PRIMARY KEY,
  ename VARCHAR(20)
);
ALTER TABLE employee MODIFY eid INT AUTO_INCREMENT;

5.3 删除自增约束

sql 复制代码
ALTER TABLE 表名称 MODIFY 字段名 数据类型; -- 移除AUTO_INCREMENT

-- 示例
ALTER TABLE employee MODIFY eid INT;

5.4 MySQL 8.0特有-自增变量的持久化

  • MySQL 5.7及之前:自增主键值若大于max(primary key)+1,重启后会重置为max(primary key)+1,可能导致冲突。
  • MySQL 8.0:将自增计数器持久化到重做日志,重启后根据日志初始化,避免重置。
  • 示例(MySQL 5.7与8.0对比):
sql 复制代码
-- 创建表
CREATE TABLE test1(id INT PRIMARY KEY AUTO_INCREMENT);
-- 插入数据
INSERT INTO test1 VALUES(0),(0),(0),(0); -- id为1,2,3,4
DELETE FROM test1 WHERE id=4; -- 删除id=4
INSERT INTO test1 VALUES(0); -- MySQL 5.7: id=5;8.0: id=5
DELETE FROM test1 WHERE id=5;
-- 重启数据库后插入
INSERT INTO test1 VALUES(0); -- MySQL 5.7: id=4;8.0: id=6

从这里对比可以看出,

MySQL 5.7 与 8.0 自增主键机制是不相同的,

5.7 中自增计数器存内存,重启后重置为表最大主键 +1,可能致主键不连续、引发冲突;

8.0 则将其持久化到重做日志,重启恢复状态保连续性。

6 外键约束(FOREIGN KEY)

保证某个表的字段引用的完整性(如员工部门需在部门表中存在)。

关键字为FOREIGN KEY

6.1 主表和从表(父表和子表)

  • 主表(父表):被引用的表。
  • 从表(子表):引用主表的表。

比如说,部门表是主表,员工表是从表;学生表和课程表是主表,选课表是从表。

6.2 特点

  • 从表外键列必须引用主表的主键唯一约束列(被引用值需唯一)。
  • 未命名时,默认生成外键名(如student_ibfk_1),可自定义。
  • 创建顺序先主表,后从表
  • 删除顺序先从表(或先删外键约束),后主表
  • 主表记录被从表引用时,不可直接删除(需先删从表依赖数据)。
  • 一个表可建立多个外键约束
  • 从表外键列与主表被引用列名可不同,但数据类型和逻辑意义必须一致。

6.3 添加外键约束

6.3.1 建表时添加

sql 复制代码
-- 创建主表
CREATE TABLE 主表名称(
  字段1 数据类型 PRIMARY KEY,
  字段2 数据类型
);

-- 创建从表(含外键)
CREATE TABLE 从表名称(
  字段1 数据类型 PRIMARY KEY,
  字段2 数据类型,
  CONSTRAINT 外键约束名 FOREIGN KEY(从表字段) REFERENCES 主表名(主表字段)
);

-- 示例
CREATE TABLE dept( -- 主表
  did INT PRIMARY KEY, -- 部门编号
  dname VARCHAR(50) -- 部门名称
);

CREATE TABLE emp( -- 从表
  eid INT PRIMARY KEY, -- 员工编号
  ename VARCHAR(5), -- 员工姓名
  deptid INT, -- 部门ID(外键)
  FOREIGN KEY (deptid) REFERENCES dept(did)
);

6.3.2 建表后添加

sql 复制代码
ALTER TABLE 从表名 ADD [CONSTRAINT 约束名] FOREIGN KEY(从表字段) REFERENCES 主表名(主表字段) [ON UPDATE 等级] [ON DELETE 等级];

-- 示例
ALTER TABLE emp ADD CONSTRAINT emp_dept_id_fk FOREIGN KEY(deptid) REFERENCES dept(did);

6.4 常见错误情况

  • 主表被引用列非键列ERROR 1215 (HY000): Cannot add foreign key constraint
  • 数据类型不一致ERROR 1215 (HY000): Cannot add foreign key constraint
  • 从表插入主表不存在的值ERROR 1452 (23000): ... foreign key constraint fails

6.5 约束等级

  • Cascade:主表更新/删除,从表同步更新/删除匹配记录。
  • Set null:主表更新/删除,从表匹配记录列设为NULL(外键列不能为NOT NULL)。
  • No action/Restrict:若从表有匹配记录,不允许主表更新/删除(默认等级)。
  • Set default:主表变更,从表外键列设为默认值(InnoDB不支持)。
  • 示例(ON UPDATE CASCADE ON DELETE SET NULL):
sql 复制代码
CREATE TABLE dept(
  did INT PRIMARY KEY,
  dname VARCHAR(50)
);

CREATE TABLE emp(
  eid INT PRIMARY KEY,
  ename VARCHAR(5),
  deptid INT,
  FOREIGN KEY (deptid) REFERENCES dept(did) ON UPDATE CASCADE ON DELETE SET NULL
);

-- 插入数据
INSERT INTO dept VALUES(1001,'教学部'),(1002,'财务部'),(1003,'咨询部');
INSERT INTO emp VALUES(1,'张三',1001),(2,'李四',1001),(3,'王五',1002);

-- 主表更新,从表同步(deptid=1002→1004,emp中王五的deptid变为1004)
UPDATE dept SET did=1004 WHERE did=1002;

-- 主表删除,从表设为NULL(删除deptid=1001,emp中张三、李四的deptid变为NULL)
DELETE FROM dept WHERE did=1001;

前面的4 5小结,理解难度不算很高,

思路也比较清晰,

这里的示例我用图片给大家解释一下,

这个理解了,后续的7、8小结理解起来会更清洗,也更容易一些。




6.6 删除外键约束

sql 复制代码
-- 1. 查看外键约束名
SELECT * FROM information_schema.table_constraints WHERE table_name = '从表名';
-- 2. 删除外键约束
ALTER TABLE 从表名 DROP FOREIGN KEY 约束名;
-- 3. 查看索引名(外键自动创建的索引)
SHOW INDEX FROM 从表名;
-- 4. 删除索引
ALTER TABLE 从表名 DROP INDEX 索引名;

-- 示例
ALTER TABLE emp DROP FOREIGN KEY emp_ibfk_1;
ALTER TABLE emp DROP INDEX deptid;

6.7 开发场景与规范

  • 非必须建立外键:外键会增加系统开销,高并发场景可能不适用,可在应用层保证数据一致性。

7 CHECK 约束

检查字段值是否符合指定条件(如范围、枚举等)。

关键字为:CHECK

7.1 示例

sql 复制代码
-- MySQL 8.0有效
CREATE TABLE employee(
  eid INT,
  ename VARCHAR(5),
  gender CHAR CHECK (gender IN ('男','女')) -- 性别只能是男/女
);

CREATE TABLE temp(
  id INT AUTO_INCREMENT,
  NAME VARCHAR(20),
  age INT CHECK(age > 20), -- 年龄大于20
  PRIMARY KEY(id)
);

8 DEFAULT 约束

为字段指定默认值,插入数据时未显式赋值则使用默认值。

关键字为:DEFAULT

8.1 添加默认值约束

8.1.1 建表时添加

sql 复制代码
CREATE TABLE 表名称(
  字段名 数据类型 PRIMARY KEY,
  字段名 数据类型 NOT NULL DEFAULT 默认值,
  字段名 数据类型 DEFAULT 默认值
);

-- 示例
CREATE TABLE employee(
  eid INT PRIMARY KEY,
  ename VARCHAR(20) NOT NULL,
  gender CHAR DEFAULT '男', -- 默认性别为男
  tel CHAR(11) NOT NULL DEFAULT '' -- 默认电话为空字符串
);

8.1.2 建表后添加

sql 复制代码
ALTER TABLE 表名称 MODIFY 字段名 数据类型 DEFAULT 默认值;
-- 保留非空约束时
ALTER TABLE 表名称 MODIFY 字段名 数据类型 DEFAULT 默认值 NOT NULL;

-- 示例
CREATE TABLE employee(
  eid INT PRIMARY KEY,
  ename VARCHAR(20),
  gender CHAR,
  tel CHAR(11) NOT NULL
);
-- 为gender添加默认值
ALTER TABLE employee MODIFY gender CHAR DEFAULT '男';
-- 为tel添加默认值并保留非空
ALTER TABLE employee MODIFY tel CHAR(11) DEFAULT '' NOT NULL;

8.2 删除默认值约束

sql 复制代码
-- 移除DEFAULT,保留非空
ALTER TABLE 表名称 MODIFY 字段名 数据类型 NOT NULL;
-- 移除DEFAULT,允许为空
ALTER TABLE 表名称 MODIFY 字段名 数据类型;

-- 示例
ALTER TABLE employee MODIFY gender CHAR; -- 移除gender的默认值
ALTER TABLE employee MODIFY tel CHAR(11) NOT NULL; -- 移除tel的默认值,保留非空

9 小结

本文详细介绍了数据库约束的核心知识,包括约束的定义分类及各类约束(非空、唯一、主键、自增、外键、CHECK、DEFAULT)的操作方法与示例。

约束是保证数据完整性的关键机制,通过合理使用约束,可有效防止无效或错误数据进入数据库

不同约束适用于不同场景,如主键用于唯一标识记录,外键保证引用完整性,默认值简化数据插入

同时,需注意数据库版本差异(如MySQL 8.0自增持久化)及开发规范(如阿里不建议使用外键),在实际应用中灵活选择合适的约束策略。

相关推荐
Jacob02345 分钟前
很多数据分析师写对了 SQL,却忽略了这件更重要的事
后端·sql·数据分析
种树达人6 分钟前
数据库常用DDL语言
java·数据库·oracle
Gauss松鼠会16 分钟前
华为云DRS实现Oracle到GaussDB数据库迁移的全流程技术方案
数据库·sql·安全·华为云·database·gaussdb
Chef_Chen1 小时前
从0开始学习R语言--Day57--SCAD模型
开发语言·学习·r语言
停走的风1 小时前
Yolo底层原理学习(V1~V3)(第一篇)
人工智能·深度学习·神经网络·学习·yolo
可涵不会debug1 小时前
AI浪潮涌,数据库“融合智能”奏响产业新乐章
数据库·人工智能
超浪的晨1 小时前
Java 单元测试详解:从入门到实战,彻底掌握 JUnit 5 + Mockito + Spring Boot 测试技巧
java·开发语言·后端·学习·单元测试·个人开发
wei_shuo1 小时前
融合与智能:AI 浪潮驱动下数据库的多维度进化与产业格局重塑新范式
数据库·人工智能·金仓数据库
Fireworkitte1 小时前
Java 常用数据库详解
java·数据库
lifallen2 小时前
Flink堆状态后端核心:CopyOnWriteStateMap解析
java·大数据·数据结构·数据库·算法·flink·哈希算法