【问题标题】:Change tracking/history keeping in SQL database using TSQL script in single table在单表中使用 TSQL 脚本在 SQL 数据库中进行更改跟踪/历史记录
【发布时间】:2021-08-25 05:24:50
【问题描述】:

对于上下文,我有一个简单的 Azure SQL 数据库,我有一个大约 12 维的理论维度模型和一些最终应该用作 DWH 的事实表。

我希望在这个维度模型上应用历史化。开始简单并应用历史化的概念,我想在一维上启用它。就我而言,这是 DimEmployee,它看起来像这样,带有一行样本数据(它有更多列,但为简单起见,我只采用这些)

EmpKey EmpName EmpCity DWHDatStart DWHDatEnd IsActive
52 John London 02/02/2020 31/12/9999 Y

现在,如果假设 John 搬到另一个城市,我希望对此进行跟踪,以便 EmpCity 的值更新时所需的情况是

EmpKey EmpName EmpCity DWHDatStart DWHDatEnd IsActive
52 John Amsterdam 08/06/2021 31/12/9999 Y
52 John London 02/02/2020 08/06/2021 N

我将如何在 TSQL 中应用它。我认为它必须是某种触发器,但缺乏应用它的具体知识。 我已经阅读过时态表,通常在启用数据历史化时,您会将历史数据存储在单独的历史表中。我认为由于这种模型设计,总是可以通过添加 DimEmployeeHist 尺寸或其他任何东西来扩展这个概念,但对于第一个概念,这不是必需的。我不知道在单独的表中执行此操作会带来什么价值,除非您有大量历史记录,因此您希望它们都整齐地存储在一个地方,以免弄乱您的主表。

这必须在创建表脚本中完成还是可以在创建表后完成?

【问题讨论】:

  • 这是一个 SCD2。这通常的工作方式是您有批处理作业(可能主要是 T-SQL 或存储过程),它们使用一堆更新和插入来获取正确格式的数据。所以简短的回答是,您使用存储过程来使用输入(更改的)数据和维度表来制定正确的操作。即:insert 一条新记录和update 旧记录。
  • 时态表也可能适用于您的这种情况(无需维护自己的触发器)。
  • 尽管许多声明另有说明,但时态表不会执行 Kimball SCD2 的功能。它们确实跟踪历史变化,但不是“维度”。对于这个(以及大多数其他)用例来说,触发器绝对是不必要的
  • @Nick.McDermaid 你有这样的存储过程的例子,它在插入/更新另一条记录时添加功能?
  • 我的例子如下。有用吗?

标签: sql-server tsql triggers azure-sql-database historical-db


【解决方案1】:

首先:忘记触发器。触发器不好

这是关于我通常如何执行此操作的脑筋急转弯。对此有很多排列,但这应该会给您一个想法。

这是目标维度中的一行。当然还有很多其他的行

SurrogateKey SourceKey SourceSystem EmpName EmpCity DWHDatStart DWHDatEnd IsActive
3678 52 HRSystem1 John London 2020-02-20 9999-12-31 Y
3642 73 HRSystem1 Jim Brisbane 2021-03-18 9999-12-31 Y
  • SurrogateKey 是连接事实的键,在维度表中是唯一的(应通过约束或索引强制执行)
  • SourceKey是源系统中的key
  • SourceSystem 是提供此记录的任何系统的代码。
  • SourceKey + SourceSystem 是唯一的(应通过约束或索引强制执行)

我们将数据加载到我们的数据仓库中。通常,第一步是将输入数据加载到临时表中。所以假设我们在临时表中有这个:

EmpKey EmpName EmpCity ActiveRecordSurrogateKey Ignore
52 John Amsterdam NULL NULL
73 Jim Brisbane NULL NULL
7 Jack Texas NULL NULL

ActiveRecordIgnore 是工作列,它们不是来自源代码。其他所有列确实来自源,但对维度一无所知

维度和staging表中显然会有很多记录。

首先排除维度中所有最新的记录。

