在企业数据库设计中,经常会遇到一个需求,就是希望把操作之前的数据保留下来,能够看到操作之前是什么数据,操作之后是什么数据。对于这种需求,我们可以使用保留历史数据或者使用版本来实现。

为了能够保留历史数据,在版本设计时有以下方案:

一、使用版本号

版本号是一种常见的版本设计方案,就是在要进行历史数据保留的表上面增加一个版本号字段,该字段可以是DateTime类型,也可以是int类型,每进行数据操作时,都是创建一个新的版本,版本是只增不减的,所以只需要拿到最大一个版本号,就能得到最新的业务数据。

版本号除了能够用于留存历史数据外,还有一个功能就是避免并发编辑操作。比如我们有一个对象A,当前的版本是1,两个用户同时打开了该对象的编辑页面,进行数据更改。先是甲用户提交更改,这个时候系统把对象的ID和版本进行查询,发现要修改的数据最新版本是1,所以成功修改,保存了对象A的新版本2。这个时候用户乙也提交了修改。系统把对象的ID和版本1进行查询,发现要修改的数据最新版本是2,不符合要求,所以拒绝用户乙的修改。用户乙只有刷新界面,拿到最新的版本2,再进行修改。

ID 单号 金额 版本号
1 EXP123 100 1

在使用版本号的情况下,对单据的金额进行修改,修改后创建新的版本号2:

ID 单号 金额 版本号
1 EXP123 100 1
2 EXP123 120 2

二、使用生效、失效时间

保存历史数据的第二办法是使用生效失效时间来表示一个版本。要进行历史数据记录的表增加“生效时间”“失效时间”两个字段,两个字段不允许为空。对于刚创建的数据,生效时间是创建该数据的时间,失效时间是9999-12-31。现在对这条数据进行了修改,那么我们只需要将当前时间设置为上一个版本的失效时间,同时创建一条新数据,生效时间是当前时间,失效时间是9999-12-31即可。

ID 单号 金额 生效时间 失效时间
1 EXP123 100 2013/9/1 15:30:00 9999/12/31 23:59:59

比如上面一条单据,是2013-9-1创建的,后来在2013-9-9 15:00:00对该单据进行修改,将金额从100修改为120,保存时创建的新数据如下:

ID 单号 金额 生效时间 失效时间
1 EXP123 100 2013/9/1 15:30:00 2013/9/9 15:00:00
2 EXP123 120 2013/9/9 15:00:00 9999/12/31 23:59:59

使用了生效、失效时间后,我们可以查询任意时刻数据库中数据的值,只需要把要查询的时刻传入,然后between 生效时间 and 失效时间即可。

使用前两种方案都需要一个业务主键来标识具体的一个业务数据。如果我们要记录的实体没有明确的“单号”、“订单号”这类的业务主键该怎么办?我们可以使用创建数据时的数据库主键作为业务主键。

员工ID 姓名 生日 业务ID 版本号
1 张三 1984/12/29 1 1

比如我们有个员工表,记录员工基本信息,在创建张三这个员工的数据时,其在数据库的ID为1,那么可以将其业务ID也设置为1。接下来对张三的属性进行更改,记录了版本,那么就会创建新的版本,其主键“员工ID”会变化,但是其业务主键“业务ID”始终是1,不会变化的。

员工ID 姓名 生日 业务ID 版本号
1 张三 1984/12/29 1 1
2 张三 1985/1/9 1 2

使用前面两个方案虽然能够很好的记录历史数据,但是每次修改数据都会导致新版本生成保存,所以每个版本的ID都是新的,所以必须有一个业务主键来标识一个实体,这里的两个例子“单号”就是其业务主键。主键的变动使得所有关联的对象都得变动,从而形成连锁效应,使得各个关联的对象也生成新的版本。比如我们有个订单系统,里面有订单表和订单明细表。现在我们要对订单的修改记录历史版本,所以增加了生效时间和实效时间,并使用订单号作为业务主键。现在有一个订单A,下面有100条明细,如果要对订单进行修改,将某一条明细的属性进行修改,从而导致整个订单的变化,那么我们就需要创建新的订单数据行,由于主键变动,所以订单明细都需要变动,所以100条明细都需要创建新的版本,新版本的订单明细中,“订单ID”指向了新的版本的订单数据的ID。

