对数据库数据进行历史比较的最佳方法是什么?

时间:2010-01-21 04:52:02

标签: database-design tracking

我有一个数据库,其中包含一个组织的员工列表。有一个主要员工表和已连接表的数量。

我想开始跟踪这周数周的每周和每月月度指标,以便我可以执行以下操作:

3月1日:人数100(2月份+1,-2)
4月1日:人数101(3月+ 3,-2)
5月1日:人数105(4月+ 10,-6)

我正在努力做到最好的方法。我:

  1. 在每个月的第一天拍摄完整的数据库快照,让我的应用程序查询多个数据库以生成这些报告。

  2. 尝试跟踪某些数据库触发历史记录表中的所有更改,并尝试聚合该信息以尝试建立每个月的当前状态。

  3. 还有其他任何建议吗?

2 个答案:

答案 0 :(得分:2)

如果您只想跟踪新员工何时被雇用或终止,那么您应该首先将相关字段添加到员工表本身:HireDate date NOT NULLTerminationDate date NULL

然后在任何特定的日子确定人数(和细节)真的很容易:

SELECT EmployeeID, EmployeeName, ...
FROM Employees
WHERE HireDate <= @EndDate
AND (TerminationDate IS NULL OR TerminationDate > @BeginDate)

如果您需要跟踪修改(即标题的更改),那么为您提供最大灵活性的方法是维护带有触发器的实时历史记录表(或者您的数据库已构建 - 在变更跟踪中,如果可用)。我不建议使用完整快照,因为这会在应用程序的生命周期内消耗大量空间。

您的历史记录表应包含基表中的所有字段,以及另外两个字段 - 修改日期和事务类型。可能也是第3个自动编号/序列/标识字段。 T-SQL版本如下:

CREATE TABLE EmployeeHistory
(
    TransactionID int NOT NULL IDENTITY(1, 1)
        CONSTRAINT PK_EmployeeHistory PRIMARY KEY CLUSTERED,
    TransactionDate datetime NOT NULL,
    TransactionType tinyint NOT NULL,    -- 1 = Add, 2 = Change, 3 = Delete
    EmployeeID int NOT NULL,
    EmployeeName varchar(100) NOT NULL,
    ...
)

然后用触发器维护它:

CREATE TRIGGER tr_Employees_History
ON Employees
FOR INSERT, UPDATE
AS BEGIN
    INSERT EmployeeHistory (TransactionDate, TransactionType, EmployeeID, ...)
        SELECT
            GETDATE(),
            CASE
                WHEN d.EmployeeID IS NULL THEN 1
                WHEN (i.TerminationDate IS NOT NULL) AND
                     (d.TerminationDate IS NULL) THEN 3
                ELSE 2
            END,
            i.EmployeeID, i.EmployeeName, ...
        FROM inserted i
        LEFT JOIN deleted d
        ON d.EmployeeID = i.EmployeeID
END

我假设您没有删除员工记录,只需设置TerminationDate;如果你删除(请不要这样做),那么你需要写一个类似的DELETE触发器而不是第二个CASE WHEN i.TerminationDate ...行。

现在播种您的历史记录表:

INSERT EmployeeHistory (TransactionDate, TransactionType, EmployeeID, ...)
    SELECT HireDate, 1, EmployeeID, ...
    FROM Employees

注意 - 如果您没有HireDate,那么只需将其替换为GETDATE() - 您的历史记录只会在您播种的那一刻起生效。

现在,如果您想获得历史“快照”,可以这样做:

CREATE FUNCTION dbo.GetEmployeeSnapshot(@ReportDate datetime)
RETURNS TABLE
AS RETURN
    WITH History_CTE AS
    (
        SELECT
            TransactionType, EmployeeID, EmployeeName, ...,
            ROW_NUMBER() OVER (ORDER BY TransactionDate DESC) AS RowNum
            FROM EmployeeHistory
            WHERE TransactionDate <= @ReportDate
    )
    SELECT *
    FROM History_CTE
    WHERE RowNum = 1
    AND TransactionType IN (1, 2)    -- Filter out terminated employees

如果此查询运行缓慢,如果您需要加速某些聚合,例如人员,然后,那么您是否应该开始考虑快照表:

CREATE TABLE HeadcountHistory
(
    ReportDate datetime NOT NULL
        CONSTRAINT PK_HeadcountHistory PRIMARY KEY CLUSTERED,
    HeadCount int NOT NULL
)

更新程序:

CREATE PROCEDURE dbo.UpdateHeadcountHistory
AS

DECLARE @ReportDate datetime
SET @ReportDate = GETDATE()

INSERT HeadcountHistory (HeadCount)
    SELECT @ReportDate, COUNT(*)
    FROM dbo.GetEmployeeSnapshot(@ReportDate)

将最后一个sproc作为预定作业的一部分运行,然后您将获得所需特定聚合的非规范化报告表。

比这更复杂的事情,我想你想开始寻找数据仓库。

答案 1 :(得分:1)

如果您只是按计划运行此操作,那么我将创建一个数据汇总表...每月运行一次进程来计算您的计数,并在表示数据的汇总表中添加一行。这样,您可以回顾历史记录并生成所需的统计数据。您可能需要考虑更频繁地生成此数据,而不是计划报告(例如,每周)...只要您的分辨率高于报告期,您就应该拥有所需的所有数据。