直接执行ALTER TABLE添加唯一约束报错Duplicate entry,是因为MySQL会在建约束前全表校验,发现目标列存在重复值即中断并报错;必须先用SELECT GROUP BY HAVING查出重复项,手动删或合并后才能成功添加。

ALTER TABLE 添加唯一约束时为什么报错 Duplicate entry
直接加约束失败,大概率是表里已有重复数据。MySQL 在创建 UNIQUE 约束前会校验全表,只要存在任意两行在目标列(或列组合)上值相同,就会报 Duplicate entry 'xxx' for key 'xxx'。
解决办法只有两个:
- 先用
SELECT找出重复项:SELECT col1, col2, COUNT(*) FROM table_name GROUP BY col1, col2 HAVING COUNT(*) > 1 - 手动去重(删行或合并),或更新成唯一值,再执行
ALTER TABLE ... ADD UNIQUE - 如果业务允许忽略历史脏数据,可先导出、清洗、清空再导入——但生产环境慎用
用 CREATE TABLE 一次性定义唯一约束更稳妥
新建表时就写明约束,避免后续校验失败。语法清晰,也方便团队理解字段语义。
常见写法有三种,区别在命名和组合粒度:
- 单列约束(隐式命名):
CREATE TABLE users (id INT, email VARCHAR(255) UNIQUE); - 单列约束(显式命名):
CREATE TABLE users (id INT, email VARCHAR(255), CONSTRAINT uk_email UNIQUE (email)); - 多列联合唯一:
CONSTRAINT uk_user_role UNIQUE (user_id, role_type)
显式命名的好处是:报错信息里能直接看到 uk_email,而不是一串自动生成的 users.email_2 类似名字,排查快得多。
唯一索引和唯一约束到底有什么区别
在 InnoDB 引擎下,UNIQUE 约束本质就是建了一个唯一索引,二者物理实现完全一样。区别只在语义和约束行为:
- 约束带 DDL 语义,强调业务规则;索引更偏性能用途
-
UNIQUE约束名会被写入INFORMATION_SCHEMA.TABLE_CONSTRAINTS,而普通唯一索引不会 - 删除约束必须用
DROP INDEX或ALTER TABLE DROP INDEX,不能用DROP CONSTRAINT(MySQL 不支持该语法) - NULL 值处理一致:多行可以有多个
NULL(因为NULL != NULL),不违反唯一性
给大表加唯一约束卡住不动怎么办
加约束会触发全表扫描+排序+索引构建,在千万级以上表上可能持续几分钟甚至更久,且期间会锁表(取决于 MySQL 版本和 ALGORITHM 设置)。
生产环境务必避开高峰,并考虑以下操作:
- MySQL 5.6+ 支持
ALGORITHM=INPLACE(需存储引擎支持),能减少锁时间:ALTER TABLE t ADD UNIQUE (col), ALGORITHM=INPLACE, LOCK=NONE; - 但
LOCK=NONE并非绝对无锁,仍可能在最后阶段短暂加 MDL 锁 - 提前在从库验证执行耗时,观察
SHOW PROCESSLIST中状态是否长时间卡在altering table - 如果表太大,可考虑分批更新+应用层双写校验,而非强依赖数据库约束
真正麻烦的不是语法,而是约束生效后对写入路径的连锁影响——比如应用没处理 1062 Duplicate entry 错误,就会直接崩掉。


