这样的设计造成的问题就是订单明细表会极速膨胀,如果一个订单有1000条明细,我们只是修改了订单本身的属性,并不修改订单明细,也会造成对这1000条明细做Copy,然后保存。那怎么办呢?我们可以使用以下办法:

1.对订单明细建立版本字段,将版本的粒度细化到订单明细,而不是订单。订单与订单明细不存在数据库级的外键关系,只存在业务级的外键关系。也就是说订单明细表中增加生效时间、失效时间之外,还需要增加“订单号”这个字段,用于表名该明细是属于哪个订单的。

我们这么修改后,如果订单对象进行了修改,订单明细没有修改(比如改了一下收件人信息),那么只需要在订单表中生成新的一行数据,订单明细不会Copy生成新的数据。如果我们对某一条订单明细进行了更改(比调整了单价、数量)那么只需要对具体修改的那条订单明细进行更改,而不需要对整个订单的所有明细进行更改。

使用这种设计后,查询订单及其明细,需要对两个表执行生效失效时间的过滤,而且明细的获取是通过订单号去取,而不是通过订单ID去取。

将版本控制的粒度细化到订单明细时,后台程序的逻辑也会更加复杂。用户在界面上操作的是订单对象,系统会将整个修改后的订单对象传到后台,后台程序需要对每个订单项进行对比,如果发现订单项进行了修改,那么就会调用生成新版本订单明细的方法。

2.使用单独的历史表

这是另外一种实现历史版本记录的方法:

三、使用单独的历史表

使用历史表其实就是建立完全相同Schema的表(当然,也可以添加更多的字段用于记录额外的历史版本信息),该表只保留历史版本的数据。这有点像一个归档逻辑,所有历史版本我们认为都应该是不经常访问的,所有可以扔到单独的表,对于现有生效的版本,仍然保留在原表中,如果需要查询历史版本,那么就从历史表中查询。

使用单独的历史表有以下好处:

  • 业务数据表的数据量不会因为历史版本记录而膨胀。因为历史数据都记录到了另外一个表中,所以业务数据表只记录了一份数据。
  • 业务数据表的Schema不需要调整,增加额外的版本字段。由于对原有数据表不做Schema变更,所以原有查询逻辑也不用更改。对于一个现有的数据库设计,在增加历史数据记录功能时更简单。
  • 业务数据表可以直接进行update操作,不会生成新的ID。由于ID不会变,所以我们并需要业务主键应用到程序逻辑中。

使用历史表记录历史版本主要是要对数据操作方法(增加、删除、修改)进行修改,使得每次数据操作时,先在历史表中留痕,然后再进行数据操作。另外就是对查询历史版本功能进行修改,因为历史数据在另外一个表中,所以对于的SQL是不一样的。当然,我们也可以创建历史版本数据库,里面保存了所有的历史表。

