资讯动态

MySQL 约束完全指南:从入门到精通(含主键、外键、唯一、非空、默认、自增)

发布时间:2026/8/24 7:31:53 来源:尧图企业网站定制
前言什么是约束为什么需要约束在数据库设计中我们经常面临一个问题如何保证存入数据库的数据是正确、完整、一致的例如学生的学号不能重复用户的邮箱不能为空订单中的商品编号必须在商品表中真实存在……这些业务规则如果仅靠应用程序来检查不仅代码繁琐而且容易遗漏甚至在高并发下出现数据错误。约束Constraint正是数据库为我们提供的“守门员”——它是在表结构上定义的一系列规则用于限制表中数据的取值范围或数据间的依赖关系。每当执行INSERT、UPDATE、DELETE操作时数据库会自动检查这些规则只有满足规则的数据才能被接受。MySQL 支持六大类约束主键约束PRIMARY KEY– 唯一标识每一行自动递增约束AUTO_INCREMENT– 为主键自动生成值唯一约束UNIQUE– 保证列值不重复非空约束NOT NULL– 禁止空值默认约束DEFAULT– 为列提供默认值外键约束FOREIGN KEY– 维护表之间的引用完整性本文将逐一深入讲解每类约束包含详细语法、示例代码、常见错误分析以及关于外键性能的争议性话题。全文不依赖任何图片所有图示均用文字描述和 SQL 代码呈现。一、主键约束PRIMARY KEY—— 数据的身份证1.1 什么是主键主键是表中唯一标识每一行数据的一列或几列的组合。它必须满足两个条件唯一性表中任意两行的主键值不能相同。非空性主键列不允许出现NULL值。从理论讲每个数据表都应该有一个主键。主键通常命名为id代表该行的“身份证号”。1.2 创建主键的三种方式方式一建表时直接指定某列为主键CREATE TABLE student ( id INT PRIMARY KEY, name VARCHAR(50), age INT, email VARCHAR(100) );方式二建表后通过ALTER TABLE添加主键CREATE TABLE student ( id INT, name VARCHAR(50), age INT, email VARCHAR(100) ); ALTER TABLE student ADD PRIMARY KEY (id);方式三联合主键使用多列共同作为主键CREATE TABLE score ( student_id INT, course_id INT, score INT, PRIMARY KEY (student_id, course_id) );联合主键要求(student_id, course_id)的组合值是唯一的。1.3 违反主键约束的后果假设我们尝试向student表插入两条id1的记录INSERT INTO student(id, name, age, email) VALUES (1, 张安, 18, 1443005893qq.com); INSERT INTO student(id, name, age, email) VALUES (1, 李四, 20, 1443005893qq.com);第二条语句会触发错误ERROR 1062 (23000): Duplicate entry 1 for key PRIMARY这是因为主键1已经存在数据库拒绝了重复数据。最佳实践主键列不应该使用业务上有意义的列如身份证号、手机号因为这些列可能会变更推荐使用一个无业务含义的整数id作为主键。二、自动递增约束AUTO_INCREMENT2.1 为什么需要自动递增如果我们手动为主键赋值就必须保证每次插入的值都不重复且不为空这在多线程环境下很容易出错。MySQL 提供了AUTO_INCREMENT属性它可以让数据库自动为主键生成一个递增的数值我们只需忽略主键列即可。2.2 AUTO_INCREMENT 的特点只能用于整数类型TINYINT, SMALLINT, INT, BIGINT 等。只能用于主键列或具有唯一索引的列但通常只用于主键。插入数据时可以不指定该列的值MySQL 会自动分配当前最大值1。初始值默认为1每次增量默认为1。一旦某个自增值被使用即使该行后来被删除该值也不会被重复使用避免产生歧义。2.3 使用 AUTO_INCREMENT 的示例创建带有自增主键的表CREATE TABLE student ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50), age INT, email VARCHAR(100) );插入数据时不指定idINSERT INTO student(name, age, email) VALUES (张安, 18, 1443005893qq.com); INSERT INTO student(name, age, email) VALUES (李四, 20, 1443005893qq.com);查询结果SELECT * FROM student;--------------------------------------- | id | name | age | email | --------------------------------------- | 1 | 张安 | 18 | 1443005893qq.com | | 2 | 李四 | 20 | 1443005893qq.com | ---------------------------------------可以看到id被自动填充为1和2。2.4 手动干预自增值查看当前自增值SHOW TABLE STATUS LIKE student;中的Auto_increment字段。强制指定自增起始值ALTER TABLE student AUTO_INCREMENT 100;插入时也可以手动指定一个大于当前最大值的 id后续自增会基于该值继续。⚠️ 注意自增列一旦被使用例如插入后回滚或删除其值不会被复用。例如删除了 id2 的行下次插入的 id 仍然是 3。三、唯一约束UNIQUE—— 不允许重复但允许空值3.1 唯一约束与主键的区别特性主键 (PRIMARY KEY)唯一约束 (UNIQUE)是否允许为空不允许允许但只能有一个 NULL表中数量最多一个可以有多个是否自动创建索引是聚簇索引是非聚簇索引业务含义行的唯一标识某列不能有重复值唯一约束非常适合用于手机号、邮箱、身份证号、用户名等业务上要求唯一的字段。3.2 创建唯一约束方式一建表时直接指定CREATE TABLE user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) UNIQUE, email VARCHAR(100) UNIQUE );方式二建表后添加ALTER TABLE user ADD UNIQUE (email);方式三为唯一约束命名推荐便于管理CREATE TABLE user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50), email VARCHAR(100), CONSTRAINT uk_username UNIQUE (username), CONSTRAINT uk_email UNIQUE (email) );3.3 唯一约束的注意事项对于VARCHAR类型如果长度过长例如超过 255MySQL 可能无法直接创建唯一索引取决于字符集和存储引擎。但在实际生产环境中255 以内的字符串是安全的。唯一约束允许NULL值但只允许一个NULL因为NULL ! NULL但数据库通常实现为最多一个 NULL。3.4 违反唯一约束的示例INSERT INTO user(username, email) VALUES (zhang, zhangexample.com); INSERT INTO user(username, email) VALUES (li, zhangexample.com); -- 重复邮箱报错ERROR 1062 (23000): Duplicate entry zhangexample.com for key uk_email四、非空约束NOT NULL—— 拒绝“空白”数据4.1 作用与语法非空约束强制要求某列的值不能为 NULL。如果插入或更新时未提供该列的值或者显式赋值为 NULL数据库会拒绝操作。在创建表时指定CREATE TABLE student ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, -- 姓名不能为空 age INT NOT NULL, -- 年龄不能为空 email VARCHAR(100) -- 邮箱可以为空 );修改现有表添加非空约束ALTER TABLE student MODIFY name VARCHAR(50) NOT NULL;4.2 违反非空约束的后果-- 没有给 name 赋值且 name 没有默认值 INSERT INTO student(age, email) VALUES (20, testexample.com);报错ERROR 1364 (HY000): Field name doesnt have a default value4.3 使用场景业务上必须存在的数据如用户名、商品价格、订单金额。外键列通常也需要设置为 NOT NULL除非业务允许“未关联”的情况。五、默认约束DEFAULT5.1 什么是默认约束当插入一行数据时如果没有为某个列提供值该列会自动采用预先定义的默认值。这可以简化代码并避免因疏忽导致意外 NULL。语法CREATE TABLE product ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100) NOT NULL, price DECIMAL(10,2) DEFAULT 0.00, -- 默认价格为0 status TINYINT DEFAULT 1, -- 默认状态为1上架 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP );5.2 默认值的规则默认值可以是常量、表达式如 CURRENT_TIMESTAMP但不能依赖于其他列的值。如果同时设置了 NOT NULL 和 DEFAULT那么插入时可以不提供该列的值数据库会用默认值填充不会违反非空约束。5.3 示例INSERT INTO product(name) VALUES (智能手机); -- 此时 price 自动为 0.00status 自动为 1created_at 自动为当前时间5.4 修改默认值ALTER TABLE product ALTER price SET DEFAULT 99.99; -- 删除默认值 ALTER TABLE product ALTER price DROP DEFAULT;六、外键约束FOREIGN KEY6.1 外键的作用外键用于在两个表之间建立引用关系保证“从表”中的某一列值必须存在于“主表”的主键或唯一键中。这种机制称为参照完整性。例如订单表 中的 customer_id 必须引用 客户表 中存在的 id从而防止订单指向一个不存在的客户。6.2 创建外键的基本语法CREATE TABLE class ( id INT PRIMARY KEY AUTO_INCREMENT, class_name VARCHAR(50) NOT NULL ); CREATE TABLE student ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, class_id INT, FOREIGN KEY (class_id) REFERENCES class(id) );也可以在创建表后添加ALTER TABLE student ADD CONSTRAINT fk_student_class FOREIGN KEY (class_id) REFERENCES class(id);6.3 外键的删除/更新规则ON DELETE / ON UPDATE这是外键最精髓的部分它定义了当主表中的记录被删除或更新时从表中的关联记录该如何处理。MySQL 支持四种选项选项行为描述CASCADE主表删除/更新时子表中匹配的记录也同步删除/更新。SET NULL主表删除/更新时子表中匹配的外键列设置为 NULL要求子表该列允许为空。NO ACTION如果子表中有匹配记录则禁止对主表执行删除/更新操作。RESTRICT与 NO ACTION 类似立即检查约束是 MySQL 的默认行为。⚠️ 注意SET DEFAULT 选项虽然语法上存在但在 InnoDB 引擎中不被支持。示例级联删除CREATE TABLE class ( id INT PRIMARY KEY, name VARCHAR(50) ); CREATE TABLE student ( id INT PRIMARY KEY, name VARCHAR(50), class_id INT, FOREIGN KEY (class_id) REFERENCES class(id) ON DELETE CASCADE );当执行DELETE FROM class WHERE id 1;时会自动删除所有class_id1的学生记录。示例限制删除RESTRICT-- 默认行为无需显式指定 FOREIGN KEY (class_id) REFERENCES class(id) ON DELETE RESTRICT此时如果班级下有学生删除班级会失败必须先删除或转移学生。6.4 使用外键的前提条件存储引擎必须为 InnoDBMyISAM 不支持外键。引用的列主表列必须为主键或具有唯一约束。外键列和引用列的数据类型必须完全一致包括长度、符号、精度。子表中的外键列数据必须在主表中存在插入/更新时检查。6.5 违反外键约束的示例-- 假设 class 表中只有 id1,2 INSERT INTO student(name, class_id) VALUES (小明, 99);报错ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails6.6 为什么很多开发者“讨厌”外键尽管外键能够保证数据一致性但在某些场景下开发团队会主动放弃使用外键主要原因包括性能损耗每次插入、更新、删除子表或主表时数据库都要额外检查外键约束在高并发写入场景下会成为瓶颈。对于数据仓库、日志系统等批量插入场景外键的开销尤其明显。分库分表困难当系统扩展为分布式数据库后跨库的外键无法实现因此从一开始就避免使用外键更利于未来扩展。维护成本复杂的级联删除可能导致意外的数据丢失且调试困难。替代方案很多团队选择在应用层保证数据完整性通过事务或业务逻辑来维护关系换取更高的数据库写入性能。 结论对于金融、电商核心订单等对数据一致性要求极高的系统建议使用外键对于日志、报表、社交动态等对性能要求更高、且允许短暂不一致的场景可以放弃外键由应用层逻辑保证。七、常用约束操作语句汇总为了日常使用方便以下列出修改约束的常用语句操作SQL 示例添加主键ALTER TABLE t ADD PRIMARY KEY (id);删除主键ALTER TABLE t DROP PRIMARY KEY;添加唯一约束ALTER TABLE t ADD UNIQUE (email);删除唯一约束ALTER TABLE t DROP INDEX index_name;索引名通常与列名相同添加非空约束ALTER TABLE t MODIFY col VARCHAR(50) NOT NULL;删除非空约束ALTER TABLE t MODIFY col VARCHAR(50) NULL;设置默认值ALTER TABLE t ALTER col SET DEFAULT 0;删除默认值ALTER TABLE t ALTER col DROP DEFAULT;添加外键ALTER TABLE child ADD CONSTRAINT fk_name FOREIGN KEY (col) REFERENCES parent(id);删除外键ALTER TABLE child DROP FOREIGN KEY fk_name;八、总结与最佳实践本文详细介绍了 MySQL 中的六大约束主键、自增、唯一、非空、默认、外键。它们共同构筑了数据库的数据安全防线。记住这几点最佳实践每一张表都应该有一个主键推荐使用INT AUTO_INCREMENT作为代理主键。对业务上要求唯一的字段邮箱、手机号添加唯一约束防止重复数据。对必须有值的字段添加非空约束减少应用层判断。合理使用默认值简化插入语句避免意外 NULL。外键要谨慎使用在强一致性要求的 OLTP 系统中推荐使用外键并选择合适的ON DELETE策略。在高性能写入、分库分表、数据仓库场景下可以考虑放弃外键由应用层保证一致性。不要过度约束过多的约束会降低写入性能且使数据库难以维护。约束不是枷锁而是保护数据的“安全带”。正确使用它们能让你的数据库更健壮、更可靠。希望这篇文章能帮助你全面掌握 MySQL 约束。如果你在实际开发中遇到了任何与约束相关的坑欢迎在评论区留言讨论。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价