🎯目标:掌握数据库五大约束的使用,理解表与表之间的三种关系及建立方式
📌 一、什么是约束?
在数据库设计中,约束(Constraint)是对表中数据施加的限制规则,用于确保数据的完整性(准确性、正确性)。
使用约束的好处:
- ✅ 防止错误数据写入数据库
- ✅ 减少冗余数据(脏数据)
- ✅ 保证数据的一致性和可靠性
MySQL 中常用的约束共有五大类:
| 约束类型 | 关键字 | 作用 |
|---|---|---|
| 主键约束 | PRIMARY KEY | 唯一标识一条记录,不允许为空且不能重复 |
| 唯一约束 | UNIQUE | 保证列中数据不重复,但允许为空 |
| 非空约束 | NOT NULL | 该列不允许为空值 |
| 默认约束 | DEFAULT | 插入时若不提供值,则使用默认值填充 |
| 外键约束 | FOREIGN KEY | 维护两张表之间数据的一致性与完整性 |
🔑 二、主键约束(PRIMARY KEY)
2.1 基本概念
- 理论上,每张表都必须有一个主键作为数据的唯一标识
- 主键列的值不允许为 NULL,且不允许重复
- 主键通常用
id命名,是额外增加的列,而非业务数据列
2.2 建表时指定主键
CREATETABLEstudent(idINTPRIMARYKEY,nameVARCHAR(50),ageINT,emailVARCHAR(100));2.3 主键重复时会报错
-- 第一条:成功INSERTINTOstudent(id,name,age,email)VALUES(1,'张三',18,'1443005893@qq.com');-- 第二条:失败!id=1 已存在,主键重复报错INSERTINTOstudent(id,name,age,email)VALUES(1,'李四',20,'1443005893@qq.com');❌
Duplicate entry '1' for key 'PRIMARY'
2.4 自动递增 AUTO_INCREMENT
手动维护主键十分麻烦,MySQL 提供了自动递增策略——每次取当前表中最大 ID 值 + 1,由数据库自动维护。
CREATETABLEstudent(idINTPRIMARYKEYAUTO_INCREMENT,nameVARCHAR(50),ageINT,emailVARCHAR(100));-- 插入时无需手动指定 idINSERTINTOstudent(name,age,email)VALUES('张三',18,'1443005893@qq.com');INSERTINTOstudent(name,age,email)VALUES('李四',20,'1443005893@qq.com');AUTO_INCREMENT 的重要特点:
- 只有整型列才能设置为自动递增
- 只有主键才能设置为自增列
- 新增数据时可以不赋值,系统自动填充
- 初始值默认为
1,步长默认为1 - 自增值一旦被使用(即便该行被删除),也不会回退复用
🔒 三、唯一约束(UNIQUE)
3.1 基本概念
- 保证某列数据不允许重复
- 与主键类似,但级别低于主键
- 一张表可以创建多个唯一约束
- 唯一约束的列允许为 NULL(NULL 不参与唯一性比较)
- ⚠️ 注意:
VARCHAR(255)长度时无法设置唯一约束
3.2 常见应用场景
唯一约束常用于约束手机号、账号、邮箱等不允许重复的字段。
CREATETABLEstudent(idINTPRIMARYKEYAUTO_INCREMENT,nameVARCHAR(50),ageINT,emailVARCHAR(100)UNIQUE-- 邮箱不允许重复);3.3 违反唯一约束时报错
INSERTINTOstudent(name,age,email)VALUES('张三',18,'1443005893@qq.com');-- 同一邮箱再次插入,触发唯一约束INSERTINTOstudent(name,age,email)VALUES('lisi',20,'1443005893@qq.com');❌
Duplicate entry '1443005893@qq.com' for key 'email'
🚫 四、非空约束(NOT NULL)
4.1 基本概念
通过NOT NULL将某列设置为必填字段,插入数据时该列不允许为空值。
4.2 使用示例
CREATETABLEstudent(idINTPRIMARYKEYAUTO_INCREMENT,nameVARCHAR(50)NOTNULL,-- 姓名不能为空ageINT,emailVARCHAR(100)UNIQUE);-- 尝试插入空姓名,会报错INSERTINTOstudent(name,age,email)VALUES(NULL,18,'test@qq.com');❌
Column 'name' cannot be null
🎛️ 五、默认约束(DEFAULT)
5.1 基本概念
通过DEFAULT为某列设置默认值,插入时若不提供该列的值,则自动使用默认值填充。
5.2 使用示例
CREATETABLEstudent(idINTPRIMARYKEYAUTO_INCREMENT,nameVARCHAR(50)NOTNULL,ageINTDEFAULT18,-- 年龄默认为18genderVARCHAR(10)DEFAULT'男'-- 性别默认为男);-- 不提供 age 和 gender,将使用默认值INSERTINTOstudent(name)VALUES('张三');-- 查询结果:age=18, gender='男'SELECT*FROMstudent;🔗 六、外键约束(FOREIGN KEY)
6.1 基本概念
外键用来让两个表的数据之间建立连接,保证数据的一致性和完整性。
- 拥有外键的表称为从表(子表)
- 被引用的表称为主表(父表)
- 从表外键列的值必须在主表对应列中存在
6.2 添加外键示例
假设有部门表dept和员工表emp:
-- 主表:部门表CREATETABLEdept(idINTPRIMARYKEYAUTO_INCREMENT,nameVARCHAR(50)NOTNULL);-- 从表:员工表,dept_id 引用 dept 的主键CREATETABLEemp(idINTPRIMARYKEYAUTO_INCREMENT,nameVARCHAR(50)NOTNULL,dept_idINT,CONSTRAINTfk_emp_deptFOREIGNKEY(dept_id)REFERENCESdept(id));6.3 删除 / 更新时的行为策略
当主表数据发生变化时,从表如何联动,可通过以下策略控制:
| 策略 | 说明 |
|---|---|
CASCADE | 父表 update/delete 时,子表匹配记录同步update/delete |
SET NULL | 父表 update/delete 时,子表外键列设为 NULL(该列不能是 NOT NULL) |
NO ACTION | 子表有匹配记录时,不允许父表进行 update/delete |
RESTRICT | 同 NO ACTION,立即检查外键约束 |
-- 使用 CASCADE:父表删除时子表联动删除CONSTRAINTfk_emp_deptFOREIGNKEY(dept_id)REFERENCESdept(id)ONDELETECASCADEONUPDATECASCADE6.4 使用外键的注意事项
- 数据库引擎必须为InnoDB(MyISAM 不支持外键)
- 从表中的外键值,必须在主表中存在,或者为 NULL
6.5 ⚠️ 为什么实际开发中常常不使用外键?
尽管外键能提高数据质量,但在实际工程中,许多团队选择不在数据库层面使用外键,而是在应用层做数据一致性校验。原因如下:
在表上存在活动外键时,每次插入、更新、删除操作前,数据库都需要检查是否违反约束,对性能影响较大。数据仓库、OLAP 场景中批量处理数据时性能开销尤为明显。
常见做法:数据库不加外键 + 业务代码保证数据一致性 + 定期数据巡检。
🗂️ 七、表与表之间的关系
数据库中,表和表之间存在三种基本关系:一对一、一对多、多对多。正确理解并设计表关系,是数据库建模的核心能力。
7.1 一对多(最常见)
概念
一张表中的一条记录对应另一张表中的多条记录。
典型例子
- 一个部门拥有多个员工
- 一个用户有多条订单
- 一个班级有多个学生
如何建立联系
在**"多"的一方**(子表)添加外键,引用**"一"的一方**(父表)的主键。
-- 父表:部门(一)CREATETABLEdept(idINTPRIMARYKEYAUTO_INCREMENT,nameVARCHAR(50)NOTNULL);-- 子表:员工(多)CREATETABLEemp(idINTPRIMARYKEYAUTO_INCREMENT,nameVARCHAR(50)NOTNULL,dept_idINT,-- 外键,指向 dept 表FOREIGNKEY(dept_id)REFERENCESdept(id));关系示意图:
7.2 一对一
概念
一张表中的一条记录对应另一张表中的恰好一条记录,反之亦然。
典型例子
- 用户表与用户详情表(基础信息 + 扩展信息拆分存储)
- 公民与身份证
- 员工与工牌
为什么需要一对一?
通常是为了:
- 垂直分表:将频繁访问的核心字段和不常访问的扩展字段分开,提升查询性能
- 字段过多时,拆分成两张表管理更清晰
如何建立联系
在任意一方添加外键,并在外键列上加UNIQUE约束(保证一对一,而非一对多)。
-- 用户基础表CREATETABLEuser(idINTPRIMARYKEYAUTO_INCREMENT,usernameVARCHAR(50)NOTNULL,passwordVARCHAR(100)NOTNULL);-- 用户详情表CREATETABLEuser_detail(idINTPRIMARYKEYAUTO_INCREMENT,user_idINTUNIQUE,-- UNIQUE 保证一对一phoneVARCHAR(20),addressVARCHAR(200),FOREIGNKEY(user_id)REFERENCESuser(id));关系示意图:
7.3 多对多
概念
一张表中的一条记录对应另一张表中的多条记录,反过来也一样。
典型例子
- 学生和课程:一个学生选多门课,一门课有多个学生
- 用户和角色:一个用户有多个角色,一个角色对应多个用户
- 订单和商品:一个订单包含多个商品,一个商品出现在多个订单中
如何建立联系:中间关系表
多对多不能直接在两张表上加外键来解决,必须引入第三张关联表(中间表)。中间表至少包含两个外键,分别指向两张主表的主键。
-- 学生表CREATETABLEstudent(idINTPRIMARYKEYAUTO_INCREMENT,nameVARCHAR(50)NOTNULL);-- 课程表CREATETABLEcourse(idINTPRIMARYKEYAUTO_INCREMENT,nameVARCHAR(100)NOTNULL);-- 中间关联表:学生选课CREATETABLEstudent_course(idINTPRIMARYKEYAUTO_INCREMENT,student_idINTNOTNULL,course_idINTNOTNULL,scoreDECIMAL(5,2),-- 还可以存储关系相关的额外属性FOREIGNKEY(student_id)REFERENCESstudent(id),FOREIGNKEY(course_id)REFERENCEScourse(id),UNIQUEKEYuk_stu_course(student_id,course_id)-- 防止重复选课);数据示例:
可以看出:张三选了 MySQL 和 Java,李四选了 MySQL,王五选了 Python。
7.4 三种关系总结对比
| 关系类型 | 典型场景 | 实现方式 |
|---|---|---|
| 一对多 | 部门-员工、用户-订单 | 在"多"的一方加外键 |
| 一对一 | 用户-详情、公民-身份证 | 在任意一方加外键 +UNIQUE约束 |
| 多对多 | 学生-课程、用户-角色 | 创建中间关联表,各加一个外键 |
📝 八、综合建表案例
以一个简单的选课系统为例,综合运用约束与表关系:
-- 1. 班级表CREATETABLEclass(idINTPRIMARYKEYAUTO_INCREMENT,nameVARCHAR(50)NOTNULLUNIQUE-- 班级名唯一);-- 2. 学生表(多对一 班级)CREATETABLEstudent(idINTPRIMARYKEYAUTO_INCREMENT,nameVARCHAR(50)NOTNULL,ageINTDEFAULT18,emailVARCHAR(100)UNIQUE,class_idINT,FOREIGNKEY(class_id)REFERENCESclass(id)ONDELETESETNULL);-- 3. 课程表CREATETABLEcourse(idINTPRIMARYKEYAUTO_INCREMENT,nameVARCHAR(100)NOTNULL,descriptionVARCHAR(500));-- 4. 选课中间表(多对多)CREATETABLEstudent_course(idINTPRIMARYKEYAUTO_INCREMENT,student_idINTNOTNULL,course_idINTNOTNULL,scoreDECIMAL(5,2),FOREIGNKEY(student_id)REFERENCESstudent(id)ONDELETECASCADE,FOREIGNKEY(course_id)REFERENCEScourse(id)ONDELETECASCADE,UNIQUEKEYuk_stu_course(student_id,course_id));💡 总结
| 知识点 | 核心要点 |
|---|---|
| 主键约束 | 唯一 + 非空,配合AUTO_INCREMENT自动维护 |
| 唯一约束 | 不重复但可为空,一表可有多个 |
| 非空约束 | 该列必须有值 |
| 默认约束 | 不传值时自动填默认值 |
| 外键约束 | 维护表间引用完整性,实际开发中常用应用层替代 |
| 一对多 | 在"多"方加外键 |
| 一对一 | 外键 + UNIQUE |
| 多对多 | 建立中间关联表 |
💬学习建议:约束是数据库设计的基础,表关系是业务建模的核心。建议多练习 ER 图设计,先画图再建表,养成良好的数据库设计习惯。
如有问题欢迎在评论区留言交流!👇