数据库模型设计——历史表与版本号设计相关推荐

  1. MySQL设计工厂管理数据库(Ⅰ)—表结构设计

    MySQL设计工厂管理数据库(Ⅰ)-表结构设计 引言 设计思路 工厂管理E-R图 设计工厂管理逻辑图 实现过程 项目(project)表实现 职工(staff)表设计 零件(components)表设 ...

  2. 订单表的字段类型 mysql_Mysql数据库下订单表如何设计?

    Mysql数据库下订单表如何设计 商品表和订单表 . 通过一个表来关联. 那删除了商品,相关联的订单表如何显示出这个已经删除的商品 订单表需要冗余商品名.商品编号.价格等基本信息. 不能只保存一个商品 ...

  3. 关系型数据库大数据性能优化解决方案之:分表(当前表历史表)、表分区、数据清理原则

    原因和目的 由于交易量大或者日积月累造成数据库的数据量越来越大.会导致系统性能大幅下降,所以要对部分业务的表数据作备份和清理 减少数据量,来提升请求响应的速度,提升用户体验 数据是否需要清理的阀值判断 ...

  4. pdman 创建表同步数据库_pdman 创建表同步数据库_使用PDMan来设计数据库系列1-PDMan软件的简介与安装...

    前言 最近除了做好手上的工作外,也需要开始设计一套数据库用来存储实验室的全套数据,可能会涉及多个表的交叉引用,为了更加高效和准确的进行设计,需要用到数据库模型建模工具.这里选择的是开源的PDMan. ...

  5. Atitit. 数据库-----catalog与schema的设计区别以及在实际中使用 获取数据库所有库表 java jdbc php  c#.Net...

    Atitit. 数据库-----catalog与schema的设计区别以及在实际中使用 获取数据库所有库表 java jdbc php  c#.Net 1. -catalog与schema的设计区别1 ...

  6. mysql数据库实验3查询_MySQL数据库实验:任务三 数据库的单表查询设计

    任务三 数据库的单表查询设计 文章目录任务三 数据库的单表查询设计[实训目的与要求][实训原理][实训步骤]一.简单查询二.按条件查询1.比较大小查询2.带in关键字的查询(确定集合)3.带BETWE ...

  7. mysql历史表_MySQL历史表设计和查询

    TL; DR:这个设计是否正确,我应该如何查询? 我们假设我们有city和地址的历史记录表,其设计如下: CREATE TABLE city_history ( id BIGINT UNSIGNED ...

  8. 解决:Activiti7与SpringBoot整合时,默认生成的activiti数据库中只有17张表,无另外8张历史表

    问题 Activiti7与SpringBoot整合时,默认生成的activiti数据库中只有17张表,无另外8张历史表. 原因 Activiti默认关闭了历史表的使用. 解决 在连接数据库的appli ...

  9. 解决Activiti 7自动部署后不生成数据库act_hi _* 历史表

    使用Activiti 7时,启动项目会自动部署生成数据库表,但在默认配置下会发现数据库中没有act_hi _* 历史表,此时我们在配置文件中加上以下配置即可: spring:activiti:db-h ...

最新文章

  1. linux 修改图片的尺寸
  2. 3.7 非极大值抑制-深度学习第四课《卷积神经网络》-Stanford吴恩达教授
  3. 计划继续深化学习物联网系统的相关知识 目前的WiFi模块基于ESP8266
  4. lcc-win32使用指南
  5. Struts2学习笔记(六) 结果(Result)(上)
  6. 自动驾驶算法-滤波器系列(三)——不同运动模型(CV、CA、CTRV、CTRA)的建模和推导
  7. 【Maxscript入门】学习笔记整理一
  8. 远程桌面提示“用户帐户限制(例如,时间限制)会阻止你登录。请与系统管理员或技术支持联系以获取帮助。”
  9. MySQL之my.cnf配置文件
  10. html 导航栏颜色代码,CSS实现五颜六色按钮组成的导航条效果代码
  11. com.baomidou.mybatisplus.core.exceptions.MyBatisPlusException: java.net.UnknownHostException
  12. 深入浅出filament Android编译脚本
  13. dr.oracle素颜霜好用吗,蒂佳婷素颜霜怎么样?蒂佳婷素颜霜好用吗
  14. python查询银行汇款_基于Python实现一个简单的银行转账操作
  15. Magic Potion(最大流,跑两遍网络流或者加一个中转点)
  16. java笔记——多线程笔记
  17. 2023年天津财经大学珠江学院专升本报名缴费准考证下载考试须知
  18. 理财-国债逆回购详细操作
  19. 园林景观cad_如何不花一分钱自学CAD?这几个网站收好!海量免费教程看不完
  20. C++ 函数对象学习笔记

热门文章

  1. KF与无迹卡尔曼滤波详解
  2. 2023年全国最新工会考试精选真题及答案51
  3. 2023年全国最新工会考试精选真题及答案10
  4. php文件目录教程,详谈PHP文件目录基础操作_PHP教程
  5. java出现次数最多的数_java如何找出一个int数组中出现次数最多
  6. 入职第一天,我接手了号称【屎山】的祖传代码,这还能卷吗???
  7. 科学幻想其实是对科学研究的发展方向起到很好的引领作用
  8. 网络基础——100道面试题,你能答对多少?
  9. 厚基础Linux——第七周作业
  10. QT之鼠标点击事件学习