为大量不同的实体存储创建/退休日期

Lai*_*ura 4 schema normalization database-design

在我现在正在处理的数据库中,几乎每个实体都有这 4 列:

CreatedDate
CreatedBy
RetiredDate
RetiredBy
Run Code Online (Sandbox Code Playgroud)

通常这用于记录目的,对于某些实体来说,知道它何时退役的有用性是有争议的(但不要告诉我的老板)。对于其他一些东西,(比如卡车)它更有意义,因为“退役”的车辆可能会重新投入使用。

无论如何,我想知道将这些信息放在一张桌子上是否是个好主意,因为它到处重复。如果是的话,任何人都有一个好名字..?简单created_retired_dates吗?

MDC*_*CCL 14

您似乎参与了一个需要创建时态数据库的项目。您也可以通过搜索术语auditable databasesdatabase history tables 来查找相关信息。

我认为这个特殊的Stack Overflow 答案是关于这些主题的顶级材料。在这样的帖子中,@PerformanceDBA为一个非常有趣的业务环境建模了一个可审计的关系数据库,其中包含的指导性包含多个方面,不仅与这些主题相关,而且与整个数据库设计和实践相关。

背景

如您所知,构建数据库是为了保留与其用户相关的信息,并且信息自然会随着时间的推移而发生变化。通过这种方式,包含在特定数据库中的值可能会受到连续修改,因此不再是“当前”值,但这些情况并不意味着相关值的先前“状态”在更改后变得无关紧要。经历了相应的时间顺序更新。

在这方面,是的,在某些情况下,随着时间的推移跟踪影响实体的更新是最重要的,在其他情况下,应该明确禁止和防止更改,因此实体更改的保留将不适用。这似乎是陈词滥调,但这些要点取决于您的确切信息要求,因此您必须彻底分析每个特定情况,以便确定如何继续。然后,一旦确定“审计追踪”有效且必要,就必须实施。

建议的方法

1. 说明性 IDEF1X 图

让我们将Truck实体类型作为参考,因为根据您的规范,它是需要启用时间功能的方面的一个很好的示例。为了说明我将提议的构建上述功能的方法,我准备了一个 IDEF1X 1图表,如图 1所示(您也可以从 Dropbox 以 PDF 格式下载):

图 1. 卡车历史 IDEF1X 图

作为上述图中所示,除了描绘卡车用户实体类型,我已经包括一个表示一个额外的一个历史记录卡车(命名,因此,TruckHistory)。

TruckTruckHistory实体类型之间的唯一区别是后者的 PRIMARY KEY,因为它由TruckNumber和一个称为AuditedDateTime的补充属性组成,当然,该属性指示给定Truck事件被“审计”的特定时间点(或更新)。重要的是要注意TruckHistory.TruckNumber被定义为指向Truck.TruckNumber 的外键,描述了这两种实体类型之间发生的关联类型。

2. 生成的说明性 SQL-DDL 逻辑布局

将本示例带到逻辑抽象级别,我从上面介绍的 IDEF1X 图中导出了以下 DDL 语句:

CREATE TABLE UserProfile (
    UserId          INT      NOT NULL,
    FirstName       CHAR(30) NOT NULL,
    LastName        CHAR(30) NOT NULL,
    BirthDate       DATE     NOT NULL,
    GenderCode      CHAR(3)  NOT NULL,
    Username        CHAR(20) NOT NULL,
    CreatedDateTime DATETIME NOT NULL,
    --
    CONSTRAINT UserProfile_PK  PRIMARY KEY (UserId),
    CONSTRAINT UserProfile_AK1 UNIQUE ( -- Composite ALTERNATE KEY.
        FirstName,
        LastName,
        BirthDate,
        GenderCode
    ),
    CONSTRAINT UserProfile_AK2 UNIQUE (Username) -- ALTERNATE KEY.
);

CREATE TABLE Truck ( -- Contains the “current” versions.
    TruckNumber     INT      NOT NULL,
    OtherColumn     CHAR(10) NOT NULL,
    IsRetired       BIT      NOT NULL,
    CreatedUserId   INT      NOT NULL,
    CreatedDateTime DATETIME NOT NULL,
    --
    CONSTRAINT Truck_PK              PRIMARY KEY (TruckNumber),
    CONSTRAINT TruckToUserProfile_FK FOREIGN KEY (CreatedUserId)
        REFERENCES UserProfile (UserId)
);

CREATE TABLE TruckHistory ( -- Holds the “past” versions.
    TruckNumber     INT      NOT NULL,
    AuditedDateTime DATETIME NOT NULL,
    OtherColumn     CHAR(10) NOT NULL,
    IsRetired       BIT      NOT NULL,
    CreatedUserId   INT      NOT NULL,
    CreatedDateTime DATETIME NOT NULL,
    --
    CONSTRAINT TruckHistory_PK              PRIMARY KEY (TruckNumber, AuditedDateTime), -- Composite PRIMARY KEY.
    CONSTRAINT TruckHistoryToTruck_FK       FOREIGN KEY (TruckNumber)
        REFERENCES Truck (TruckNumber),
    CONSTRAINT TruckHistoryToUserProfile_FK FOREIGN KEY (CreatedUserId)
        REFERENCES UserProfile (UserId),
    CONSTRAINT DateSuccession_CK            CHECK       (AuditedDateTime > CreatedDateTime)
);
Run Code Online (Sandbox Code Playgroud)

