我意识到将这些称为维度和事实表并不完全合适。我对于更好的术语感到迷茫,所以请原谅我在帖子中使用的这种分类。
我正在建立员工记录保存申请。
数据库将包含组织信息。信息主要在三个表中定义:位置,部门和部门。但是,还有其他类似问题。首先,我需要存储这些表的可用值。这将允许在管理员工时应用程序中的可用值以及在添加/删除部门等时管理这些值。例如,Locations表可能看起来像
LocationId | LocationName | LocationStatus
1 | New York | Active
2 | Denver | Inactive
3 | New Orleans | Active
然后,我需要为每位员工存储这些值并保留其历史记录。我的第一个想法是创建LocationHistory,DivisionHistory和DepartmentHistory表。我无法确定原因,但这让我感到很糟糕。我的下一个愿望是创建一个DimLocation / FactLocation,DimDivision / FactDivision,DimDepartment / FactDepartment表。我不相信这也是有道理的。我还考虑将它们命名为Employee的组合,即EmployeeLocations,EmployeeDivisions等。无论这些表的命名约定如何,我认为数据看起来类似于我在下面的简化版本:
EmployeeId | LocationId | EffectiveDate | EndDate
1 | 3 | 2008-07-01 | NULL
1 | 2 | 2007-04-01 | 2008-06-30
我意识到我上面描述的任何想象的解决方案都可以工作,但我真的希望创建一个易于其他人使用直观,熟悉的结构进行维护的设计。我希望得到这个社区对此事的帮助,意见和经验。我愿意接受并欢迎任何建议。例如,我是否应该将这三个表的可用值存储在数据库中?它们应该保存在应用程序代码/业务逻辑层中吗?我是否只需要重复看到历史重复三次?
谢谢!
答案 0 :(得分:3)
首先,我认为在仓库之外将这些描述为Dimension和Fact表没有问题:)
在概念化和理解关系方面,我个人认为开始/结束日期的使用非常容易让人们理解。允许代理和位置事实表,然后是时间相关的映射表,例如Agent_At_Location等。但它们确实存在值得注意的问题。
如果EndDate是2008-08-30
,那个位置的员工是8月30日,或者是8月30日,包括在内。
处理查询中重叠的日期时间会产生混乱的查询,但更重要的是,查询速度慢。
第一个似乎只是一个惯例问题,但在与其他数据交配时可能会产生某些影响。例如,考虑EndDate 2008-08-30
表示它们位于该位置,包括8月30日。然后你加入他们当天的每日代理数据(例如当他们实际上到工作岗位,离开休息时间等)。您需要加入ON AgentDailyData.EventTimeStamp < '2008-08-30' + 1
才能包含当天发生的所有事件。
这是因为数据的EventTimeStamp不是以天为单位测量的,而是以分钟或秒为单位。
如果您认为'2008-08-30'的结束日期意味着代理人在8月30日到达该位置但未包括在内,则该联接不需要+ 1
。实际上,您不需要知道日期是否为DAY绑定,或者可以包含时间组件。您只需要TimeStamp < EndDate
。
通过使用EXCLUSIVE结束标记,您的所有查询都可以简化,永远不需要+ 1 day
或+ 1 hour
来处理边缘条件。
第二个难以解决。解决重叠期的最简单方法如下:
SELECT
CASE WHEN TableA.InclusiveFrom > TableB.InclusiveFrom THEN TableA.InclusiveFrom ELSE TableB.InclusiveFrom END AS [NetInclusiveFrom],
CASE WHEN TableA.ExclusiveFrom < TableB.ExclusiveFrom THEN TableA.ExclusiveFrom ELSE TableB.ExclusiveFrom END AS [NetExclusiveFrom],
FROM
TableA
INNER JOIN
TableB
ON TableA.InclusiveFrom < TableB.ExclusiveFrom
AND TableA.ExclusiveFrom > TableB.InclusiveFrom
-- Where InclusiveFrom is the StartDate
-- And ExclusiveFrom is the EndDate, up to but NOT including that date
该查询的问题是索引问题。可以使用索引来解析第一个条件TableA.InclusiveFrom < TableB.ExclusiveFrom
。但它可以提供大量的日期。然后,对于这些记录中的每一个,ExclusiveDate
都可以是任何事情,当然也不是一个有助于快速解决的TableA.ExclusiveFrom > TableB.InclusiveFrom
我之前使用的解决方案是InclusiveFrom
和ExclusiveFrom
之间允许的最大间隔。这允许类似......
ON TableA.InclusiveFrom < TableB.ExclusiveFrom
AND TableA.InclusiveFrom >= TableB.InclusiveFrom - 30
AND TableA.ExclusiveFrom > TableB.InclusiveFrom
条件TableA.ExclusiveFrom > TableB.InclusiveFrom
STILL无法从索引中受益。但是我们通过搜索TableA.InclusiveFrom
限制了可以返回的行数。它最多只有30天的数据,因为我们知道我们将持续时间限制在最多30天。
这方面的一个例子是按日历月分解关联(最长持续时间为31天)。
EmployeeId | LocationId | EffectiveDate | EndDate
1 | 2 | 2007-04-01 | 2008-05-01
1 | 2 | 2007-05-01 | 2008-06-01
1 | 2 | 2007-06-01 | 2008-06-25
(Representing Employee 1 being in Location 2 from 1st April to (but not including) 25th June.)
这实际上是一种权衡;使用磁盘空间来获得性能。
我甚至已经看到这种情况被推到了不实际存储日期范围的极端,而是存储了每天的实际映射。从本质上讲,它就像将最长持续时间限制为1天......
EmployeeId | LocationId | EffectiveDate
1 | 2 | 2007-06-23
1 | 2 | 2007-06-24
1 | 3 | 2007-06-25
1 | 3 | 2007-06-26
本能地,我最初反对这一点。但是在随后的ETL,仓储,报告等方面,我实际上发现它非常强大,适应性强,可维护性强。实际上,我看到人们减少了编码错误,在更短的时间内编写代码,代码最终运行得更快,并且能够更好地适应客户不断变化的需求。
唯一的两个缺点是:
1.占用更多的磁盘空间(但与事实表的大小相比较)
2.此映射的插入和更新速度较慢
插入和更新的实际减速实际上只有一次,这个模型用于表示不断变化的过程网络;应用程序想要每秒更改映射大约30次。即使这样它起作用,它只会占用比理想时间更多的CPU时间。
答案 1 :(得分:1)
如果你想要有效率并保持历史,那就做这些事。这个问题有多种解决方案,但这是我一直回到的问题:
请记住,每行代表一个实体,如果您对实体进行更正,那很好,但不要重新使用,并为新位置添加ID。设置它,以便不是删除一个位置,而是用一点将其标记为已删除,并将其从界面中隐藏起来,当它历史性地被引用时,它仍然存在。
创建包含当前值的历史记录表,如果当前未设置值,则不记录任何记录。将外键绑回员工并绑定到该位置。
在employee表中创建一个指向历史记录中当前活动位置的列。当您需要获取员工位置时,您可以根据此ID加入历史记录表。当您需要从历史记录表中获取您加入的员工的所有历史记录时。
此结构使其全部标准化,并为您提供了一种查找当前值的简便方法,而无需进行任何日期比较。
就使用历史一词而言,可以用不同的术语来思考:因为它包含当前项目和历史项目,所以它实际上只是一个保留旧项目的联结表。因此,您可以将其命名为EmployeeLocations。