如何设计具有修订历史的数据库?

2023-12-19

我是构建新的团队的一员内容管理系统对于我们的公共网站。我正在尝试找到最简单和最好的方法来构建版本控制机制。对象模型非常基本。我们有一个摘要BaseArticle包含版本无关/元数据属性的类,例如Heading & CreatedBy。许多类都继承自此,例如DocumentArticle拥有以下财产URL这将是一个文件的路径。WebArticle也继承自BaseArticle并包括FurtherInfo财产和集合Tabs对象,其中包括Body它将保存要显示的 HTML(Tab 对象不派生自任何内容)。NewsArticle and JobArticle继承自WebArticle。我们还有其他派生类,但这些提供了足够的示例。

We come up with two approaches to persistence for Revision Control. I call these Approach1 and Approach2. I've used SQL Server to do a basic diagram of each:Database Diagram of Approach 1 Database Diagram of Approach 2

  • With 方法1,该计划将针对新版本Articles通过数据库持久化Update。将为更新设置触发器并将旧数据插入到xxx_Versions桌子。我认为需要在每个表上配置一个触发器。这种方法确实有一个优点,即唯一head每篇文章的版本都保存在主表中,旧版本被分开。这使得将文章的头版本从开发/登台数据库复制到Live one.
  • With 方法2,该计划将针对新版本Articles要插入到数据库中。文章的头部版本将通过视图来识别。这似乎具有更少的表和更少的代码(例如没有触发器)的优点。

请注意,对于这两种方法,计划都是为映射到相关对象的表调用 Upsert 存储过程(我们必须记住处理添加新文章的情况)。此 upsert 存储过程将为其派生的类调用该存储过程,例如upsert_NewsArticle会打电话upsert_WebArticle etc.

我们正在使用SQL Server 2005,尽管我认为这个问题与数据库风格无关。我在互联网上进行了广泛的搜索,并找到了这两种方法的参考资料。但我还没有发现任何东西可以比较两者并表明其中一个更好。我认为对于世界上所有的数据库书籍来说,这种方法的选择一定已经出现过。

我的问题是:其中哪一个方法是最好的,为什么?


我的实现可能有点复杂。

首先,您只有一个表来处理所有事情,以便仅在一个点上保持模型设计和数据完整性。

这是基本思想,您可以扩展设计created_by & updated_by如果需要的话。

在 MySQL 上实现

下面的实现是为了MySQL,但这个想法也可以在其他类型的 SQL 数据库上实现。

Table

DROP TABLE IF EXISTS `myTable`;

CREATE TABLE `myTable` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'Primary Key',
  `version` int(11) NOT NULL DEFAULT 0 COMMENT 'Version',
  `title` varchar(32) NOT NULL COMMENT 'Title',
  `description` varchar(1024) DEFAULT NULL COMMENT 'Description',
  `deleted_at` datetime DEFAULT NULL COMMENT 'Record deleted at',
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Record created at'
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

ALTER TABLE `myTable`
  ADD PRIMARY KEY (`id`, `version`) USING BTREE,
  ADD KEY `i_title` (`title`);
  • The 记录ID定义为id & version.
  • With deleted_at,该模型支持软删除特征。

Views

获取当前版本

获取当前记录版本:

CREATE OR REPLACE VIEW vMyTableCurrentVersion AS
SELECT
    `id`
  , MAX(`version`) AS `version`
  , MIN(`created_at`) AS `created_at`
FROM `myTable`
GROUP BY `id`;

获取所有记录(包括已删除的记录)

获取所有记录,包括软删除记录:

CREATE OR REPLACE VIEW vMyTableAll AS
SELECT
    T.id
  , T.version

  , T.title
  , T.description

  , T.deleted_at
  , _T.created_at
  , T.created_at AS `updated_at`
FROM
  `myTable` AS T
  INNER JOIN vMyTableCurrentVersion AS _T ON
    T.id = _T.id
    AND T.version = _T.version;

获取记录

获取记录,删除软删除结果记录。

