1. 数据库约束概述
在数据库中,约束(Constraints)是作用在表字段上的规则,用于限制加入表的数据,从而确保数据的 完整性(Integrity)和准确性(Accuracy)。
2. 字段(列级)域约束
2.1 非空约束(NOT NULL)
非空约束 用于保证该列的值绝对不能为 NULL。如果尝试插入 NULL 值,数据库将会直接拦截并报错。
💡 核心法则与注意事项
- 全面适用:所有数据类型的字段(数字、日期、字符串等)都可以添加非空约束。
- 列级限制 :非空约束只能声明在具体的列级别上,不能声明为表级约束。
- 多列并存:一张表中允许同时有多个字段设置非空约束。
- 🔥 空值判定细节(新手必背 🔑) :
- 空字符串
''不算NULL!非空字段允许写入空字符串。 - 数字
0不算NULL!非空字段允许写入数字 0。 - 只有显式写入
NULL或在没有默认值的情况下缺省该列,才会触发非空约束拦截。
- 空字符串
一、 约束的添加(ADD)
1. 建表时添加(CREATE TABLE)
在定义字段时,直接在数据类型后面加上 NOT NULL 关键字。
-
语法模板 :
sqlCREATE TABLE 表名称( 字段名1 数据类型 [列级约束], 字段名2 数据类型 NOT NULL, 字段名3 数据类型 NOT NULL [其他列级约束] ); -
具体示例 :
sql-- 创建员工表 emp1,并对 id 和 name 设置非空约束 CREATE TABLE emp1( id INT(10) NOT NULL, name VARCHAR(20) NOT NULL, sex CHAR(1) );
2. 建表后修改/添加(ALTER TABLE)
如果表已经创建完毕,我们可以通过 ALTER TABLE ... MODIFY 来为现有字段追加 NOT NULL 限制。
-
语法模板 :
sqlALTER TABLE 表名称 MODIFY 字段名 数据类型 NOT NULL; -
具体示例 :
sql-- 将表 emp1 中的 sex 字段修改为非空约束 ALTER TABLE emp1 MODIFY sex CHAR(1) NOT NULL;
二、 约束的删除(REMOVE)
删除非空约束的本质,就是通过修改列属性,重新将其修改为允许为 NULL(或者直接缺省约束)。
-
语法模板 :
sql-- 方式一:显式允许为 NULL ALTER TABLE 表名称 MODIFY 字段名 数据类型 NULL; -- 方式二:直接修改类型(不加任何限制,MySQL 默认允许为 NULL) ALTER TABLE 表名称 MODIFY 字段名 数据类型; -
具体示例 :
sql-- 将 emp1 表中的 sex 字段改为允许为 NULL ALTER TABLE emp1 MODIFY sex CHAR(1) NULL; -- 或者(同时将长度修改为 30,不加限制即代表允许为 NULL) ALTER TABLE emp1 MODIFY sex VARCHAR(30);
三、 查看与验证非空约束(DESCRIBE)
在任何添加、修改或删除约束的操作后,我们都可以通过 DESC 命令来查看表结构,实时验证约束状态。
-
语法模板 :
sqlDESC 表名称; -- 或 DESCRIBE 表名称; -
具体示例 :
sqlDESC emp1;- 🔑 结果解读 :观察输出表格中的
Null列:- 若显示
NO:代表非空约束生效(该列绝对不允许为NULL)。 - 若显示
YES:代表允许为空(非空约束不存在或已被成功删除)。
- 若显示
- 🔑 结果解读 :观察输出表格中的
2.2 默认值约束(DEFAULT)
默认值约束用于指定当插入新数据时,若没有显式为该字段赋值,则由数据库系统自动赋予的预设默认值。
💡 核心法则与注意事项
- 位置约定(防重冲突 🔑) :默认值约束通常不添加到唯一约束(
UNIQUE)或主键(PRIMARY KEY)上。否则,多行记录若都缺省并采用同一默认值,会直接触发主键/唯一性冲突报错。 - 生效时机 :只有当插入(
INSERT)数据时,完全不提及(即缺省)该列 ,默认值才会自动生效。如果显式传入了NULL,字段的值依然会是NULL,而不会触发默认值! - 细节特点:设定的默认值,其数据类型必须与字段定义的数据类型完全保持一致。
一、 默认值的添加(ADD)
1. 建表时添加(CREATE TABLE)
在字段定义的数据类型后面,使用 DEFAULT 默认值 关键字进行声明。
-
语法模板:
sqlCREATE TABLE 表名称( 字段名 数据类型 DEFAULT 默认值, 字段名2 数据类型 NOT NULL DEFAULT 默认值 ); -
具体示例:
sql-- 创建表 emp2,为 id 设置默认值 0,为 name 设置非空且默认值为 '二狗子' CREATE TABLE emp2( id INT(10) DEFAULT 0, name VARCHAR(20) NOT NULL DEFAULT '二狗子' );
2. 建表后修改/添加(ALTER TABLE)
如果表已建好,可以通过 MODIFY 语句追加或更改默认值。
!WARNING\] **🚨 极其重要的细节(非空约束并存问题)** : 在通过 `MODIFY` 修改或追加默认值时,**如果该列原本设置了非空约束(NOT NULL),你必须在语句末尾显式地重新写上 `NOT NULL`** 。 否则,MySQL 会认为你重新定义了该字段的所有属性,从而悄悄将原有的非空约束给**自动删除**!
-
语法模板 :
sql-- 仅仅追加默认值约束 ALTER TABLE 表名称 MODIFY 字段名 数据类型 DEFAULT 默认值; -- 追加默认值的同时,显式保留或设置非空约束(推荐!🛡️) ALTER TABLE 表名称 MODIFY 字段名 数据类型 DEFAULT 默认值 NOT NULL; -
具体示例 :
sql-- 将 emp2 表中已有非空约束的 name 字段增加/修改默认值为 ''(空字符串),并显式保留非空约束 ALTER TABLE emp2 MODIFY name VARCHAR(20) DEFAULT '' NOT NULL; -- 给表里的 tel 字段增加默认值约束(无非空约束) ALTER TABLE emp2 MODIFY tel CHAR(11) DEFAULT '';
二、 默认值的删除(REMOVE)
删除默认值的本质,同样是通过 MODIFY 语句重新定义字段,并在定义中省去 DEFAULT 关键字。
!WARNING\] **🚨 删除时同样需要注意非空约束的保留**: * 如果你只想删除默认值,**但要保留原有的非空约束** ,必须在语句中显式写出 `NOT NULL`。 * 如果你想连同默认值和非空约束**一并删除**,则不需要写任何修饰词。
-
语法模板 :
sql-- 方式一:删除默认值,也不保留非空约束(均恢复默认) ALTER TABLE 表名称 MODIFY 字段名 数据类型; -- 方式二:删除默认值,但强行保留原有的非空约束 ALTER TABLE 表名称 MODIFY 字段名 数据类型 NOT NULL; -
具体示例 :
sql-- 删除 NAME 字段的默认值约束,由于该列无非空约束,直接修改类型即可 ALTER TABLE emp2 MODIFY NAME VARCHAR(20); -- 删除 age 字段的默认值约束,但强行保留其已有的非空约束(NOT NULL) ALTER TABLE emp2 MODIFY age INT NOT NULL;
三、 查看与验证默认值
-
表结构查看(验证默认值) : 运行
DESC 表名;,查看输出表格中Default列:- 若显示为您设置的默认值(如
0或'二狗子')➡️ 默认值设置成功。 - 若显示为
NULL➡️ 代表没有设置默认值或已成功删除。
- 若显示为您设置的默认值(如
-
插入测试(验证生效时机):
-
情况 A(完全不指定该列 ➡️ 自动填入默认值) :
sql-- 插入一条不包含 name 字段的记录 INSERT INTO emp2 (id) VALUES (1); -- 查询验证:name 会被自动赋予默认值 '二狗子' SELECT * FROM emp2; -
情况 B(显式给列投递 NULL ➡️ 不会走默认值!) : 在 MySQL 中,如果显式地向该列写入
NULL:sql-- 显式写入 NULL(如果该列允许为 NULL) INSERT INTO emp2 (id, name) VALUES (2, NULL); -- 此时该列的值会是真正的 NULL,而绝对不会被替换为默认值!
-
2.3 检查约束(CHECK)
检查约束用于限制某个字段的值必须符合指定的逻辑范围(如年龄必须大于 0 且小于 150,性别必须是 '男' 或 '女')。
💡 核心法则与注意事项
- 版本新特性(重要限制 🔑) :
- MySQL 5.7 及以下版本不支持此约束(只解析,不强制生效)。
- MySQL 8.0.16 及以上版本才正式支持并生效!
- 万能约束:你可以通过在括号内编写任意自定义的 Boolean 表达式,实现几乎任何自定义的数据约束!
- 开发实践建议 :通常不推荐在数据库级别使用
CHECK约束进行强数据检查。因为这会增加数据库的计算负载。业界主流做法是推荐在**应用程序代码级(如后端业务代码)**进行格式和范围检测。
一、 语法模板与具体示例
-
语法模板 :
sqlCREATE TABLE 表名称( 字段名 数据类型, CHECK (逻辑限制表达式) ); -
具体示例 :
sql-- 创建表 emp3,限定年龄在 0 到 150 岁之间,性别必须为 '男' 或 '女' CREATE TABLE emp3( name VARCHAR(20), sex CHAR(1), age INT, CONSTRAINT chk_emp3 CHECK (age BETWEEN 0 AND 150 AND sex IN ('男', '女')) );
3. 实体(表 / 行)级约束
3.1 唯一约束(UNIQUE)
唯一约束用于限定某个字段(或多个字段的组合)在整张表中存储的数据必须是唯一的,不允许重复。
💡 核心法则与注意事项
- 约束数量 :与主键不同,一张表中允许同时存在多个唯一约束。
- 空值处理(经典考点 🔑) :唯一约束允许列值为
NULL!并且,在唯一约束列上允许同时插入多个NULL值 ,而绝对不会触发重复冲突报错(因为在 SQL 规范中,NULL代表未知,两个未知的值不相等,所以不冲突)。 - 约束名称 :创建唯一约束时,如果不显式指定约束名,MySQL 默认将列名作为约束的名称。由于唯一约束底层是由**索引(INDEX)**实现的,因此删除唯一约束时,实际上需要删除对应的唯一索引!
- 🔥 复合唯一约束(多列组合唯一)判重逻辑(高频面试考点 🔑) :
- 当使用
UNIQUE(列1, 列2)声明组合唯一时,只有当插入的数据中"列1"与"列2"的值同时与已有数据重复时,才会触发冲突拦截。 - 如果仅其中一列重复,而另一列不同,则允许正常插入!
- 此外,只要组合列中任意一列为
NULL,MySQL 就不进行唯一性限制(因为NULL代表未知,无法与已有值做相等性比较,故不视为重复)。
- 当使用
一、 唯一约束的添加(ADD)
1. 建表时添加(CREATE TABLE)
支持列级声明(字段后直接加 UNIQUE)和表级声明(在表尾统一声明,支持复合唯一键及自定义约束名)。
-
语法模板 :
sql-- 方式一:列级添加(直接在字段后加 UNIQUE 或 UNIQUE KEY) CREATE TABLE 表名称( 字段名 数据类型 UNIQUE, 字段名2 数据类型 UNIQUE KEY ); -- 方式二:表级添加(支持自定义约束名,推荐!) CREATE TABLE 表名称( 字段名 数据类型, 字段名2 数据类型, [CONSTRAINT 约束名称] UNIQUE KEY(字段名) ); -
具体示例 :
sql-- 方式一:列级唯一约束形式一 CREATE TABLE emp4( NAME VARCHAR(20), phone VARCHAR(11) UNIQUE ); -- 方式二:列级唯一约束形式二 CREATE TABLE emp5( NAME VARCHAR(20), phone VARCHAR(11) UNIQUE KEY ); -- 方式三:表级唯一约束形式三(简洁写法) CREATE TABLE emp6( NAME VARCHAR(20), phone VARCHAR(11), UNIQUE KEY(phone) ); -- 方式四:复合唯一约束(多列组合唯一,限制 classes 与 num 组合唯一) CREATE TABLE emp7( classes INT, num INT, NAME VARCHAR(20), phone VARCHAR(11), UNIQUE KEY(classes, num) );
2. 建表后添加/修改(ALTER TABLE)
-
语法模板 :
sqlALTER TABLE 表名称 ADD [CONSTRAINT 约束名称] UNIQUE(列名1, 列名2, ...); -
具体示例 :
sql-- 为已存在的表 emp7 追加单列唯一约束,并自定义约束名为 emp7_name ALTER TABLE emp7 ADD CONSTRAINT emp7_name UNIQUE KEY (NAME);
二、 唯一约束的查看与删除(REMOVE)
由于唯一约束限制在 MySQL 底层是以索引形式存放的,因此我们要查阅或删除它,需要借助索引管理机制。
1. 查看约束名称
通过查阅 INFORMATION_SCHEMA 系统元数据表,快速确认表中的约束名称。
-
语法模板 :
sqlSELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = '库名' AND TABLE_NAME = '表名'; -
具体示例 :
sql-- 查看 test05_constraint 库中 emp7 表的约束名 SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = 'test05_constraint' AND TABLE_NAME = 'emp7';
2. 删除唯一约束
-
语法模板 :
sql-- 方式一:通过 DROP CONSTRAINT 删除(MySQL 8.0 推荐 🛡️) ALTER TABLE 表名称 DROP CONSTRAINT 唯一键约束名称; -- 方式二:通过 DROP INDEX 删除(传统方式,高度兼容 🛡️) ALTER TABLE 表名称 DROP INDEX 唯一键索引名; -
具体示例 :
sql-- 删除 emp7 的 emp7_name 唯一约束 ALTER TABLE emp7 DROP CONSTRAINT emp7_name; -- 或 ALTER TABLE emp7 DROP INDEX emp7_name;
3.2 主键约束(PRIMARY KEY)
主键约束是数据库中最为核心和高频使用的约束类型。它用于唯一标识表中的每一行记录,是数据库表设计的"灵魂"。
💡 什么是主键?
在没有约束的普通表中,可能会插入多行完全一样的数据(例如两个姓名同为"二狗子"、年龄 18、性别男的学生)。这会导致表中的数据出现无序且完全重复的冗余,数据库在后续的数据更新、删除或关联查询时将完全无法精确定位某一行数据!
为了在物理和逻辑上彻底杜绝此问题,数据库引入了主键的概念:
- 唯一标识 :确保表中的行数据至少有一列(或多列组合)是绝对不重复的,用来作为每一行记录的"专属身份证"。
- 非空且唯一(核心本质 🔑) :主键列在满足唯一约束(UNIQUE)的同时,还必须满足非空约束(NOT NULL)。
- 经典实例 :
- 学生表中的 学号(student_id)
- 员工表中的 工号(emp_id)
- 用户表中的 身份证号(id_card) 或 递增ID(id)
!IMPORTANT\] **🚨 为什么必须设置主键?** 任何一个符合规范的数据库表设计,都**强烈建议且必须**设置一个主键。主键不仅在业务上起到了唯一标识行数据的作用,在数据库底层也是构建聚簇索引(Clustered Index)、提升海量数据检索效率的关键物理支撑!
💡 主键的分类与设计原则(工业界核心雷区 🔑)
在表设计中,根据主键字段的来源与业务属性,主键可以划分为两大类:
| 主键类型 | 定义与使命 | 经典实例 | 优缺点分析 |
|---|---|---|---|
| 自定义主键 (代理主键) | 人为创建的一列,没有任何业务属性,唯一的使命就是保证行数据的唯一性。 | id (自增整数)、uuid (随机字符串)、学号 |
推荐!🛡️ 由于与业务完全解耦,即便未来业务逻辑发生剧烈变化,主键也永远无需变更,安全稳固。 |
| 自然主键 | 不是人为创建的列,而是数据实体自带的属性列。它同时承担"实体属性值"和"唯一标识"两个使命。 | DNA序列、身份证号、手机号、邮箱 |
不推荐!❌ 容易因为业务需求或社会政策的改变而被迫修改主键,代价极其惨重。 |
!TIP\] **🔥 为什么工业界强烈推荐使用"自定义主键(代理主键)"?(高频面试避坑 🔑)** 很多人容易犯一个错误,直接拿**身份证号** 或**邮箱/手机号**当作自然主键,认为它们反正也是唯一的。但在实际项目中,这隐藏着巨大的隐患: 1. **业务易变性**:如果用户的手机号注销被运营商重新回收,或者用户的邮箱要换,你的主键就要被迫跟着改。一旦主键更改,所有关联这个主键的外键表、历史日志、甚至第三方库全部要进行连锁修改,代价无可估量! 2. **安全和隐私保护**:身份证号、手机号等属于高度敏感的隐私数据(PII),如果直接拿来做主键在数据库系统里四处流通或作关联键,极易造成隐私泄露。 3. **索引效率** :自然主键(如 UUID、DNA序列)通常较长,在构建索引时非常消耗存储空间,且可能导致索引分裂;而自定义主键(如自增主键 `INT`/`BIGINT`)存储开销极小,写入极快。
💡 主键细节说明(5 大核心机制 🔑)
为了能深度吃透主键约束的底层运行机理,必须牢记以下 5 点核心细节规范:
- 数量限制(唯一性) :
- 每个表中只能有且仅有一个主键(指的是主键约束这个"对象"在整张表里只能创建一个)。
- 单一主键与复合主键 :
- 虽然主键只能有一个,但主键可以绑定作用在单个列 上(单一主键),或者由多个列的组合共同构成(复合主键/联合主键)。
-
!NOTE
-
🔑 大白话逻辑解密:
-
这就如同"一个班级只能有一个班长"(主键数量唯一)。但这一个班长职位,既可以由某一个同学单独挑起 (单一主键),也可以由两位同学结成联合体共同担任 (复合主键)。当为复合主键时,只有当多列的值同时重复时才会触发冲突,但联合体中的任意一列都绝对不允许为
NULL!
- 主键列类型支持 :
- 主键列可以是任意数据类型 (整数、字符串等),只要满足 "唯一且不为 NULL" 这一硬性标准即可。在实际开发中,出于存储空间和索引效率考量,强烈推荐使用整数类型(如
INT/BIGINT)。
- 主键列可以是任意数据类型 (整数、字符串等),只要满足 "唯一且不为 NULL" 这一硬性标准即可。在实际开发中,出于存储空间和索引效率考量,强烈推荐使用整数类型(如
- 命名习惯 :
- 主键的字段命名一般采用 "标识(identify)" 单词的缩写,如
xxid或xx_id(例如user_id、order_id)。这仅是开发规范,并非数据库的强制要求。
- 主键的字段命名一般采用 "标识(identify)" 单词的缩写,如
- 底层主键索引(PRIMARY) :
- 核心特性 :创建主键约束时,数据库底层会自动 在主键列(或组合列)上构建一个对应的主键索引(Clustered Index,聚簇索引)。通过主键查找数据时效率最高!
- 固定命名 :主键索引在系统中的名称是固定的,永远为
PRIMARY。即使在定义主键时通过CONSTRAINT pk_name试图起其他名字,MySQL 也会无视,依然将其锁定命名为PRIMARY! - 生命周期联动 :主键索引与主键约束"同生共死"。一旦删除主键约束,对应的主键索引也会被系统自动删除。
💡 核心辩证:主键 vs 主键约束(面试灵魂拷问 🔑)
在概念认知上,许多开发者容易将"主键"与"主键约束"混为一谈。根据课件逻辑,两者的边界和关系如下:
- 主键(概念/属性) :是表中用于确保行数据不重复的列 。主键列在逻辑上应该满足唯一且不为空的属性。
- 主键约束(物理限制/规则) :是数据库系统针对主键列施加的数据约束和强限制规则 。它的职责是充当"守门人",在物理层面确保主键列中绝对不会出现重复或
NULL的错误数据。
❓ 灵魂拷问 1:主键列一定添加主键约束么?
!NOTE\] **答:在规范的物理数据库设计中,是的,必须添加!** 虽然从理论上说,如果你为某一列手动添加了 `NOT NULL UNIQUE`(非空且唯一)约束,它在功能上确实已经具备了主键的特征,甚至在某些时候可以充当主键。但为了使系统正式将其识别为"官方主键"(在元数据中标记为 `PRI` 键,并在底层自动为其构建至关重要的聚簇索引),**我们必须为其显式赋予主键约束(PRIMARY KEY)**。
❓ 灵魂拷问 2:没有主键约束,那它还是不是主键列呢?
!WARNING\] **答:在物理数据库层面,它就丧失了主键列的资格!** 1. 如果一列(如 `student_no`)在你的业务设计中被规划为唯一标识一行的字段(此时它只是\*\*"逻辑主键"**),但你在建表时** 忘记**了声明主键约束,那么它在数据库物理层就**不是\*\*一个真正的主键列。 2. 由于缺乏主键约束的规则校验,数据库将无法拦截重复数据或 `NULL` 值的写入,脏数据一旦灌入,该列在物理上便彻底沦为普通明细列。 3. 因此,**没有主键约束这条物理缰绳,"主键列"就只能算是一个美丽的空中楼阁(逻辑概念),而无法在物理数据库中落地生效**。
🛠️ 主键约束的添加与删除(ADD & REMOVE)
在 DDL 层面,主键约束的声明支持建表时声明(列级、表级)、建表后修改追加以及整表卸载。
一、 主键约束的添加(ADD)
1. 建表时添加(CREATE TABLE)
支持列级声明 与表级声明(当要创建多列联合复合主键时,必须使用表级声明)。
-
语法模板 :
sql-- 方式一:列级声明(直接在字段定义后加 PRIMARY KEY) CREATE TABLE 表名称( 字段名 数据类型 PRIMARY KEY, 字段名2 数据类型 ); -- 方式二:表级声明(在表尾部统一声明) CREATE TABLE 表名称( 字段名 数据类型, 字段名2 数据类型, [CONSTRAINT 约束名称] PRIMARY KEY(字段名) ); -- 方式三:复合联合主键表级声明 CREATE TABLE 表名称( 字段名1 数据类型, 字段名2 数据类型, PRIMARY KEY(字段名1, 字段名2) ); -
具体示例 : 与本章 SQL 演示中的列级
emp8、表级emp9和联合主键emp10完全对齐。
2. 建表后添加/修改(ALTER TABLE)
-
语法模板 :
sqlALTER TABLE 表名称 ADD PRIMARY KEY(字段名1, 字段名2, ...); -
具体示例 :
sql-- 为已存在且没有主键的表 emp8 追加 e_id 作为主键 ALTER TABLE emp8 ADD PRIMARY KEY (e_id);
二、 主键约束的删除(REMOVE)
由于一张表中有且只能存在一个主键,所以我们删除主键时无需提供具体的主键约束名 ,直接执行 DROP PRIMARY KEY 即可。
-
语法模板 :
sqlALTER TABLE 表名称 DROP PRIMARY KEY; -
具体示例 :
sql-- 卸载表 emp8 的主键约束,无需指定约束名称 ALTER TABLE emp8 DROP PRIMARY KEY;
🚨 极其震撼的底层雷区:非空保留,唯一消失!
!WARNING\] **🔥 99% 开发者都会踩的删除盲区(课件黄金要点 🔑)** 当执行 `DROP PRIMARY KEY` 成功删除主键约束后: 1. **"唯一性"物理消失**:该列上的主键聚簇索引被彻底卸载,此时表允许插入重复的 ID 值。 2. **"非空性"依然顽固存在** :**虽然主键没了,但该列的定义在 MySQL 底层依然强制保留了 `NOT NULL`(非空约束)** !如果您尝试向该列写入 `NULL`,数据库依然会无情报错拦截! **🛠️ 彻底恢复为普通可空列的完美解法** : 如果您想在删除主键后,让它真正能够允许为 `NULL`,必须在 `DROP PRIMARY KEY` 之后,**手动重新修改列属性**: ```sql -- 第一步:删除主键约束(此时非空还在) ALTER TABLE emp8 DROP PRIMARY KEY; -- 第二步:手动重新将其定义为允许 NULL ALTER TABLE emp8 MODIFY e_id INT NULL; ```
3.3 自增长约束(AUTO_INCREMENT)
自增长约束用于限定某个整数类型字段,当向表中插入新数据时,该字段无需手动维护,由数据库系统自动以递增方式(默认步长为 1)生成唯一的序列值。
💡 核心作用
它通常与主键约束配合使用,主要用于实现 "自动增长的 ID 唯一标识",降低应用程序生成唯一 ID 的复杂度(如课件图示中,无需显式传值,ID 会自动从 1、2、3、4 递增生成)。
💡 自增长约束的 4 大硬性特点(面试必考 🔑)
- 添加位置(必须绑定键列) :
- 自增长约束只能添加到"键列"上 (如主键
PRIMARY KEY或唯一键UNIQUE)。普通非键列绝对不允许设置自增长属性!
- 自增长约束只能添加到"键列"上 (如主键
- 约束数量限制 :
- 一张表中有且仅能有一个自增长约束列。
- 数据类型限制 :
- 施加自增长约束的字段,其数据类型必须是整数类型 (如
TINYINT、INT、BIGINT等)。
- 施加自增长约束的字段,其数据类型必须是整数类型 (如
- 🔥 特殊值插入机制(高频面试考点与避坑 🔑) :
- 情况 A(传入 0 或 NULL ➡️ 触发自增) : 向该列显式传入
0或者NULL时,MySQL 不会把 0 或 NULL 写入,而是会自动计算下一位递增值并赋予该列! - 情况 B(传入非零且非空数值 ➡️ 强制覆盖) : 如果向该列显式传入了一个非零且非空的具体数值(如
100),MySQL 会尊重您的选择,强制真实设置该置值(写入 100)! - 情况 C(覆盖后的下一次自增 ➡️ 基于最大值 +1) : 当您强制写入 100 后,下一次再传入
NULL或不传值时,MySQL 自动增长的基数将从 100 开始 ,自动生成101,而不会回到原来的自然序列! - 情况 D(单向递增性,就算删除值也不会回退 ➡️ 黄金要点 🔑) : 自增长列每次都是在整张表当前曾经出现过的历史最大值 基础上进行增长。即使您把表中最大的几行(如 id=10, 11)删除了,下一次再插入时,自增列的值依然会是
12,而绝对不会回退去复用被删除的10或11!
- 情况 A(传入 0 或 NULL ➡️ 触发自增) : 向该列显式传入
一、 自增长约束的添加与删除(ADD & REMOVE)
在 DDL 层面,自增长约束支持建表时直接声明、建表后修改追加以及后续的卸载。
1. 建表时添加(CREATE TABLE)
直接在主键或唯一键整数列的定义后面加上 AUTO_INCREMENT 关键字。
-
具体示例 :
sql-- 创建表 emp9,主键为 e_id,且设置为自动递增 CREATE TABLE emp9( e_id INT PRIMARY KEY AUTO_INCREMENT, e_name VARCHAR(10) );
2. 建表后修改追加(ALTER TABLE ... MODIFY)
-
语法模板 :
sqlALTER TABLE 表名 MODIFY 字段名 整数类型 AUTO_INCREMENT; -
具体示例 :
sql-- 为已有主键但无自增的表 emp10 的 e_id 字段追加自增长属性 ALTER TABLE emp10 MODIFY e_id INT AUTO_INCREMENT;
!IMPORTANT\] **🚨 黄金设计细节:不需要再次指定主键,主键不会被覆盖!(课件王牌细节 🔑)** 在通过 `MODIFY` 修改列属性为自增长(`AUTO_INCREMENT`)时,**千万不要在语句后面再次写上 `PRIMARY KEY`** ! 因为主键约束是表级别的核心元数据,它此时依然稳固存在。`MODIFY` 重新定义字段为自增时,主键绝对不会被覆盖或降级!
3. 自增长约束的删除(REMOVE)
删除自增长的本质是修改该列定义,并在修改中省去 AUTO_INCREMENT 关键字。
-
语法模板 :
sqlALTER TABLE 表名 MODIFY 字段名 整数类型; -
具体示例 :
sql-- 删除 emp10 表中 e_id 的自增长属性,同时保持其非空与主键资格 ALTER TABLE emp10 MODIFY e_id INT;
4. 参照引用(外键)约束
外键约束(FOREIGN KEY)是用于建立和加强两张表数据之间的链接,从而保证数据的参照完整性(Referential Integrity)。
4.1 外键(参照 / 引用)约束基础
💡 什么是外键与外键约束?(大白话二狗子成绩解密 🔑)
为了说清外键的本质,我们假设有一套"学生与成绩管理"系统:
- 学生表(主表/父表) :记录学生基本信息。主键是
sid(如1代表二狗子,2代表驴蛋蛋)。 - 分数表(从表/子表) :记录各科成绩。主键是
cid,表中有一列sid指向学生表。
!CAUTION\] **🚨 恐怖的数据悬案:这 60 分到底是谁考的?** 如果我们**不添加外键约束** ,分数表中可能会插入一条记录:成绩为 `60分`,学科为 `MySQL`,关联的学生 `sid` 填入了 **`4`** 。 回头去学生表一查------**根本没有 sid 为 4 的学生!** 此时,这 60 分就成了彻头彻尾的"无头悬案"(脏数据),数据的准确性与完整性彻底崩塌!
为了杜绝这种脏数据的产生,数据库引入了外键与外键约束:
- 外键(Foreign Key 列) :引用或参照其他表主键列值的列。外键列的值范围,应当严格对应所引用主键的值范围!
- 外键约束(物理限制规则) :是由数据库系统施加的强力限制规则。它确保从表(分数表)外键列中写入的值,必须且只能是主表(学生表)主键列中已经存在的值 !如果尝试写入主表中不存在的值(如
sid=4),数据库将在物理层面无情拦截并报错!
💡 外键的核心辩证关系(父子表模型 🔑)
在外键关系中,两张表有着明确的"父子/主从"关系:
- 主表(父表,Parent Table) :被引用的表(如上述的 学生表)。外键所指向的列通常是主表的主键。
- 从表(子表,Child Table) :包含外键列并实施外键约束的表(如上述的 分数表)。
!IMPORTANT\] **🚨 外键关系的生命周期铁律**: * **先主后从**:当插入从表(分数表)数据时,外键值必须在主表(学生表)中提前存在。 * **父在子在** :在删除主表数据时,如果该行主键正被从表的外键引用,数据库默认将**拦截删除**(以防子表数据沦为无头数据),除非设置了级联删除。
💡 外键细节说明(5 大设计铁律 🔑)
为了在实际开发中能游刃有余地驾驭外键,必须掌握以下 5 大核心设计铁律:
- 外键数量(多外键特性) :
- 与主键(单表唯一)不同,一张表中可以同时包含多个外键列!
- 例如:在订单表(
orders)中,既可以包含指向用户表(users)的外键user_id,也可以包含指向商品表(goods)的外键goods_id。
- 外键跨表(父子角色分明) :
- 外键天然具有跨表引用的能力,它跨表指向另一个表的键列。外键所在的表为从表(子表/分数表) ,被引用的表为主表(父表/学生表)。
- 外键类型一致性(极其致命的报错雷区 🔑) :
- 外键的列类型绝对不能是任意类型 !它必须与所引用的主表主键列的数据类型完全保持一致 (如主表
sid是INT,从表外键sid也必须是INT;主表是VARCHAR(30),从表也必须是VARCHAR(30)),甚至字符集与排序规则 都必须一致,否则建表时会直接报1215 - Cannot add foreign key constraint严重物理错误! - 在字段命名上,虽然没有强制限制,但强烈推荐尽量命名相同 (都叫
sid),以极大提升代码的可读性。
- 外键的列类型绝对不能是任意类型 !它必须与所引用的主表主键列的数据类型完全保持一致 (如主表
- 主外键的核心价值(水平联查) :
- 关系型数据库(Relational Database)的"关系"二字,物理上主要就是通过主外键关系连接的。
- 有了主外键约束的保驾护航,两张表在执行多表关联查询(如
JOIN)时,便可以安全高效地进行水平联查,确保拼凑出的明细数据绝对可靠无脏值。
- 主表数据删除影响与避坑(高频实战 🔑) :
- 由于主外键约束的保护,当你尝试直接
DELETE主表(学生表)中的某行数据时,如果该行主键正被子表(分数表)引用,数据库将无情报错并直接拦截删除操作以保证数据安全! -
🛠️ 正确的安全删除顺序:
-
- 方法一(常规删除) :先删除子表(分数表)中所有引用的数据,然后再去删除主表(学生表)中的对应数据。
-
- 方法二(级联更新与级联删除,CASCADE) :在定义外键时设置
ON DELETE CASCADE,从而实现"父表一删,子表自动联动全部抹去"的高级功能,后续章节我们将深度拆解该高级语法。
- 方法二(级联更新与级联删除,CASCADE) :在定义外键时设置
- 由于主外键约束的保护,当你尝试直接
4.2 外键约束的添加与删除(ADD & REMOVE)
在 DDL 层面,外键约束支持建表时直接声明、建表后修改追加以及后续的卸载。
一、 外键约束的添加(ADD)
1. 建表时添加(CREATE TABLE)
通常在从表(子表)的字段声明完毕后,作为表级约束统一追加。
-
语法模板 :
sqlCREATE TABLE 从表名称( 字段名 数据类型, ... [CONSTRAINT 约束名称] FOREIGN KEY(从表外键列) REFERENCES 主表(主表主键列) [ON UPDATE CASCADE][ON DELETE CASCADE] -- 扩展级联属性(可选) ); -
具体示例 :
sql-- 先创建主表学生表 student1 CREATE TABLE student1( sid INT PRIMARY KEY AUTO_INCREMENT, sname VARCHAR(20) ); -- 再创建从表分数表 score1 并添加外键约束 CREATE TABLE score1( cid INT PRIMARY KEY AUTO_INCREMENT, NUMBER INT, sid INT, CONSTRAINT s_s_1_kf FOREIGN KEY(sid) REFERENCES student1(sid) );
2. 建表后修改追加(ALTER TABLE ... ADD CONSTRAINT)
-
语法模板 :
sqlALTER TABLE 从表名 ADD [CONSTRAINT 外键约束名] FOREIGN KEY(从表外键列) REFERENCES 主表名(主表主键列) [ON UPDATE CASCADE][ON DELETE CASCADE]; -
具体示例 :
sql-- 假设创建 score2 时未定义外键,后期修改追加外键约束: ALTER TABLE score2 ADD CONSTRAINT s_s_2_kf FOREIGN KEY(sid) REFERENCES student2(sid);
二、 外键约束的删除与残留索引清理(REMOVE 🔑)
!WARNING\] **🔥 极其极其震撼的删除双层雷区(课件黄金最高考点 🔑)** 很多开发者认为删除外键约束跟删除主键一样简单,直接跑一行 `DROP FOREIGN KEY` 就可以了。这其实隐藏着一个致命的"物理残留": 1. **第一步:卸载外键约束** : 当你执行 `ALTER TABLE 表名 DROP FOREIGN KEY 约束名;` 时,**外键的物理强关系限制确实消失了**(从表此时已允许插入脏数据)。 2. **第二步:手动清理自动建立的残留索引** : **MySQL 底层在外键创建时会自动生成一个隐藏的普通索引以加速联查,而当你 DROP FOREIGN KEY 后,这个普通索引却依然顽固残留!** 这不仅会造成多余的存储和计算开销,在复杂的表结构迁移中还可能导致死锁或表锁定。 你**必须** 再次执行 `DROP INDEX` 将这个残留索引手动彻底打扫干净!
-
完美删除两步法实操模板(以 score2 为例) :
sql-- 【步骤 1】先在元数据表中查出外键约束的精确名字,然后将其删除 -- 1. 查看约束名 SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_NAME = 'score2'; -- 2. 卸载外键约束 ALTER TABLE score2 DROP FOREIGN KEY s_s_2_kf; -- 【步骤 2】查看自动建立的残留索引名,并将其手动彻底铲除! -- 1. 查阅残留索引名 SHOW INDEX FROM score2; -- 2. 手动卸载该残留索引(索引名默认与外键名或外键列名相同,如 s_s_2_kf) ALTER TABLE score2 DROP INDEX s_s_2_kf;