软删除和唯一索引,二者不可得兼?
Posted jiangxiaoju
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了软删除和唯一索引,二者不可得兼?相关的知识,希望对你有一定的参考价值。
原创不易,未经允许,请勿转载。
文章目录
什么是软删除?
在说软删除之前,我们先来说一下什么是硬删除?硬删除也就是物理删除,是直接把数据从数据库中移除出去的,比如当用户执行了某个删除操作时,数据库就会相应的执行delete xxx where xxx
这种操作。使用这种删除方式的话,数据是不可恢复的,如果一定要恢复的话,只能通过日志。
而软删除也叫做逻辑删除,它并不是真正的把数据删除,而是用一个字段来标记当前数据是否是被删除的。使用软删除的时候,通常会在表中增加一个字段deletedAt
表示是否删除,如果deletedAt
字段类型为boolean的话,0可以表示未删除,1表示删除。若是deletedAt
为datetime
类型的话,如果数据未被删除,则该字段为null,否则则为删除的时间。
当使用软删除时,假设deletedAt
字段为datetime
类型,那么当执行删除操作时,会把原本的delete xxx
变成update set deleteAt=now() where xxx
。而在实行查询或者更新操作时,都会在条件中加上deletedAt is not null
,例如select * from xxx where deletedAt is not null
怎么实现软删除?
正如上面所说,实现软删除一般都是通过一个字段来做标记。一般的orm框架都集成了软删除的功能。
例如Java中的Mybatis-Plus,就提供了软删除的实现,(下面这段说明摘抄自Mybatis-Plus官方文档)
说明:
只对自动注入的 sql 起效:
- 插入: 不作限制
- 查找: 追加 where 条件过滤掉已删除数据,且使用 wrapper.entity 生成的 where 条件会忽略该字段
- 更新: 追加 where 条件防止更新到已删除数据,且使用 wrapper.entity 生成的 where 条件会忽略该字段
- 删除: 转变为 更新
例如:
- 删除: update user set deleted=1 where id = 1 and deleted=0
- 查找: select id,name,deleted from user where deleted=0
字段类型支持说明:
- 支持所有数据类型(推荐使用 Integer,Boolean,LocalDateTime)
- 如果数据库字段使用datetime,逻辑未删除值和已删除值支持配置为字符串null,另一个值支持配置为函数来获取值如now()
在Go语言中,GORM框架也提供了软删除的支持。(以下摘抄自gorm官方文档)
如果您的模型包含了一个 gorm.deletedat 字段(gorm.Model 已经包含了该字段),它将自动获得软删除的能力!
拥有软删除能力的模型调用 Delete 时,记录不会被从数据库中真正删除。但 GORM 会将 DeletedAt 置为当前时间, 并且你不能再通过正常的查询方法找到该记录。
// user 的 ID 是 `111`
db.Delete(&user)
// UPDATE users SET deleted_at="2013-10-29 10:23" WHERE id = 111;
// 批量删除
db.Where("age = ?", 20).Delete(&User)
// UPDATE users SET deleted_at="2013-10-29 10:23" WHERE age = 20;
// 在查询时会忽略被软删除的记录
db.Where("age = 20").Find(&user)
// SELECT * FROM users WHERE age = 20 AND deleted_at IS NULL;
如果您不想引入 gorm.Model,您也可以这样启用软删除特性:
type User struct
ID int
Deleted gorm.DeletedAt
Name string
什么是唯一索引?
唯一索引指的是在数据库层面来约束数据的唯一性。唯一索引可以建立在一个字段上,也可以是在多个字段上。例如账号是不可以重复的,那么就需要在账号这个字段上,加上唯一索引进行约束。比如对于同一个课程,每个学生只能选择一次,那么就需要在学生ID和课程ID这两个字段上建立唯一索引进行约束。
软删除和唯一索引只能选一个?
最近在做的项目中遇到了这么一个问题。
有一个用户表,定义如下,在username字段上用了唯一索引标注,使用deleted_at字段标记是否删除。
create table user(
id int not null,
username varchar(20) not null unique,
deleted_at datetime default null,
primary key(id)
)
比如username
为xiaoming
的用户被删除了(即delete_at
字段不为null
了),之后有添加添加username
为xiaoming
的用户就会报错。因为虽然之前的xiaoming
是被删除了,但是实际还是存在表中的,只是我们看不到了而已。
既然这样不行,那该怎么办?我们可以用联合唯一索引的形式,unique key uk_username_deleted(username,deleted_at)
在username
和deleted_at
这两个字段上建立唯一索引约束。
不过这看似解决问题了,但是当我尝试的时候,又双叒叕出现了问题。
插入了两个username为xiaoming的数据也可以。这是因为deleted_at
字段默认为null
,导致索引失效了。
既然当deleted_at
默认为null的时候会导致索引失效,那不如我们可以给deleted_at
一个默认值,比如把deleted_at
字段改成int类型,未删除则用0表示,删除的话,则填入删除时的时间戳。
因为项目中是使用gorm作为数据库操作的,而它默认提供的软删除方案则是上面说所的使用时间来标记是否删除了。不过好在官方gorm官方也提供了第二种方案,
这个插件实现了我所说的第二种解决思路。
import "gorm.io/plugin/soft_delete"
type User struct
ID uint
Name string
DeletedAt soft_delete.DeletedAt
// Query
SELECT * FROM users WHERE deleted_at = 0;
// Delete
UPDATE users SET deleted_at = /* current unix second */ WHERE ID = 1;
如果这篇文章对您有所帮助,麻烦点个一键三连。
原创不易,未经允许,请勿转载。
以上是关于软删除和唯一索引,二者不可得兼?的主要内容,如果未能解决你的问题,请参考以下文章