CREATE OR REPLACE VIEW vMyTable AS
SELECT *
FROM `vMyTableAll`
WHERE `deleted_at` IS NULL;

触发器和验证

对于这个例子,我将实现一个unique title验证:

DROP PROCEDURE IF EXISTS myTable_uk_title;
DROP TRIGGER IF EXISTS myTable_insert_uk_title;
DROP TRIGGER IF EXISTS myTable_update_uk_title;

DELIMITER //

CREATE PROCEDURE myTable_uk_title(id INT, title VARCHAR(32)) BEGIN
  IF (
    SELECT COUNT(*)
    FROM vMyTable AS T
    WHERE
      T.id <> id
      AND T.title = title
  ) > 0 THEN
    SIGNAL SQLSTATE '45000'
    SET MESSAGE_TEXT = 'Duplicated "title"', MYSQL_ERRNO = 1000;
  END IF;
END //

CREATE TRIGGER myTable_insert_uk_title BEFORE INSERT ON myTable
FOR EACH ROW
BEGIN
  CALL myTable_uk_title(NEW.id, NEW.title);
END //

CREATE TRIGGER myTable_update_uk_title BEFORE UPDATE ON myTable
FOR EACH ROW
BEGIN
  CALL myTable_uk_title(NEW.id, NEW.title);
END //

DELIMITER ;

使用示例

Select

SELECT * FROM `vMyTable`;

选择已删除的记录

SELECT * FROM `vMyTableAll`;

插入/添加/新建

INSERT INTO myTable (`title`) VALUES ('Test 1');

更新/编辑

更新操作应使用以下代码完成,而不是UPDATE ...:

INSERT INTO myTable (`id`, `version`, `title`, `description`)
SELECT
    `id`
  , `version` + 1 as `version` -- New version
  , `title`
  , 'New description' AS `description`
  FROM `vMyTable`
  WHERE id = 1;

软删除

The 软删除行动是历史上的其他点:

INSERT INTO myTable (`id`, `version`, `title`, `description`, `deleted_at`)
SELECT
    `id`
  , `version` + 1 as `version` -- New version
  , `title`
  , `description`
  , NOW() AS `deleted_at`
  FROM `vMyTable`
  WHERE id = 1;

恢复软删除的记录

INSERT INTO myTable (`id`, `version`, `title`, `description`, `deleted_at`)
SELECT
    `id`
  , `version` + 1 as `version` -- New version
  , `title`
  , `description`
  , null AS `deleted_at`
  FROM `vMyTableAll` -- Get with deleted
  WHERE id = 1;

删除记录和历史记录

To delete相关历史记录:

DELETE FROM `myTable` WHERE id = 1;

记录历史

SELECT *
FROM `myTable`
WHERE id = 1
ORDER BY `version` DESC;

Cons

  • 唯一键约束不可能,但您可以创建一个Trigger来处理它。
  • Update同时有许多记录(UPDATE ...)如果你想保存历史记录是不可能的。
  • Delete同时有许多记录(DELETE ...)如果你想保存历史记录是不可能的。

