在没有日期列的情况下限制 ETL 中的行以供提示
Limit Rows in ETL Without Date Column for Cue
我们有两个大的 tables(客户和联系人)每晚都经过 ETL 过程,被插入到数据仓库中的单个 "People" table 中。这个 table 在很多地方都有使用,如果不做大量工作就无法进行重大更改。
源 table 由第三方软件填充;我们曾经假设我们可以通过使用每个中的 "UpdateDate" 列来识别自昨晚以来更新的行,但最近识别了一些未被 ETL 触及的行,如 "UpdateDate"专栏的行为并不像我们想象的那样;软件公司不认为这是一个错误,所以我们不得不接受这个事实。
因此,我们现在将所有源行转换为临时暂存table,然后将其合并到数据仓库中,使用合并来识别任何更改的值。我们注意到这个过程在某些日子里花费的时间太长,并且希望限制 ETL 过程查看的行数,因为我们认为造成阻碍的原因主要是数据量太大检查并存储在临时数据库中。我们无法仅查看源数据并确定每一行最后更改的时间。
这是 ETL 存储过程的简化伪代码,尽管该过程实际执行的操作与问题并不相关(包括以防万一您不同意我的看法!)
CREATE #TempTable (ClientOrContact BIT NOT NULL, Id INT NOT NULL, [Some_Other_Columns])
INSERT #TempTable
SELECT 1 AS ClientOrContact, C.Id, [SomeColumns] FROM
(SELECT [SomeColumns]
FROM Source_ClientsTable C
JOIN FieldsTable F JOIN [SomeOtherTables])
PIVOT (MAX(F.FieldValue) FOR F.FieldName IN ([SomeFieldNames]));
INSERT #TempTable
SELECT 0 AS ClientOrContact, C.Id, [SomeColumns] FROM
(SELECT [SomeColumns]
FROM Source_ContactsTable C
JOIN FieldsTable F JOIN [SomeOtherTables])
PIVOT (MAX(F.FieldValue) FOR F.FieldName IN ([SomeFieldNames]));
ALTER #TempTable ADD PRIMARY KEY (ClientOrContact, Id);
MERGE Target_PeopleTable AS Tgt
USING (SELECT [SomeColumns] FROM #TempTable JOIN [SomeOtherTables]) AS Src
ON Tgt.ClientOrContact = Src.ClientOrContact AND Tgt.Id = Src.Id
WHEN MATCHED AND NOT EXISTS (SELECT Tgt.* INTERSECT SELECT Src.*)
THEN UPDATE SET ([All_NonKeyTargetColumns] = [All_NonKeySourceColumns])
WHEN NOT MATCHED BY Target THEN INSERT [All_TargetColumns] VALUES [All_SourceColumns]
OUTPUT $Action INTO @Changes;
RETURN COUNT(*) FROM @Changes;
GO
每个源 table 大约有 150 万行,但每天只有相对少量的行被插入或更新(从未删除)。每个 table 中大约有 50 列,其中大约 40 列每晚可以更改值。大多数列是 VARCHAR,每个 table 包含一个独立的增量主键列。我们可以为源 table 添加索引,但不能以任何其他方式更改它们(它们已经被前任索引)源 table 和目标 table 在相同的服务器,但不同的数据库。 编辑: 目标 Table 在 ClientOrContact
和 Id
列上有一个复合主键,与临时 table 上显示的相匹配在上面的脚本中。
所以,我的问题是 - 您能否提出任何可能有助于限制我们每晚查看或复制的行数的一般可能策略?如果我们每晚只触及我们需要的行,我们将触及不到我们目前所做数据的 1%...
在您尝试以下建议之前,只需检查一件事,即 Target_PeopleTable 在 id 列上有一个索引或主键。它可能会,但没有模式信息来验证我没有做任何假设,这可能会加快合并阶段。
正如您所确定的那样,如果您能以某种方式将 TempTable 中的记录限制为仅更改的行,那么这可以为实际的 MERGE 语句提供性能胜利(取决于仅确定更改的行的成本)。
作为一般策略,我会考虑使用某种校验和来尝试仅识别更改的记录。 T-SQL Checksum function 可用于通过将列指定为该函数的逗号分隔列表来计算所需列的校验和,或者有可用的实际列类型,例如 Binary_Checksum.
由于您无法更改源架构,因此您必须在目标数据库中维护记录 ID 和关联校验和的列表,以便您可以轻松地将源校验和与上次 运行 的目标校验和进行比较以便识别差异。
然后您只能插入目标和源之间存在校验和差异或目标数据库中不存在 ID 的临时 table。
这可能只是将性能问题转移到临时插入部分,但我认为值得一试。
你考虑过触发器吗?我像避开瘟疫一样避开它们,但它们确实是一些问题的解决方案。
在您的两个来源 table 上放置一个 INSERT/UPDATE [/DELETE?] 触发器。对其进行编程,以便在添加或更新行时,触发器将在(您必须创建此)审计 table 中记录这些行的 ID,其中 table 将包含 ID,更改的类型(更新或插入 - 以及删除,如果您不得不担心这些)以及更改的时间。当您 运行 ETL 时,将此“待合并”项目列表与源 table 加入。完成后,删除 table 并为下一个 运行 重置。 (使用“添加于”日期时间列确保您没有删除可能在 运行 宁 ETL 时添加的行。)
正确使用和实施背后有很多细节,但总的来说,这个想法应该可以满足您的需要。
我们有两个大的 tables(客户和联系人)每晚都经过 ETL 过程,被插入到数据仓库中的单个 "People" table 中。这个 table 在很多地方都有使用,如果不做大量工作就无法进行重大更改。
源 table 由第三方软件填充;我们曾经假设我们可以通过使用每个中的 "UpdateDate" 列来识别自昨晚以来更新的行,但最近识别了一些未被 ETL 触及的行,如 "UpdateDate"专栏的行为并不像我们想象的那样;软件公司不认为这是一个错误,所以我们不得不接受这个事实。
因此,我们现在将所有源行转换为临时暂存table,然后将其合并到数据仓库中,使用合并来识别任何更改的值。我们注意到这个过程在某些日子里花费的时间太长,并且希望限制 ETL 过程查看的行数,因为我们认为造成阻碍的原因主要是数据量太大检查并存储在临时数据库中。我们无法仅查看源数据并确定每一行最后更改的时间。
这是 ETL 存储过程的简化伪代码,尽管该过程实际执行的操作与问题并不相关(包括以防万一您不同意我的看法!)
CREATE #TempTable (ClientOrContact BIT NOT NULL, Id INT NOT NULL, [Some_Other_Columns])
INSERT #TempTable
SELECT 1 AS ClientOrContact, C.Id, [SomeColumns] FROM
(SELECT [SomeColumns]
FROM Source_ClientsTable C
JOIN FieldsTable F JOIN [SomeOtherTables])
PIVOT (MAX(F.FieldValue) FOR F.FieldName IN ([SomeFieldNames]));
INSERT #TempTable
SELECT 0 AS ClientOrContact, C.Id, [SomeColumns] FROM
(SELECT [SomeColumns]
FROM Source_ContactsTable C
JOIN FieldsTable F JOIN [SomeOtherTables])
PIVOT (MAX(F.FieldValue) FOR F.FieldName IN ([SomeFieldNames]));
ALTER #TempTable ADD PRIMARY KEY (ClientOrContact, Id);
MERGE Target_PeopleTable AS Tgt
USING (SELECT [SomeColumns] FROM #TempTable JOIN [SomeOtherTables]) AS Src
ON Tgt.ClientOrContact = Src.ClientOrContact AND Tgt.Id = Src.Id
WHEN MATCHED AND NOT EXISTS (SELECT Tgt.* INTERSECT SELECT Src.*)
THEN UPDATE SET ([All_NonKeyTargetColumns] = [All_NonKeySourceColumns])
WHEN NOT MATCHED BY Target THEN INSERT [All_TargetColumns] VALUES [All_SourceColumns]
OUTPUT $Action INTO @Changes;
RETURN COUNT(*) FROM @Changes;
GO
每个源 table 大约有 150 万行,但每天只有相对少量的行被插入或更新(从未删除)。每个 table 中大约有 50 列,其中大约 40 列每晚可以更改值。大多数列是 VARCHAR,每个 table 包含一个独立的增量主键列。我们可以为源 table 添加索引,但不能以任何其他方式更改它们(它们已经被前任索引)源 table 和目标 table 在相同的服务器,但不同的数据库。 编辑: 目标 Table 在 ClientOrContact
和 Id
列上有一个复合主键,与临时 table 上显示的相匹配在上面的脚本中。
所以,我的问题是 - 您能否提出任何可能有助于限制我们每晚查看或复制的行数的一般可能策略?如果我们每晚只触及我们需要的行,我们将触及不到我们目前所做数据的 1%...
在您尝试以下建议之前,只需检查一件事,即 Target_PeopleTable 在 id 列上有一个索引或主键。它可能会,但没有模式信息来验证我没有做任何假设,这可能会加快合并阶段。
正如您所确定的那样,如果您能以某种方式将 TempTable 中的记录限制为仅更改的行,那么这可以为实际的 MERGE 语句提供性能胜利(取决于仅确定更改的行的成本)。
作为一般策略,我会考虑使用某种校验和来尝试仅识别更改的记录。 T-SQL Checksum function 可用于通过将列指定为该函数的逗号分隔列表来计算所需列的校验和,或者有可用的实际列类型,例如 Binary_Checksum.
由于您无法更改源架构,因此您必须在目标数据库中维护记录 ID 和关联校验和的列表,以便您可以轻松地将源校验和与上次 运行 的目标校验和进行比较以便识别差异。
然后您只能插入目标和源之间存在校验和差异或目标数据库中不存在 ID 的临时 table。
这可能只是将性能问题转移到临时插入部分,但我认为值得一试。
你考虑过触发器吗?我像避开瘟疫一样避开它们,但它们确实是一些问题的解决方案。
在您的两个来源 table 上放置一个 INSERT/UPDATE [/DELETE?] 触发器。对其进行编程,以便在添加或更新行时,触发器将在(您必须创建此)审计 table 中记录这些行的 ID,其中 table 将包含 ID,更改的类型(更新或插入 - 以及删除,如果您不得不担心这些)以及更改的时间。当您 运行 ETL 时,将此“待合并”项目列表与源 table 加入。完成后,删除 table 并为下一个 运行 重置。 (使用“添加于”日期时间列确保您没有删除可能在 运行 宁 ETL 时添加的行。)
正确使用和实施背后有很多细节,但总的来说,这个想法应该可以满足您的需要。