MySQL 条件唯一索引实战:用 delete_time 实现活跃记录唯一
在开发中,我们经常遇到这种需求:
“希望表里的某些组合在活跃状态下唯一,但已删除或软删除记录可以重复。”
比如我们有一个 客服提奖设置表,字段包括 admin_id、goods_type、goods_id、dis_commission 和 delete_time。
我们希望:
活跃记录(
delete_time IS NULL)必须唯一已删除记录(
delete_time不为 NULL)可以重复
一、传统做法的问题
在应用层判断
写查询判断是否存在
弊端:代码复杂、容易出错,性能差
触发器
BEFORE INSERT / UPDATE 检查
弊端:写法复杂,性能消耗大
MySQL 8.x 提供了更优雅的方案:生成列(Generated Column)+ 唯一索引。
二、实现思路
1. 生成列(Generated Column)
利用 delete_time 创建一个辅助列 admin_goods_unique_active:
ALTER TABLE dyy_shopro_customer_setting ADD COLUMN admin_goods_unique_active INT GENERATED ALWAYS AS ( CASE WHEN delete_time IS NULL THEN 1 ELSE NULL END ) VIRTUAL;活跃记录 → 1
已删除记录 → NULL
虚拟列:不占用存储空间,动态计算
2. 唯一索引
在原来的组合字段基础上加上生成列:
CREATE UNIQUE INDEX unique_admin_goods_commission_active ON dyy_shopro_customer_setting ( admin_id, goods_type, goods_id, dis_commission, admin_goods_unique_active );效果:
只检查活跃记录的唯一性
已删除记录不会触发唯一约束(MySQL 允许唯一索引包含多个 NULL)
三、通俗理解
把生成列想象成 一个开关:
| delete_time | admin_goods_unique_active | 是否检查唯一性 |
|---|---|---|
| NULL | 1 | 是 |
| 非 NULL | NULL | 否 |
开关开着(1) → 唯一索引生效,不能重复
开关关着(NULL) → 唯一索引忽略,允许重复
五、应用层异常处理
在 在业务层面可以判断数据是否重复 给出友好提示
1062 是 MySQL 唯一索引冲突错误码
可以直接返回给前端提示信息
六、总结
活跃记录唯一,删除记录允许重复
生成列自动计算,无需手动维护
性能好、安全可靠,不依赖应用层判断或触发器
通用性强,可以用于各种“条件唯一”场景
七、可视化理解(流程图)
delete_time │ ▼ 生成列 admin_goods_unique_active │ ├─> 1 (活跃) ─> 唯一索引检查唯一性 │ └─> NULL (已删除) ─> 唯一索引忽略
插入或更新时,生成列会自动计算
唯一索引只对生成列为 1 的记录生效
💡 一句话总结:
用
delete_time+ 生成列 + 唯一索引,你可以优雅实现“活跃记录唯一、删除记录可重复”的条件唯一索引,数据库自动保证安全性和唯一性。
