MySQL 表约束是 “数据质量的守护者”—— 它在数据类型之外,从业务逻辑层面强制规范数据,避免脏数据(如空值、重复值、无效关联)的产生。很多开发者初期忽略约束,导致后期出现 “用户手机号重复”“订单关联不存在的商品” 等业务故障。本文将系统讲解 MySQL 的 9 种核心约束(空属性、默认值、主键、自增长等),结合实战案例解析每种约束的适用场景与配置方法,帮你在表设计阶段就筑牢数据安全防线。
一、约束的本质:为什么需要约束?
数据类型(如int、varchar)仅能限制数据的 “格式”(如数字、字符串长度),但无法满足业务逻辑需求(如 “用户名不能为空”“订单号必须唯一”)。约束的核心价值是从业务层面定义数据的 “合法性规则”,让数据库主动拒绝不符合规则的数据,减少程序端的校验逻辑。
例如:
若 “用户表” 的email字段无约束,可能出现重复邮箱,导致登录冲突;
若 “订单表” 的goods_id无约束,可能关联不存在的商品,导致业务逻辑异常。
MySQL 的核心约束包括:null/not null(空属性)、default(默认值)、comment(列描述)、zerofill(零填充)、primary key(主键)、auto_increment(自增长)、unique key(唯一键)、foreign key(外键),下文逐一详解。
二、基础约束:空属性与默认值
基础约束是表设计的 “底线规则”,确保数据不会出现 “无意义空值” 或 “重复冗余配置”。
2.1 空属性(null/not null):拒绝无意义空值
null表示 “数据未知或不存在”,但很多业务字段不允许为空(如用户名、订单金额),not null约束强制字段必须有值,避免数据无法参与运算(如1 + null结果为null)。
实战案例:创建 “班级表”,强制班级名和教室不为空
-- 班级名和教室均不允许为空
CREATE TABLE myclass (
class_name VARCHAR(20) NOT NULL, -- 班级名不能为空
class_room VARCHAR(10) NOT NULL -- 教室不能为空
);
-- 插入数据:未指定class_room,报错(违反not null约束)
INSERT INTO myclass (class_name) VALUES ('class1');
-- 错误:ERROR 1364 (HY000): Field 'class_room' doesn't have a default value
-- 插入合法数据(两个字段均赋值)
INSERT INTO myclass VALUES ('class1', '301教室');
设计建议:
业务核心字段(如用户名、手机号、订单号)必须加not null;
非核心字段(如用户备注、地址补充)可保留null(默认),但建议用default设置默认值(如空字符串),避免null值影响查询(如where remark is not null)。
2.2 默认值(default):减少重复赋值
当某字段频繁出现固定值(如 “性别默认男”“年龄默认 0”),default约束可自动填充默认值,减少插入数据时的重复代码。
实战案例:创建 “用户表”,设置年龄和性别默认值
CREATE TABLE user (
name VARCHAR(20) NOT NULL, -- 用户名不能为空
age TINYINT UNSIGNED DEFAULT 0, -- 年龄默认0(无年龄信息时)
sex CHAR(2) DEFAULT '男' -- 性别默认男
);
-- 插入数据:仅指定name,age和sex自动用默认值
INSERT INTO user (name) VALUES ('zhangsan');
-- 查询结果:age=0,sex=男
SELECT * FROM user;
-- 输出:name=zhangsan, age=0, sex=男
注意事项:
default仅在 “插入时未赋值” 时生效,若显式赋值null(且字段允许null),会覆盖默认值;
not null与default通常不同时使用(default已确保字段有值,无需not null强制),但需确保默认值符合业务逻辑(如age default 0适合 “未知年龄” 场景)。
三、辅助约束:列描述与零填充
辅助约束不影响数据合法性,主要用于 “提升表的可读性” 和 “格式化输出”,属于表设计的 “优化项”。
3.1 列描述(comment):给字段加 “说明书”
comment用于添加字段的业务说明(如 “用户身份证号,唯一标识”),无实际功能,但能帮助团队理解字段含义,减少沟通成本。
实战案例:创建 “学生表”,添加列描述
CREATE TABLE student (
id INT UNSIGNED COMMENT '学生学号(唯一)',
name VARCHAR(20) NOT NULL COMMENT '学生姓名',
age TINYINT UNSIGNED DEFAULT 0 COMMENT '学生年龄'
);
-- 查看列描述(desc无法查看,需用show create table)
SHOW CREATE TABLE student\G
-- 输出包含:`id` int(10) unsigned DEFAULT NULL COMMENT '学生学号(唯一)'
建议:
所有业务字段都应添加comment,尤其对 “抽象字段”(如status需说明 “0 = 禁用,1 = 启用”);
描述需简洁明确,避免歧义(如 “用户手机号” 而非 “联系电话”)。
3.2 零填充(zerofill):格式化数字输出
zerofill仅对整数类型生效,当数字长度小于字段定义的 “显示宽度” 时,自动在前面填充 0(仅影响显示,不改变实际存储值)。
实战案例:给 “序号” 字段添加零填充
-- 创建表,a字段为int(5) unsigned zerofill(显示宽度5,零填充)
CREATE TABLE tt3 (
a INT(5) UNSIGNED ZEROFILL,
b INT(10) UNSIGNED
);
-- 插入数据
INSERT INTO tt3 VALUES (1, 2);
-- 查询结果:a显示为00001(填充4个0),b显示为2
SELECT * FROM tt3;
-- 输出:a=00001, b=2
-- 验证实际存储值(hex函数查看,仍为1)
SELECT a, HEX(a) FROM tt3;
-- 输出:a=00001, HEX(a)=1
适用场景:
仅用于 “格式化显示”(如序号、编号需固定位数,如 “0001” 而非 “1”);
不影响数据存储和运算,若需实际存储带 0 的字符串(如 “0001”),建议用CHAR(4)而非zerofill。
四、核心约束:主键、自增长与唯一键
核心约束是表设计的 “骨架”,确保数据的 “唯一性” 和 “逻辑标识”,是业务关联的基础。
4.1 主键(primary key):表的 “唯一标识”
主键是表中唯一能确定一条记录的字段(或字段组合),具有两个核心特性:非空(not null)+ 唯一(unique),一张表仅能有一个主键。
常见主键类型:
- 单一字段主键:适合用无业务含义的整数(如
id),避免业务变更导致主键修改(如用 “身份证号” 作主键,后续身份证号变更会引发关联问题); - 复合主键:多个字段组合成主键(如 “学生选课表” 用
id+course_id,确保同一学生不重复选同一课程)。
实战案例 1:单一字段主键(用户表)
-- 创建用户表,id为主键(非空+唯一)
CREATE TABLE user (
id INT UNSIGNED PRIMARY KEY, -- 主键约束
name VARCHAR(20) NOT NULL
);
-- 插入重复id,报错(违反唯一约束)
INSERT INTO user VALUES (1, 'zhangsan');
INSERT INTO user VALUES (1, 'lisi');
-- 错误:ERROR 1062 (23000): Duplicate entry '1' for key 'PRIMARY'
实战案例 2:复合主键(学生选课表)
-- 创建选课表,id(学生号)和course_id(课程号)为复合主键
CREATE TABLE student_course (
id INT UNSIGNED,
course_id CHAR(10) NOT NULL,
score TINYINT UNSIGNED DEFAULT 60,
PRIMARY KEY (id, course_id) -- 复合主键:两个字段组合唯一
);
-- 插入重复组合,报错
INSERT INTO student_course (id, course_id) VALUES (1, 'C001');
INSERT INTO student_course (id, course_id) VALUES (1, 'C001');
-- 错误:ERROR 1062 (23000): Duplicate entry '1-C001' for key 'PRIMARY'
主键操作:追加与删除
-- 表已创建,追加主键(如给user表的id加主键)
ALTER TABLE user ADD PRIMARY KEY (id);
-- 删除主键(谨慎!会丢失唯一约束)
ALTER TABLE user DROP PRIMARY KEY;
4.2 自增长(auto_increment):自动生成唯一标识
auto_increment通常与主键搭配,让整数主键 “自动递增”(每次插入时,若未赋值,自动取当前最大值 + 1),避免手动生成唯一 ID 的麻烦。
核心特性:
仅对整数类型生效,且字段必须是索引(key一栏有值,如主键、唯一键);
一张表仅能有一个自增长字段;
自增长值从 1 开始,可通过last_insert_id()获取上次插入的自增长值。
实战案例:创建 “订单表”,自增长订单号
CREATE TABLE order (
order_id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT, -- 自增长主键
user_id INT UNSIGNED NOT NULL,
total_price DECIMAL(10,2) NOT NULL
);
-- 插入数据:无需指定order_id,自动生成
INSERT INTO order (user_id, total_price) VALUES (1, 99.99);
INSERT INTO order (user_id, total_price) VALUES (2, 199.99);
-- 查询结果:order_id自动为1、2
SELECT * FROM order;
-- 获取上次插入的自增长值(返回2)
SELECT LAST_INSERT_ID();
注意事项:
若手动赋值自增长字段,需确保值未被使用,否则会导致后续自增长值从 “最大值 + 1” 开始(如手动插入order_id=10,下次自动生成 11);
自增长值不会回滚(如插入order_id=3后删除,下次仍生成 4),避免依赖自增长值的连续性。
4.3 唯一键(unique key):多字段的唯一性约束
主键仅能有一个,但表中可能多个字段需唯一(如 “用户表” 的手机号和邮箱都需唯一),unique key解决这一问题,核心特性:允许空值(null),但非空值必须唯一(多个 null 不冲突)。
主键与唯一键的区别:
| 维度 | 主键(primary key) | 唯一键(unique key) |
|---|---|---|
| 数量限制 | 一张表仅 1 个 | 一张表可多个 |
| 空值允许 | 不允许(自动 not null) | 允许(多个 null 不冲突) |
| 核心作用 | 表的唯一标识(逻辑主键) | 业务字段的唯一性(如手机号、邮箱) |
实战案例:创建 “用户表”,手机号和邮箱唯一
CREATE TABLE user (
id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
phone CHAR(11) UNIQUE NOT NULL COMMENT '手机号,唯一',
email VARCHAR(64) UNIQUE COMMENT '邮箱,唯一(可空)'
);
-- 插入重复手机号,报错
INSERT INTO user (phone, email) VALUES ('13800138000', 'user1@example.com');
INSERT INTO user (phone, email) VALUES ('13800138000', 'user2@example.com');
-- 错误:ERROR 1062 (23000): Duplicate entry '13800138000' for key 'phone'
-- 插入多个null邮箱,不冲突(unique允许null)
INSERT INTO user (phone) VALUES ('13900139000');
INSERT INTO user (phone) VALUES ('13700137000');
五、关联约束:外键(foreign key):保障表间数据一致性
外键用于定义 “主表” 与 “从表” 的关联关系(如 “订单表” 关联 “商品表”),强制从表的关联字段值 “必须在主表中存在或为 null”,避免 “无效关联”(如订单关联不存在的商品)。
5.1 外键的核心规则:
主表:必须有主键或唯一键(外键关联的字段);
从表:外键字段的类型必须与主表关联字段一致(如主表id为int,从表class_id也需为int);
数据插入:从表的外键值必须是主表关联字段的有效值,或为 null。
实战案例:学生表(从表)关联班级表(主表)
-- 1. 创建主表(班级表):id为主键
CREATE TABLE myclass (
id INT PRIMARY KEY,
class_name VARCHAR(30) NOT NULL COMMENT '班级名'
);
-- 2. 创建从表(学生表):class_id为外键,关联myclass.id
CREATE TABLE student (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(30) NOT NULL,
class_id INT, -- 外键字段
-- 定义外键:class_id关联myclass.id
FOREIGN KEY (class_id) REFERENCES myclass (id)
);
-- 3. 插入主表数据(先有班级,再有学生)
INSERT INTO myclass VALUES (10, 'C++班'), (20, 'Java班');
-- 4. 插入合法从表数据(class_id在主表中存在)
INSERT INTO student (name, class_id) VALUES ('张三', 10), ('李四', 20);
-- 5. 插入非法数据(class_id=30在主表中不存在,报错)
INSERT INTO student (name, class_id) VALUES ('王五', 30);
-- 错误:ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails
-- 6. 插入null(允许学生暂未分配班级)
INSERT INTO student (name, class_id) VALUES ('赵六', null);
5.2 外键的优缺点:
优点:数据库自动校验关联关系,减少程序端逻辑,避免脏数据;
缺点:增加表间耦合,影响删除 / 更新效率(如删除主表数据需先删除从表关联数据),高并发场景(如电商订单)通常不使用外键,改用程序端校验。
六、实战综合案例:设计 “商店系统” 表结构
结合上述约束,设计包含 “商品表、客户表、购买表” 的商店系统,确保数据完整性与业务正确性:
-- 创建数据库
CREATE DATABASE IF NOT EXISTS bit32mall DEFAULT CHARACTER SET utf8;
USE bit32mall;
-- 1. 商品表(主表):goods_id自增长主键,商品名非空
CREATE TABLE IF NOT EXISTS goods (
goods_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '商品编号',
goods_name VARCHAR(32) NOT NULL COMMENT '商品名称',
unitprice INT NOT NULL DEFAULT 0 COMMENT '单价(单位:分)',
category VARCHAR(12) COMMENT '商品分类',
provider VARCHAR(64) NOT NULL COMMENT '供应商'
);
-- 2. 客户表:customer_id自增长主键,姓名非空,邮箱/身份证唯一
CREATE TABLE IF NOT EXISTS customer (
customer_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '客户编号',
name VARCHAR(32) NOT NULL COMMENT '客户姓名',
address VARCHAR(256) COMMENT '客户地址',
email VARCHAR(64) UNIQUE COMMENT '电子邮箱(唯一)',
sex ENUM('男', '女') NOT NULL COMMENT '性别(单选)',
card_id CHAR(18) UNIQUE COMMENT '身份证号(唯一)'
);
-- 3. 购买表(从表):关联客户表和商品表
CREATE TABLE IF NOT EXISTS purchase (
order_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '订单号',
customer_id INT COMMENT '客户编号(关联customer)',
goods_id INT COMMENT '商品编号(关联goods)',
nums INT DEFAULT 0 COMMENT '购买数量',
-- 外键关联
FOREIGN KEY (customer_id) REFERENCES customer (customer_id),
FOREIGN KEY (goods_id) REFERENCES goods (goods_id)
);
七、约束选型总原则
- 优先保证核心字段:主键、唯一键、not null 优先配置,确保数据唯一性和非空性;
- 减少不必要约束:外键在高并发场景慎用,zerofill 仅用于格式化显示;
- 结合业务设计:默认值需符合业务逻辑(如 “年龄默认 0” 而非 “默认 18”);
- 可读性优先:所有字段添加 comment,明确业务含义。
八、总结
MySQL 表约束是 “数据质量的第一道防线”,合理配置约束能大幅减少程序端的校验逻辑,避免脏数据引发的业务故障。本文讲解的 9 种约束中,primary key(主键)、unique key(唯一键)、not null(空属性)是所有表的 “必选项”,auto_increment(自增长)适合生成唯一标识,foreign key(外键)适合低并发场景的表间关联。

被折叠的 条评论
为什么被折叠?



