【问题标题】:What is the best way to do historic comparisons of database data?对数据库数据进行历史比较的最佳方法是什么?
【发布时间】:2010-01-21 04:52:02
【问题描述】:

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

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

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

我正在尝试最好的方法来做到这一点。我是:

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

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

  3. 还有其他建议吗?

【问题讨论】:

  • 什么平台?例如,SQL Server 2008 具有内置的更改跟踪。
  • SQL Server 2005 但如果这是 2008 年的解决方案,可以满足我上面的需求,我会考虑升级
  • 我建议详细说明数据大小以及您希望对数据运行什么样的查询 - 也许这会激发一些想法。

标签: database-design tracking


【解决方案1】:

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

那么确定任何特定日子的人数(和详细信息)真的很容易:

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

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

您的历史记录表应包含基表中的所有字段,以及另外两个字段 - 修改日期和交易类型。可能还有第三个自动编号/序列/身份字段。 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)

将最后一个存储过程作为计划作业的一部分运行,然后您将获得一个针对所需特定聚合的非规范化报告表。

任何比这更复杂的东西,我认为您应该开始研究数据仓库。

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 但如果我想深入查看历史日期的详细信息,我如何“返回”并获取当时的状态?
    • 如果您想了解历史日期的详细信息,那么除了将相应的列复制到另一个表中之外,您别无选择。这将是一个相当不寻常的要求,我会质疑你为什么需要这种水平的历史知识??
    猜你喜欢
    • 2018-06-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-07
    • 2020-04-24
    • 1970-01-01
    • 2018-08-08
    相关资源
    最近更新 更多