UPDATE StagingTable
SET Ignore = 'Y'
FROM StagingTable TGT
INNER JOIN DimensionTable SRC
ON TGT.EmpKey = SRC.SourceKey
AND TGT.SourceKey = 'HRSystem1'
AND TGT.EmpName=SRC.EmpName 
AND TGT.EmpCity=SRC.EmpCity
AND SRC.IsActive = 'Y'

我们发现 Jim 没有改变,可以忽略

EmpKey EmpName EmpCity ActiveRecordSurrogateKey Ignore
52 John Amsterdam NULL NULL
73 Jim Brisbane NULL Y
7 Jack Texas NULL NULL

标识暂存表中维度中已有活动记录但属性不同的所有记录

UPDATE StagingTable
SET ActiveRecordSurrogateKey = SRC.SurrogateKey
FROM StagingTable TGT
INNER JOIN DimensionTable SRC
ON TGT.EmpKey = SRC.SourceKey
AND TGT.SourceKey = 'HRSystem1'
AND TGT.IsActive='Y'
AND (TGT.EmpName<>SRC.EmpName OR TGT.EmpCity<>SRC.EmpCity)

(如果您愿意,这两个单独的更新可以合并为一个。)

现在我们的临时表看起来像这样。

EmpKey EmpName EmpCity ActiveRecordSurrogateKey Ignore
52 John Amsterdam 3678 NULL
73 Jim Brisbane NULL Y
7 Jack Texas NULL NULL

现在我们有足够的信息来更新维度。我们可以根据辅助列编写更多的 SQL 以应用于维度。

但首先,让我们指定一个固定日期。如果它在午夜之前和之后运行,这将阻止奇怪的事情发生。或者您可能希望以其他方式确定这一点,例如源中的输入参数或数据

DECLARE @Date DATE = GETDATE();

现在我们可以插入所有新记录(全新或更改)

-- This line inserts new dimension records:
INSERT INTO DimensionTable (SourceKey,SourceSystem,EmpName,EmpCity, StartDate,EndDate,IsActive)
SELECT EmpKey,'HRSystem1',EmpName,EmpCity, @Date,'2999-01-01','Y' 
FROM StagingTable 
WHERE Ignore IS NULL

现在我们的维度看起来像这样

SurrogateKey SourceKey SourceSystem EmpName EmpCity DWHDatStart DWHDatEnd IsActive
3678 52 HRSystem1 John London 2020-02-20 9999-12-31 Y
3642 73 HRSystem1 Jim Brisbane 2021-03-18 9999-12-31 Y
3693 7 HRSystem1 Jack Texas 2021-06-09 9999-12-31 Y
3694 52 HRSystem1 John Amsterdam 2021-06-09 9999-12-31 Y

现在我们结束现有记录的日期:

-- This line end-dates existing records:
UPDATE DimensionTable
SET DWEndDate = @Date, Active = 'N'
FROM DimensionTable TGT
INNER JOIN StagingTable SRC
ON TGT.SurrogateKey  = ActiveRecordSurrogateKey
SurrogateKey SourceKey SourceSystem EmpName EmpCity DWHDatStart DWHDatEnd IsActive
3678 52 HRSystem1 John London 2020-02-20 2021-06-09 N
3642 73 HRSystem1 Jim Brisbane 2021-03-18 9999-12-31 Y
3693 52 HRSystem1 Jack Texas 2021-06-09 9999-12-31 Y
3694 52 HRSystem1 John Amsterdam 2021-06-09 9999-12-31 Y

因此,您基本上将所有这些 T-SQL 语句包装在一个存储过程中,添加一些事务、日志记录和错误处理。

CREATE PROC pUpdateDimPerson
AS
BEGIN

-- All the code above

END

存储过程比较源(暂存)和目标(维度)并做所有正确的事情。

还有很多其他的事情需要考虑,但这会给你一个想法。

【讨论】:

  • 这个答案有帮助吗?
猜你喜欢
  • 1970-01-01
  • 2023-04-06
  • 1970-01-01
  • 2010-09-13
  • 1970-01-01
  • 1970-01-01
  • 2020-11-07
  • 2017-07-30
  • 1970-01-01
相关资源
最近更新 更多