alter table ... add primary key 会因主键已存在、含空值或重复值而报错;需先drop primary key,修复数据后重加;主键顺序影响索引效率,innodb下还会触发全表重建。

主键已存在但没命名,ALTER TABLE ... ADD PRIMARY KEY 会报错
MySQL 不允许一张表有多个主键,但更隐蔽的问题是:如果表已有主键(比如建表时隐式定义、或通过 PRIMARY KEY 列定义生成),再执行 ALTER TABLE t ADD PRIMARY KEY (id) 会直接报 ERROR 1068: Multiple primary key defined。这不是语法错,而是约束冲突。
- 先确认是否存在主键:
SHOW CREATE TABLE t,看输出里有没有PRIMARY KEY字段或约束名 - 若已有主键但没显式命名,MySQL 内部会自动命名为
PRIMARY,无法重复添加 - 想替换主键?必须先用
ALTER TABLE t DROP PRIMARY KEY,再加新的 —— 注意:DROP PRIMARY KEY不需要指定列名,也不接受括号
给含空值或重复值的列加主键必然失败
主键要求非空(NOT NULL)且唯一,MySQL 在执行 ADD PRIMARY KEY 时会立刻校验全表数据。哪怕只有一行 NULL 或两个相同值,操作就中断,表结构不变。
- 检查空值:
SELECT COUNT(*) FROM t WHERE id IS NULL - 检查重复:
SELECT id, COUNT(*) FROM t GROUP BY id HAVING COUNT(*) > 1 - 修复后才可加:对
id列补全非空值(如用自增临时填充),再ALTER TABLE t MODIFY id BIGINT NOT NULL,最后加主键 - 别跳过校验:有些 ORM 或迁移工具会默认加
ALGORITHM=INPLACE,但空值/重复仍会拒绝,不是性能问题,是语义强制
ADD PRIMARY KEY 的列顺序影响查询性能,不只是语法要求
主键即聚簇索引,数据物理存储按主键顺序排列。如果主键是多列组合(如 (status, created_at, id)),那只有带 status 条件的查询才能高效走索引;单独查 created_at 基本无效。
- 单列主键最常见也最安全,优先选高区分度、稳定、不常更新的字段(比如自增
id或 UUID) - 复合主键慎用:除非业务强依赖(如联合唯一标识订单+子项),否则容易让
WHERE条件无法命中索引前缀 - 加主键时 MySQL 不会自动优化顺序 —— 你写
ADD PRIMARY KEY (a,b),它就按这个顺序建 B+ 树,不会重排
MyISAM 和 InnoDB 对主键添加行为一致,但底层影响完全不同
语法上 ALTER TABLE ... ADD PRIMARY KEY 在两种引擎下都能执行,但结果差异极大:InnoDB 会重建整张表(即使空表),MyISAM 只改表结构文件。这意味着在大表上操作,InnoDB 可能锁表几分钟,而 MyISAM 几乎瞬时。
- 查引擎:
SELECT ENGINE FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'db_name' AND TABLE_NAME = 't' - InnoDB 下加主键 = 全表重建 + 排序 + 重新写入数据文件,期间
INSERT/UPDATE/DELETE都被阻塞(除非用ALGORITHM=INPLACE且满足条件) - MyISAM 不支持在线 DDL,但加主键不涉及数据重排,所以快;不过 MyISAM 已淘汰,生产环境基本不用考虑
- 真正要注意的是:加主键后,InnoDB 的
row_format和fill_factor行为也会变化,可能影响后续写入性能











