在单个 table 中使用 TSQL 脚本更改 tracking/history 保留在 SQL 数据库中
Change tracking/history keeping in SQL database using TSQL script in single table
对于上下文,我有一个简单的 Azure SQL 数据库,我有一个理论维度模型,其中包含 12 个维度和一些最终应该作为 DWH 运行的事实 table。
我希望在此维度模型上应用历史化。为了简单起见并应用历史化的概念,我想在一个维度上启用它。在我的例子中,这是 DimEmployee,它看起来像这样,带有一行示例数据(它有更多的列,但为了简单起见,我只采用这些)
EmpKey
员工姓名
EmpCity
DWHDatStart
DWHDatEnd
有效
52
约翰
伦敦
02/02/2020
31/12/9999
Y
现在,如果假设 John 搬到另一个城市,我希望对此进行跟踪,那么更新 EmpCity 的值时所需的情况将是
EmpKey
员工姓名
EmpCity
DWHDatStart
DWHDatEnd
有效
52
约翰
阿姆斯特丹
2021 年 6 月 8 日
31/12/9999
Y
52
约翰
伦敦
02/02/2020
2021 年 6 月 8 日
N
我将如何在 TSQL 中应用它。我认为它必须是某种触发器,但缺乏应用它的具体知识。
我读过有关时间 tables 的信息,通常在对数据启用历史化时,您将历史数据存储在单独的历史记录 table 中。我认为由于这个模型设计,总是可以通过添加 DimEmployeeHist 维度或其他任何东西来扩展这个概念,但对于第一个概念,这是不需要的。我不知道单独 table 这样做会带来什么价值,除非你有很多历史记录,所以你希望它们全部整齐地存储在一个地方,以免弄乱你的主 table s.
这是否必须在创建 table 脚本中完成,还是可以在创建 table 之后完成?
首先:忘记触发器。触发器不好
这是关于我通常如何执行此操作的脑洞。对此有很多排列,但这应该会给你一个想法。
这是目标维度中的一行。当然还有很多其他行
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
是连接到您的事实的键,并且在维度 table 中是唯一的(并且应该使用约束或索引来强制执行)
SourceKey
是源系统中的key
SourceSystem
是提供此记录的任何系统的代码。
SourceKey
+ SourceSystem
是唯一的(应该用约束或索引强制执行)
我们将数据加载到我们的数据仓库中。通常第一步是将输入数据加载到分段 table。所以说我们在一个阶段 table:
EmpKey
EmpName
EmpCity
ActiveRecordSurrogateKey
Ignore
52
John
Amsterdam
NULL
NULL
73
Jim
Brisbane
NULL
NULL
7
Jack
Texas
NULL
NULL
ActiveRecord
和 Ignore
是工作专栏,它们不是来自源代码。其他每一列都来自源,但对维度一无所知
维度和分期显然会有很多记录table。
首先排除维度中所有最新的记录。
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'
我们确定吉姆没有改变,可以忽略
EmpKey
EmpName
EmpCity
ActiveRecordSurrogateKey
Ignore
52
John
Amsterdam
NULL
NULL
73
Jim
Brisbane
NULL
Y
7
Jack
Texas
NULL
NULL
识别暂存中的所有记录table,这些记录在维度中已有活动记录但具有不同的属性
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)
(如果您愿意,可以将这两个单独的更新合并为一个。)
现在我们的舞台 table 看起来像这样。
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
存储过程比较源(暂存)和目标(维度)并做所有正确的事情。
还有很多其他的事情需要考虑,但这给了你一个想法。
对于上下文,我有一个简单的 Azure SQL 数据库,我有一个理论维度模型,其中包含 12 个维度和一些最终应该作为 DWH 运行的事实 table。
我希望在此维度模型上应用历史化。为了简单起见并应用历史化的概念,我想在一个维度上启用它。在我的例子中,这是 DimEmployee,它看起来像这样,带有一行示例数据(它有更多的列,但为了简单起见,我只采用这些)
EmpKey | 员工姓名 | EmpCity | DWHDatStart | DWHDatEnd | 有效 |
---|---|---|---|---|---|
52 | 约翰 | 伦敦 | 02/02/2020 | 31/12/9999 | Y |
现在,如果假设 John 搬到另一个城市,我希望对此进行跟踪,那么更新 EmpCity 的值时所需的情况将是
EmpKey | 员工姓名 | EmpCity | DWHDatStart | DWHDatEnd | 有效 |
---|---|---|---|---|---|
52 | 约翰 | 阿姆斯特丹 | 2021 年 6 月 8 日 | 31/12/9999 | Y |
52 | 约翰 | 伦敦 | 02/02/2020 | 2021 年 6 月 8 日 | N |
我将如何在 TSQL 中应用它。我认为它必须是某种触发器,但缺乏应用它的具体知识。 我读过有关时间 tables 的信息,通常在对数据启用历史化时,您将历史数据存储在单独的历史记录 table 中。我认为由于这个模型设计,总是可以通过添加 DimEmployeeHist 维度或其他任何东西来扩展这个概念,但对于第一个概念,这是不需要的。我不知道单独 table 这样做会带来什么价值,除非你有很多历史记录,所以你希望它们全部整齐地存储在一个地方,以免弄乱你的主 table s.
这是否必须在创建 table 脚本中完成,还是可以在创建 table 之后完成?
首先:忘记触发器。触发器不好
这是关于我通常如何执行此操作的脑洞。对此有很多排列,但这应该会给你一个想法。
这是目标维度中的一行。当然还有很多其他行
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
是连接到您的事实的键,并且在维度 table 中是唯一的(并且应该使用约束或索引来强制执行)SourceKey
是源系统中的keySourceSystem
是提供此记录的任何系统的代码。SourceKey
+SourceSystem
是唯一的(应该用约束或索引强制执行)
我们将数据加载到我们的数据仓库中。通常第一步是将输入数据加载到分段 table。所以说我们在一个阶段 table:
EmpKey | EmpName | EmpCity | ActiveRecordSurrogateKey | Ignore |
---|---|---|---|---|
52 | John | Amsterdam | NULL | NULL |
73 | Jim | Brisbane | NULL | NULL |
7 | Jack | Texas | NULL | NULL |
ActiveRecord
和 Ignore
是工作专栏,它们不是来自源代码。其他每一列都来自源,但对维度一无所知
维度和分期显然会有很多记录table。
首先排除维度中所有最新的记录。
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'
我们确定吉姆没有改变,可以忽略
EmpKey | EmpName | EmpCity | ActiveRecordSurrogateKey | Ignore |
---|---|---|---|---|
52 | John | Amsterdam | NULL | NULL |
73 | Jim | Brisbane | NULL | Y |
7 | Jack | Texas | NULL | NULL |
识别暂存中的所有记录table,这些记录在维度中已有活动记录但具有不同的属性
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)
(如果您愿意,可以将这两个单独的更新合并为一个。)
现在我们的舞台 table 看起来像这样。
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
存储过程比较源(暂存)和目标(维度)并做所有正确的事情。
还有很多其他的事情需要考虑,但这给了你一个想法。