当前位置: 首页 > news >正文

软删除设计:为什么使用 deleted_at = ‘1970-01-01 00:00:00‘ 表示未删除?

文章目录

  • 软删除设计: 为什么使用 deleted_at = '1970-01-01 00:00:00' 表示未删除?
    • 什么是软删除
    • 为什么使用 deleted_at = '1970-01-01 00:00:00' 表示未删除?
    • 实际演示:在 SQL 中实现软删除

软删除设计: 为什么使用 deleted_at = ‘1970-01-01 00:00:00’ 表示未删除?

在现代数据库设计中,尤其是在处理用户数据、审计追踪和合规要求的应用中,软删除 已成为硬删除的标准替代方案。软删除是通过标记记录为已删除而不实际从数据库中移除它们。这种方法保留了数据以供潜在恢复、历史分析或监管用途。

什么是软删除

**软删除(Soft Delete)**是一种数据删除策略,不是真正从数据库中删除记录,而是通过标记字段来表示记录已被"删除"。

为什么使用 deleted_at = ‘1970-01-01 00:00:00’ 表示未删除?

GORM 官方文档中关于软删除(Soft Delete)的说明位于「Delete」章节。
GORM 文档明确建议:

  • 如果你的模型包含一个gorm.DeletedAt字段(该字段包含在gorm.Model中),它将自动获得软删除能力!
  • 调用 Delete 时,记录不会从数据库中删除,但 GORM 会将 DeletedAt 的值设置为当前时间,并且使用常规查询方法将无法再找到该数据。

MySQL 官方文档里 唯一提到的“零值”是 0000-00-00 00:00:00。
在这里插入图片描述

MySQL permits you to store a “zero” value of ‘0000-00-00’ as a “dummy date.” In some cases, this is more convenient than using NULL values, and uses less data and index space. To disallow ‘0000-00-00’, enable the NO_ZERO_DATE mode.
MySQL 允许您将 ‘0000-00-00’ 的“零”值存储为“虚拟日期”。在某些情况下,这比使用 NULL 值更方便,并且使用的数据和索引空间更少。要禁止 ‘0000-00-00’,请启用 NO_ZERO_DATE 模式。

用 1970-01-01 00:00:00 代替 0000-00-00 00:00:00”是 社区/业务代码里的一种常见做法。完全取决于你的业务约定。

一般开发者为了避免 0000-00-00 00:00:00 带来的数据库严格模式报错、又希望保留“非 NULL”语义时,自发采用的一个“社区惯例”。

但是把“空/未知/缺失”时间写成 1970-01-01 00:00:00 既不会触发数据库 “zero date” 报错,又很容易被开发者一眼识别为“空值”。

这个做法最早在大量开源项目、博客、Stack Overflow 回答里被反复引用,久而久之就成了“约定俗成”。

设计思路:

deleted_at DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00'

1970-01-01 00:00:00 是Unix时间戳的起点,明显不是真实的删除时间,具有特殊意义,不会与实际删除时间冲突。

好处:
✅ 支持唯一约束:同一企业内可以有多个同名的已删除记录,但只能有一个未删除的
支持唯一约束:在多租户应用(如企业系统)中,您可能需要在租户内对名称等字段施加唯一约束。通过软删除,已删除记录保留其数据但被标记为非活跃。该方案允许同一名称的多个“已删除”记录(例如,用于历史版本),同时确保只有一个活跃(未删除)记录。这可以通过在复合唯一索引中包含 deleted_at 来实现。

✅ 索引效率高:deleted_at字段有固定值,索引查询更高效
固定、非 NULL 值如 Unix 纪元能够更好地利用索引。筛选活跃记录的查询(例如 WHERE deleted_at = ‘1970-01-01 00:00:00’)可以更有效地利用索引,而不是使用 IS NULL 或布尔标志的查询,后者在某些情况下可能需要全表扫描。这在高流量系统中会导致更快的读取。

✅ 历史兼容:'1970-01-01’是Unix时间戳起点,语义清晰
Unix 纪元是一个永恒的标准,确保长期兼容性。它不太可能与未来的时间戳冲突,并且与 Python 或 Java 等语言的 Unix 时间戳原生处理很好整合。此外,它避免了时区或夏令时的问题,因为它是一个固定点
所有主流语言和数据库都能无歧义地解析这个常量。

与布尔标志(如 is_deleted)相比,这种基于时间戳的方法提供了更丰富的信息元数据(例如,何时 被删除),而无需额外字段。

实际演示:在 SQL 中实现软删除

步骤 1:创建表

CREATE TABLE enterprises (id INT AUTO_INCREMENT PRIMARY KEY,tenant_id INT NOT NULL,name VARCHAR(255) NOT NULL,deleted_at DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00',-- 包含 deleted_at 的复合唯一索引,用于软唯一性UNIQUE KEY unique_name_per_tenant (tenant_id, name, deleted_at)
);

步骤 2:插入记录

-- 插入一个活跃的企业
INSERT INTO enterprises (tenant_id, name) VALUES (1, 'Acme Corp');-- 通过更新 deleted_at “删除”它
UPDATE enterprises SET deleted_at = NOW() WHERE id = 1;-- 插入同一名称的新活跃记录(允许,因为前一个已“删除”)
INSERT INTO enterprises (tenant_id, name) VALUES (1, 'Acme Corp');

步骤 3:查询活跃记录

-- 只获取活跃(未删除)的企业
SELECT * FROM enterprises WHERE deleted_at = '1970-01-01 00:00:00';-- 获取已删除记录用于审计
SELECT * FROM enterprises WHERE deleted_at > '1970-01-01 00:00:00';
http://www.dtcms.com/a/285680.html

相关文章:

  • 1-大语言模型—理论基础:详解Transformer架构的实现(1)
  • 零信任产品联合宁盾泛终端网络准入,打造随需而变、精准贴合业务的网络安全访问体系
  • python爬虫获取PDF
  • pdf格式怎么提取其中一部分张页?
  • PDF 拆分合并PDFSam:开源免费 多文件合并 + 按页码拆分 本地处理
  • Elasticsearch 和 solr 的区别
  • 【Docker#2】容器历史发展 | 虚拟化实现方式
  • C# 转换(引用转换)
  • uni-app 跳转页面传参
  • Docker --privileged 命令详解
  • Diffusion-VLA 中的 Reasoning Token 注入机制解析:语言推理如何控制扩散模型?
  • 2.3 前端-ts的接口以及自定义类型
  • k8s:手动创建PV,解决postgis数据库本地永久存储
  • 【unity实战】使用unity的Navigation+LineRenderer实现一个3D人物寻路提前指示预测移动轨迹的效果,并可以适配不同的地形
  • 搜索引擎优化全攻略:提升百度排名优化
  • 解决Flutter运行android提示Deprecated imperative apply of Flutter‘s Gradle plugins
  • 武汉江滩某码头变形及应力自动化监测
  • 麒麟操作系统unity适配
  • java前端基础--HTMLCSS、JavaScript、Vue、Ajax
  • Python网络爬虫实现selenium对百度识图二次开发以及批量保存Excel
  • unity实现梦日记式效果
  • CS课程项目设计3:支持AI人机对战的井字棋游戏
  • 「Java EE开发指南」如何用MyEclipse创建企业应用项目?(一)
  • UniApp -- 小程序自定义导航栏组件
  • 小程序常用api
  • 2025年算法备案发号规律总结与下半年发号预测
  • js中 new Set()实例的各个api使用
  • Http请求中的特殊字符
  • iOS加固工具有哪些?项目场景下的组合策略与实战指南
  • Axios泛型参数解析与使用指南