如何最好地管理数据库中的历史查找值?

时间:2017-11-24 18:15:12

标签: database oracle database-design

概述

事件数据库,其中包含许多列,其中包含查找表中保存的记录的ID。

我正在尝试解决的问题

我需要提出一个强大的解决方案来管理历史数据,其中某些字段包含查找ID。我列出了我提出的解决方案以及替代方案。我想知道其他开发人员是否在他们的项目中以类似的方式管理这些场景。也许你有更好的方法?

数据库:Oracle 10g

:Department_name

场景:部门名称可以更改一年中的X次。该企业需要报告其所有部门的数据,但希望在事件发生时看到各自部门名称下的事件。

建议的解决方案:在部门名称查找表中设置条目时,请设置开始日期和结束日期值。使用视图,根据事件日期创建计算字段,以便在任何给定时间点访问正确的部门名称。

优点:通过一些防御性编码,它将使所选用户能够通过GUI管理其静态数据,而无需任何其他数据库更改。在运行中可以进行改变,例如完全改变名称。不需要DBA支持。

缺点:考虑到在大型数据集上进行查找/计算的量,可能是一项昂贵的操作。

替代解决方案:只需使用并插入部门名称的纯文本值即可。这里的缺点是,adhoc请求更改/更新值时需要DBA,可能针对特定日期范围并错过一些错误记录。表空间消耗量也会增加。

:Assigned_Technician_ID

场景:事件将分配一名技术人员,其中将存储技术人员的ID。查找表将保存所有可用技术人员的“当前”列表。当人们离开公司时,必须刷新清单并删除过时的技术人员。这是为了将下拉列表中的值的数量保持最小。该公司仍然希望看到哪些技术人员被分配了所有事故数据。

解决方案:不是从技术人员查找表中删除条目,而是使用表示“已存档/已删除”的标记标记该条目。此标志将充当GUI下拉列表的过滤器,以删除不需要的条目。

优点:查找表只包含员工表中技术人员的UID。因此,如果业务需求发生变化,则很容易在主视图中呈现技术人员的任何属性,例如全名或员工编号等。

缺点:与前面的示例一样,查找可能对大型数据集进行昂贵的操作。关于业务逻辑和设计,GUI侧需要额外的工作。具体来说,当原始条目被“存档”时,如何管理下拉列表。

替代解决方案:与上面的示例一样,只需使用纯文本值即可。这里的缺点是更大的表空间消耗,并且随着业务需求的变化而变得不灵活。

2 个答案:

答案 0 :(得分:3)

有一种称为版本控制的技术已存在多年,但由于多种原因在很大程度上是不可行的。但是,有一种类似的技术我称之为Version Normal Form,我发现它非常有用。以下是使用Employees表的示例。

首先,创建静态表。这是主实体表,它包含有关实体的静态数据。静态数据是指在实体生命周期内不会发生变化的数据,例如出生日期。

create table Employees(
  ID        int  auto_generated primary key,
  FirstName varchar( 32 ),
  Hiredate  date not null,
  TermDate  date,            -- last date worked
  Birthdate date,
  ...              -- other static data
);

重要的是要意识到每个员工都有一个条目,就像任何这样的表格一样。

然后是关联的版本表。这与静态表建立了1米的关系,因为员工可能有多个版本。

create table Employee_versions(
  ID         int   not null,
  EffDate    date  not null,
  char( 1 )  IsWorking not null default true,
  LastName   varchar( 32 ),    -- because employees can change last name
  PayRate    currency not null,
  WorkDept   int   references Depts( ID ),
  ...,              -- other changable data
  constraint PK_EmployeeV primary key( ID, EffDate )
);

在版本表注释中,有一个生效日期,但没有匹配的不再有效字段。这是因为一旦版本生效,它将一直有效,直到被后续版本替换。 ID和EffDate的组合必须是唯一的,因此同一个员工不能同时激活两个版本,也不能在一个版本结束和下一个版本启动之间存在差距。

大多数查询都希望了解员工数据的当前版本。这是通过将员工的静态行与现在生效的版本相结合来提供的。这可以通过以下查询找到:

select  ...
from    Employees e
join    Employee_versions v1
    on  v1.ID = e.ID
    and v1.EffDate =(
        select  Max( v2.EffDate )
        from    EmployeeVersions v2
        where   v2.ID = v1.ID
            and v2.EffDate <= NOW()
    )
where  e.ID = :EmpID;

这将返回最近一次启动的唯一一个版本。在日期检查(v2.EffDate <= NOW())中使用不等式&lt; =允许将来生效日期。假设您知道新员工将在下个月的第一天开始,或者计划在下个月的第13个月加薪,这些数据可以提前插入。这样&#34;预装&#34;条目将被忽略。

不要让子查询给你。所有搜索字段都被编入索引,因此结果非常快。

这种设计有很多灵活性。上面的查询返回所有员工,现在和过去的最新数据。您可以查看TermDate字段以获取现有员工。实际上,由于您的应用中的许多地方只对当前员工的当前信息感兴趣,因此该查询将提供良好的视图(省略最终的where子句)。不需要应用程序甚至不知道存在这样的版本。

如果您有特定日期,并且希望查看当时有效的数据,请将子查询中的v2.EffDate <= NOW()更改为v2.EffDate <= :DateOfInterest

可以在幻灯片演示here和未完成的文档here中找到更多详细信息。

为了展示设计的一些可扩展性,请注意版本表中有一个IsWorking指示符以及静态表中的终止日期。当员工离开公司时,最后一个日期将插入静态表中,并且IsWorking设置为false的最新版本的副本将插入到版本表中。

员工离开公司一段时间然后再被雇用是相当普遍的。只使用静态表中的日期,只需将该日期设置回NULL即可再次激活该条目。但是回顾&#34;回顾&#34;查询该人不再是员工的任何时候都会返回结果。没有迹象表明他们已离开公司。但是当离开公司时IsWorking = false且返回公司时IsWorking = true的版本将允许在感兴趣时检查该值,并在员工不再是员工时忽略员工即使他们以后回来了。

答案 1 :(得分:1)

我是SQL Server开发人员并且一直遇到这些问题。除非我将数据加载到数据仓库,否则我从不喜欢使用文本(非规范化)。

Re:Department_name

生效日期可能是最好的答案,让我暂停的是我不确定我理解这个问题。我想不出一个部门如此频繁地更改名称的商业案例。

Re:AssignedTech

我在几乎所有基于员工的查找中都使用了活动标志。我从来没有遇到过性能问题。在处理高转换公司时,我使用了过滤索引和视图。