Refs

  • https://gist.github.com/reduardo7/e5846b17eba288006579fc35269f3e96 https://gist.github.com/reduardo7/e5846b17eba288006579fc35269f3e96
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何设计具有修订历史的数据库? 的相关文章

  • 如何在asp.net中按下按钮后刷新Gridview

    我正在尝试制作一个简单的图书馆数据库 我在网格视图中列出搜索结果 然后有一个文本框和一个按钮 用户输入 isbn 并单击贷款按钮 然后 如果有足够数量的物品 itemNumber gt 0 则由用户借出 这是用户界面的屏幕截图 我的问题是
  • 让 Prometheus 发送 SQL 查询

    我正在尝试使用普罗米修斯 https prometheus io 监视我的 MySQL 数据库 但似乎找不到添加 SQL 查询的区域 例如 我想运行一个返回值的 SQL 查询 然后将该值添加到图表中 发送警报 有没有办法让 Promethe
  • SQL:查找每个跑步者跑步之间的平均天数

    因此 如果我们给出下表 runner ran Carol 2011 02 01 Alice 2011 02 01 Bob 2011 02 01 Carol 2011 02 02 Bob 2011 02 02 Bob 2011 02 03 B
  • SQLSTATE[HY000] [2002] 资源暂时不可用 - mysql - innodb 和 pdo

    在我的错误日志中得到大量结果 如下所列 数据库中的所有表都是 innodb 并且就与这些表的任何交互而言 一切都是带有准备好的语句的 pdo 正如我所说 所有错误几乎与下面列出的错误相同 但发生在几个不同的页面上 无论页面如何 错误行始终指
  • 不是 select 中带有 MAX 的单组组函数

    Select sg gameno Max sg Year sg end sg hostcity country olympic name from Summergames s Country co where s country isoco
  • 在 MySQL 数据库上使用版本控制 (Git)

    我是一名 WordPress 设计师 开发人员 越来越多地使用版本控制 特别是 Git 尽管我确实在某些项目中使用 SVN 我目前正在使用 Beanstalk 作为我的远程仓库 将所有 WordPress 文件添加到我的存储库中是没有问题的
  • 是否需要连续编号?

    我正在开发一个 winform NET 应用程序 其中包括订单 发票 服务订单 票务等 这些实体在对其 ID 进行编号时是否必须按顺序排列 国际海事组织没有 以一个订单为例 它只有通过业务层才有效 在此过程中 可能已经创建了另一个订单 批准
  • SQL Server递归查询显示父级路径

    我正在使用 SQL Server 语句并有一张表 例如 item value parentItem 1 2test 2 2 3test 3 3 4test 4 5 1test 1 6 3test 3 7 2test 2 我想使用 SQL S
  • 多级排序

    我有一个表 其中包含一些记录 其中包含名称 评级等字段 我首先想要根据评级将结果限制为 20 进行排序 然后在此结果集上想要进一步应用基于名称的排序 我知道要排序我们需要使用像这样的查询 Select from table order by
  • SQLite 条件 ORDER BY 中的 DESC

    我需要选择按以下逻辑排序的记录 但是当 DESC 处于条件中时 SQLite 会引发错误 ORDER BY CASE WHEN parentGUID IS NULL THEN datePosted DESC ELSE datePosted
  • 如果我的应用程序安装在 SD 卡上,私人数据也在那里吗?

    我假设应用程序的私有数据 例如 SharedPreferences 和 SQLite 数据库 位于手机的内部存储而不是 SD 卡上 即使应用程序本身安装在 SD 卡上 我在任何地方都找不到对此的简单明确的确认 有人可以确认一下吗 是的 私有
  • Slick 中的 Scala 枚举(案例对象),良好实践

    假设我有一个代表一组几个有效状态的特征 将对象存储在数据库中是一个好习惯吗 存储 Int 并使用隐式函数 MappedColumnType base Int DoorState 将它们映射到 DoorState 会更好吗 trait Doo
  • 按时间戳聚合

    搜索引擎优化 gt 搜索引擎优化 gt 付费 1 付费 gt 付费 gt 联盟 gt 付费 1 SEO gt 会员 1我有一个查询 结果包含客户 ID 号 营销渠道 时间戳和购买日期的数据 所以 结果可能看起来像这样 id marketin
  • 在 SQL Server SELECT 语句中使用 CASE 时消除 NULL

    我有一份大而混乱的报告要写 它连接了 5 个表 一个表中有一列用于多个不同的值 本质上是一个 标签 列 其中标签根据用户想要使用的各种元数据的类型以创造性的方式使用 因此 我对报告的查询返回 3 个几乎相同的行 仅 标签 列有所不同 例如
  • Netezza SQL 将 VARCHAR 转换为二进制字符串

    我有一个位图存储为VARCHAR在内特扎 需要转换一下VARCHAR转换为 Netezza 中的二进制字符串 输入 Netezza col 值 VARCHAR 0xFFFFFFFFFFFFFFFF 期望的输出 VARCHAR gt 1111
  • 在 Sql Server 中启用 DTD 支持

    我有各种 xml 文档需要存储在数据库列中 这些文档包含对 DTD 的引用 并且 SQL Server 不会导入 xml 因为它存在安全风险 如何在数据库上启用 DTD 支持 以便它可以让我插入 xml 内容 你必须CONVERT首先 MS
  • Django 模型同步表

    如果我更改 Django 模型中的字段 如何将其与数据库表同步 我是否需要在数据库上手动执行此操作 或者是否有工具可以帮助完成此过程 唉 Django 不支持任何简单的解决方案 django 唯一能为你做的就是使用与新模型匹配的新表重新启动
  • PLSQL 中的时区转换

    我需要将系统日期和时间转换为特定时区 例如东部时间 我无法假设我当前的时区 如何在plsql中转换它 请帮我 假设你有一个TIMESTAMP WITH TIME ZONE 例如systimestamp 您可以使用AT TIME ZONE句法
  • PDO 连接字符串:最好的方法是什么? [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 我想使用 php pdo 制作一个后端应用程序 我发现了很多不同的方法来处理 PDO 连接字符串 我想知道使用 pdo 执行连接字符串的最佳方法
  • 用于桌面数据库应用程序的 Python 框架

    是否有一个框架可以为Python开发桌面数据库应用程序 一些带有CRUD屏幕的屏幕 我正在寻找类似于 Windows 窗体的东西 能够将 TextField Combos 和其他 UI 隐喻与datasets连接到关系数据库例如 MySQL

随机推荐

  • ngx-datatable 的通用“包装”组件

    一些介绍 我们目前正在开发一个基于 Angular2 的应用程序 该应用程序数据量很大 为了显示这些数据 我们决定给出ngx 数据表 https github com swimlane ngx datatable尝试一下 需要大量组件来显示
  • Android:创建自定义容器视图

    我正在尝试在 android 中创建一个自定义视图 或更好的布局 它用作 2 个子视图的容器 将其视为垂直分隔 2 个容器的条形图 可以上下滑动 我想像 xml 中的布局一样使用它 以便您可以在其中嵌套任何视图 我想到了类似的事情
  • 更新企业应用程序的 iOS 开发和 APNs 生产 [AirWatch]

    我为我的公司开发了一个 iOS 应用程序 我们通过 AirWatch 分发它 有些证书即将过期 试图弄清楚该怎么做是非常令人困惑的 我用谷歌搜索了一下 似乎每个页面都有不同的信息 如果有人能指出我正确的方向 我真的很高兴 我有一张 iOS
  • glGenerateMipmap 需要哪个内存屏障?

    我已使用 GL ARB shader image load store 写入纹理的第一个 mipmap 级别 文档指出 在其他操作中使用该图像的内容之前 我需要调用 glMemoryBarrier 以便适当地刷新缓存 例如 在执行 glTe
  • MPNowPlayingInfoCenter 与 Apple Music 冲突

    我正在开发一个在后台播放音乐的音乐播放器 该应用程序与 Spotify 和 Apple Music 集成 用户将仅在其中一项服务中进行身份验证 目前 我可以使用这两种服务在应用程序和后台播放音乐 我也能够设置MPNowPlayingInfo
  • MySql 查询:从表中为每个类别选择前 3 行

    我有一个包含记录的表 它有一行名为category 我插入了太多文章 我只想从每个类别中选择两篇文章 我尝试做这样的事情 我创建了一个视图 CREATE VIEW limitrows AS SELECT FROM tbl artikujt
  • 为什么在 PHP 中对日期格式为“YYYY-MM-DD”的两个字符串进行小于或大于比较会起作用,即使它们是字符串?

    我正在为一个项目编写一段 PHP 代码 该项目将 YYYY MM DD 格式的日期与当前日期进行比较 以查看它是否小于当前日期 在代码的不同点 使用了两种不同的方法进行比较 第一个使用的get timestamp 日期并根据时间戳进行比较
  • 在 MATLAB 中使用转置与 ctranspose

    在 MATLAB 中转置向量 矩阵时 我只看到并使用了 撇号 运算符很长一段时间 例如 gt gt v 1 2 3 v 1 2 3 然而这是共轭转置正如我最近发现的那样 或者ctranspose 这似乎只在涉及复数时才重要 如果你想转置矩阵
  • 旋转时调整 UINavigationBar 的大小

    我有一个 UIViewController 的子类 它处理 UIView 视图控制器以模态方式呈现 它从屏幕底部向上滑动 在视图的顶部 我添加了一个导航栏 请注意 该栏不是由导航控制器处理的 我想让导航栏在视图旋转到横向时缩小高度 类似于
  • 如何更改 iOS7 中 UISearchBar 的取消按钮色调颜色

    我想将 Textfield 的色调颜色更改为蓝色 并将 UISearchBar 的取消按钮色调颜色更改为白色 我正在使用下面的代码 for UIView subView in searchBar subviews for UIView nd
  • Python 列表与数组——何时使用?

    如果要创建一维数组 可以将其实现为列表 或者使用标准库中的 array 模块 我一直对一维数组使用列表 我想改用数组模块的原因或情况是什么 是为了性能和内存优化 还是我错过了一些明显的东西 基本上 Python 列表非常灵活 可以保存完全异
  • Python 日志记录不会关闭

    我一直在学习 python 日志记录模块 但在完成后关闭日志记录时遇到问题 这是一个例子 import logging log logging getLogger log setLevel logging INFO handler logg
  • emacs 更改默认行结尾

    在 Windows 中 Emacs 使用 cr lf 进行换行 但我喜欢所有文件都使用 Unix 行结尾 lf 我找到了一种在会话期间更改它的方法 但我不是 Emacs 专家 无法将解决方案转换为 emacs 文件中的 elisp 命令 有
  • 如何在Google Sheet App脚本中使用JS库?

    我想在 Google Sheet 中编写一个自定义函数来生成条形码 如何导入外部 js 库 例如https lindell me JsBarcode https lindell me JsBarcode 这就是我到目前为止所做的 funct
  • PHP cli 脚本不输出任何内容

    所以我有一个 php 脚本 我使用以下命令执行 php f my script php myArguments 该脚本使用 svn 进行版本控制 我刚刚更新了它 将运行它的命令粘贴到终端中 然后执行它 但是 没有任何输出 不是失败消息 不是
  • 如何在也具有导航的 Tabbar 应用程序中调用 viewWillDisappear 方法

    我在我的应用程序中创建了 5 个选项卡 在Tab1 i have UITableView On didSelectRowAtIndexPath我正在导航到另一个 UIView 其中显示了我的所有 5 个选项卡 我还在那个导航视图中播放歌曲
  • Delphi XE5可以编译一个可以用“System.loadLibrary”在Java中加载的.so库吗?

    我之前已经使用 Free Pascal FPC 编译器成功编译了一个用 Pascal 编写的 so 库 该库通过 System loadLibrary 在 Android Java 中成功加载 并且可以通过 Android Java 代码中
  • 从rc6升级到Rails 3.1.0,资产预编译失败

    从 Rails 3 1 0 rc6 升级到 Rails 3 1 0 后 运行时收到一些错误消息assets precompile像这样的任务 RAILS ENV production RAILS GROUPS assets rake ass
  • 如何在 PHP 中创建持久会话?

    I used session start 在 PHP 中启动会话 但是当我的浏览器关闭时 会话就消失了 如何使用 PHP 创建跨浏览器关闭的持续会话 See the php ini value session cookie lifetime
  • 如何设计具有修订历史的数据库?

    我是构建新的团队的一员内容管理系统对于我们的公共网站 我正在尝试找到最简单和最好的方法来构建版本控制机制 对象模型非常基本 我们有一个摘要BaseArticle包含版本无关 元数据属性的类 例如Heading CreatedBy 许多类都继