样本数据

启用之前公开的逻辑设计后,假设我们将接下来的两行保留在UserProfile表中:

+-——————-+-—————————-+-————————-+-—————————--+-—— ——————-+-———————————————————————————-+
| 用户 ID | 名字| 姓氏| 出生日期  | 性别代码| 创建日期时间         |
+-——————-+-—————————-+-————————-+-—————————--+-—— ——————-+-———————————————————————————-+
| 1 | 詹姆斯 | 史密斯 | 1985-06-30 | 男 | 2013-02-12 07:32:04.000 |
+--------+-----------+---------+------------+---- --+--------------------------+
| 2 | 妮可 | 约翰逊 | 1987-10-14 | F | 2013-03-24 09:02:03.000 |
+--------+-----------+---------+------------+---- --+--------------------------+

然后,假设该Truck表包含对应于TruckNumber标识的卡车的(“当前”或“当前”)行,如下所示: 1750

+-————————————-+-————————————-+-————————————-+-————————— —————-+-——————————————————————————————-+
| 卡车编号| 其他专栏| 已退休| 创建的用户 ID | 创建日期时间         |
+-————————————-+-————————————-+-————————————-+-————————— —————-+-——————————————————————————————-+
| 1750 | 酒吧 | 错误 | 2 | 2015-06-30 16:58:12.000 |
+------------+------------+------------+--------- ------+------------------------------+

最后,假设卡车通过鉴定TruckNumber 1750持有TruckHistory行是如下:

+-————————————-+-———————————————————————————-+-———————— ———-+-—————————-+-——————————————-+-————————————————— ————————-+
| 卡车编号| 审核日期时间         | 其他专栏| 已退休| 创建的用户 ID | 创建日期时间         |
+-————————————-+-———————————————————————————-+-———————— ———-+-—————————-+-——————————————-+-————————————————— ————————-+    
| 1750 | 2013-12-10 17:05:01.000 | 福 | 错误 | 1 | 2013-06-30 10:34:12.000 |
+------------+-------------------------+--------- ----+-----------+---------------+----------------- --------+
| 1750 | 2014-03-22 14:08:08.000 | 酒吧 | 真| 2 | 2013-12-10 17:05:01.000 |
+------------+-------------------------+--------- ----+-----------+---------------+----------------- --------+
| 1750 | 2014-09-14 09:45:06.000 | 酒吧 | 错误 | 2 | 2014-03-22 14:08:08.000 |
+------------+-------------------------+--------- ----+-----------+---------------+----------------- --------+
| 1750 | 2015-06-30 16:58:12.000 | 福 | 错误 | 1 | 2014-09-14 09:45:06.000 |
+------------+-------------------------+--------- ----+-----------+---------------+----------------- --------+

好处

因此,在TruckHistory表中,有一个时间序列,由与TruckNumber标识的Truck相关的每一个变化组成。一个人可以知道 1750

  • 谁是Users进行此类更改的人(通过CreatedUserId);
  • 与这些变化相关的值开始成为“当前”或“当前”(通过CreatedDateTime)的确切时刻;和
  • 在“当前”Truck表中接收新值的特定行被修改的特定时刻(通过AuditedDateTime)。

我认为,这个配置是不是只维持最近更有利的日期,其中的每个实例卡车是与一起更新的标识中的用户谁执行相应的更新操作,但在同一时间,失去每一个的之前的实体“状态”。

数据操作注意事项

从数据操作的角度来看,这意味着每次 (1) 精确Truck行遭受 UPDATE 时,您还必须 (2) 将“当前”TruckHistory的相应Truck值插入表中,直到上述TruckUPDATE 生效。我会果断地在ACID TRANSACTIONS的帮助下执行这些操作,以便它们作为单个工作单元要么成功要么失败。

类似场景

我不知道整个网络的 Stack Exchange 团队如何设置后期修订历史记录,但它提供的功能与我建议的方法提供的功能非常相似。仔细检查这个 Stack Exchange 过程会很有帮助,这样你就可以更广泛地了解管理这种性质的场景的方式。


尾注

1所 对于信息建模集成定义IDEF1X)是被确立为一个非常可取的数据建模技术标准由美国在1993年12月美国国家标准与技术研究院(NIST)。它被牢固地基于(a)早期理论材料撰写由鞋底始发的的数据的关系模型,即EF科德博士; 关于 (b)实体-关系视图,由PP Chen 博士开发;以及 (c) 逻辑数据库设计技术,由 Robert G. Brown 创建。