唯一约束和唯一索引报错现象相同但机制不同:约束属逻辑层、支持延迟校验,索引属物理层、实时校验且不可延迟;外键必须引用约束而非索引;删除约束通常连带删索引,但仅删索引不解除约束语义。

唯一约束和唯一索引报错一模一样?别被骗了
报错看起来一样,但背后触发机制不同:两者都因违反唯一性而抛出类似错误,比如 SQL Server 的 Msg 2627、MySQL 的 ERROR 1062、PostgreSQL 的 duplicate key violates unique constraint。但关键区别在于——约束是逻辑校验层,索引是物理结构层;错误虽同源,可禁用/绕过方式完全不同。
- 禁用唯一约束(如 PostgreSQL 的
SET CONSTRAINTS ... DEFERRED或 SQL Server 的NO CHECK CONSTRAINT)后,插入重复值可能成功(取决于是否延迟校验),但对应唯一索引仍会拦住——索引无法“禁用校验”,只能DISABLE(SQL Server)或DROP(其他库) - Oracle 和 PostgreSQL 允许唯一约束延迟检查(
DEFERRABLE),但唯一索引从不支持延迟;一旦建好,每条 INSERT/UPDATE 都实时校验 - NULL 值处理陷阱:SQL Server 和 MySQL 中,多个
NULL在唯一索引下会被视为重复(报duplicate key value is (<null>)</null>),而 PostgreSQL 默认把NULL当作不参与比较——这点极易在迁移时翻车
INSERT IGNORE / ON CONFLICT 是索引还是约束在起作用?
答案是:全靠底层唯一索引。无论你定义的是 UNIQUE CONSTRAINT 还是 UNIQUE INDEX,INSERT ... ON DUPLICATE KEY UPDATE(MySQL)、INSERT ... ON CONFLICT DO UPDATE(PostgreSQL)这类语法,匹配的都是索引键,不是约束名。
- MySQL 的
INSERT IGNORE本质是捕获ERROR 1062并静默跳过,它依赖的是已存在的唯一索引(不管这索引来自约束还是手动创建) - PostgreSQL 的
ON CONFLICT ON CONSTRAINT uq_email看似指约束,实则必须确保该约束背后有索引;如果删掉索引只留约束(Oracle 可行,PG 不行),语句直接报错there is no unique or exclusion constraint matching the ON CONFLICT specification - SQL Server 没有原生
ON CONFLICT,得靠MERGE或IGNORE_DUP_KEY = ON——但后者只对唯一索引生效,对约束本身无效;设在约束上实际是设在它背后的索引上
大批量导入时,先删约束还是先删索引?
先删约束,再重建索引——更安全、更可控。因为约束删除会连带删掉它自动生成的索引(SQL Server/PostgreSQL/MySQL 均如此),而单独删索引可能导致约束失效却无提示。
- MySQL 场景:若表已有
UNIQUE INDEX idx_name,再加同名CONSTRAINT uq_name会失败;但反过来,先建约束再建同名索引也会报错Duplicate key name - 批量导入前,推荐操作链:
ALTER TABLE t DROP CONSTRAINT uq_col;→ 导入 →ALTER TABLE t ADD CONSTRAINT uq_col UNIQUE(col);。这样既清除了校验开销,又避免手动管理索引名冲突 - Oracle 是例外:唯一约束可不依赖索引存在(查
user_constraints.index_name为 NULL),此时删约束不删索引;但性能极差——没索引的唯一约束,每次 INSERT 都要全表扫描
为什么外键必须引用唯一约束,不能引用唯一索引?
因为外键依赖的是「约束的语义完整性」,不是「索引的物理结构」。数据库需要确保被引用列具备可验证、可声明、可继承的业务规则属性,而索引只是实现手段。
- PostgreSQL 和 SQL Server 明确要求
FOREIGN KEY ... REFERENCES table(col)中的col必须有UNIQUE CONSTRAINT或PRIMARY KEY;仅建UNIQUE INDEX会报错there is no unique constraint matching given keys for referenced table - MySQL 5.7+ 放宽了限制,允许外键引用唯一索引列,但这是特例,且不保证跨引擎兼容(如迁到 TiDB 就会失败)
- 一个现实坑点:用工具导出表结构时,可能只导出索引不导出约束,导致重建库时外键挂掉——务必检查
SHOW CREATE TABLE输出里有没有CONSTRAINT关键字
真正容易被忽略的,是那个“约束自动建索引”的默认行为——你以为删了约束就清掉了所有开销,其实只要没显式 DROP INDEX,那个索引还在磁盘上占着空间、拖慢写入;而反过来,以为建了唯一索引就等于有了约束保障,结果在外键或迁移场景下突然崩掉。











