MySQL 两列分别设 UNIQUE,为什么一列重复就触发更新
对照独立与组合唯一约束,检查真实冲突行及更新结果,变更索引前核对业务和数据依赖。
业务希望“门店和问卷都相同才更新”,表里却分别给 tienda、cuestionario 建了唯一索引。执行 INSERT ... ON DUPLICATE KEY UPDATE 时,只改问卷而门店不变,仍进入更新分支。先核对约束定义:两项独立唯一约束要求每一列分别唯一,和组合唯一约束不同。
看完整结构,不只看 INSERT 的两列
在实际数据库中读取表定义,下面的表名是示例:
SHOW CREATE TABLE questionnaire_entries;
如果约束片段是下面这样,它表达的是两条独立规则:
UNIQUE KEY uq_store (tienda),
UNIQUE KEY uq_questionnaire (cuestionario)
这里展示建表定义的片段,不是可以直接执行的迁移脚本。即使两列组合从未出现,只要其中一列与现有记录冲突,就可能触发更新。
MySQL 的这个语法没有 PostgreSQL 那样的 ON CONFLICT (列) 目标选择。主键或任一唯一索引冲突,都可能进入同一段更新逻辑;遗漏检查的第三个唯一索引,同样会影响结果。
组合唯一约束表达的是一对值
在两列都为 NOT NULL、业务确实允许同一门店有不同问卷、同一问卷出现在不同门店时,组合约束可以表达这对值的唯一性:
UNIQUE KEY uq_store_questionnaire (tienda, cuestionario)
仅增加组合索引,而保留原来的两项独立唯一约束,不会取消旧规则。是否移除它们要先核实业务、外键和其他查询依赖;允许 NULL 或使用前缀索引时,也需要重新检查实际唯一性含义,不能直接沿用这里的非空列判断。
变更前保存结构和恢复所需数据,查清已有重复组合及应用对对象身份的依赖,在受控迁移中处理。不要在生产上直接执行一段不含原索引名、关联关系和锁评估的 DROP INDEX 示例。
两个索引撞上不同记录,也不会同时更新两行
按 MySQL 8.4 的规则,多项唯一条件分别匹配不同记录时,只会更新其中一行,不能把它理解为逐个索引更新所有匹配行。也不要依赖猜测的索引顺序来指定业务对象。
若更新表达式又让被选中的记录违反另一个唯一约束,语句还可能因新的冲突被拒绝。INSERT IGNORE 会改变错误处理,REPLACE 也有不同的数据影响,都不应为了让步骤“成功”随手替换。
验收时分别覆盖原组合、只改门店、只改问卷,以及冲突分别指向不同记录的输入。检查实际行身份、字段值、记录数量与错误,不只看 affected rows。发现已更新了错误记录,应先停止无条件重放,再按保存的输入和原值恢复;撤回索引或 SQL 配置不会自动找回被覆盖的数据。


