MySQL唯一索引允许无限多个NULL值,因NULL≠NULL,故不触发唯一性校验;联合唯一索引中任一列为NULL即逃逸约束检查;要确保约束生效,所有参与列必须声明NOT NULL或改用非NULL默认值。

因为唯一索引字段中存在 NULL 值,MySQL 认为所有 NULL 互不相等,所以不会触发唯一性校验。
唯一索引对 NULL 的特殊处理
MySQL(InnoDB)规定:NULL 不等于 NULL。这意味着在联合唯一索引中,只要任意一列是 NULL,整行就“逃逸”了唯一约束检查。
- 例如索引
ux_category_unit_model(category_id,unit_id,model_hash),当model_hash为NULL时,即使category_id和unit_id完全相同,多条记录也能插入成功 - 这是标准 SQL 行为(SQL-92),不是 MySQL Bug,但常被误认为“索引失效”
- 单列唯一索引也一样:允许无限多个
NULL,只对非NULL值做去重
NOT NULL 是硬性前提
唯一索引要真正生效,参与索引的所有字段必须声明为 NOT NULL,否则约束形同虚设。
- 建表时应显式加约束:
model_hash VARCHAR(255) NOT NULL - 已有表需先清理
NULL数据,再执行:ALTER TABLE product_group_unique MODIFY model_hash VARCHAR(255) NOT NULL; - 如果业务上确实允许“无属性”,可改用默认值(如
'unset'或''),而非NULL
别指望 INSERT IGNORE 或 ON DUPLICATE KEY UPDATE 救场
这些语法只在唯一冲突发生时起作用;而 NULL 导致的“伪重复”根本不会触发冲突,所以它们完全无效。
-
INSERT IGNORE INTO product_group_unique (...) VALUES (1, 2, NULL);插入两次,都会成功 -
INSERT ... ON DUPLICATE KEY UPDATE同样不触发UPDATE分支 - 唯一约束没报错,就说明 MySQL 根本没把它当重复——这不是语法问题,是数据语义问题
真正容易被忽略的点是:你查 SHOW INDEX FROM product_group_unique 看到索引存在,就默认它“管用”;但只要字段允许 NULL,它就在关键场景下静默失效。校验必须落到每一列的 NULL 属性上,而不是只看索引定义本身。


















