可以使 SQL 中的 MERGE 操作运行得更快的方法
Approaches that would allow to make the MERGE opearation in SQL work faster
您能否推荐一些方法,使 SQL 中的 MERGE 操作运行得更快?
我认为这个问题都是关于知识和经验的,不应该被认为是基于意见的,因为任何可以使操作更快的东西绝对适合这个问题并且操作越快越好答案是。
在我的特定情况下,我有大约 170 万条记录,我在重复作业中获取这些记录,并使用这些记录来更新现有记录。为了尽可能少地锁定真实的table(它是[LegalContractors]
),我使用了一个临时的table(它是[LegalContractorTemps]
),我将所有的来自非 SQL(但 C#)代码的记录,之后我 运行 MERGE
。
这是我正在尝试的:
DELETE FROM [dbo].[LegalContractorTemps] WHERE [Code] IS NULL;
DELETE FROM [dbo].[LegalContractorTemps]
WHERE [Id] IN (
SELECT [Id]
FROM [dbo].[LegalContractorTemps] [Temp]
JOIN (
SELECT [Code], [Status], MAX([Id]) as [MaxId]
FROM [dbo].[LegalContractorTemps]
GROUP BY [Code], [Status]
HAVING COUNT([Id]) > 1
) [TempGroup]
ON ([Temp].[Code] = [TempGroup].[Code] AND [Temp].[Status] = [TempGroup].[Status] AND [MaxId] != [Id])
);
CREATE UNIQUE INDEX [CodeStatus]
ON [dbo].[LegalContractorTemps] ([Code], [Status]);
SELECT GETDATE() AS [beginTime];
MERGE [dbo].[LegalContractors] AS TblTarget
USING [dbo].[LegalContractorTemps] AS TblSource
ON (TblSource.[Code] = TblTarget.[Code] AND TblSource.[Status] = TblTarget.[Status])
WHEN NOT MATCHED BY TARGET THEN
INSERT ([Code], [ShortName], [Name], [LegalAddress], [Status], [LastModified])
VALUES (TblSource.[Code], TblSource.[ShortName], TblSource.[Name], TblSource.[LegalAddress], TblSource.[Status], GETDATE())
WHEN MATCHED AND
(TblTarget.[ShortName] != TblSource.[ShortName] OR
TblTarget.[Name] != TblSource.[Name] OR
TblTarget.[LegalAddress] != TblSource.[LegalAddress]) THEN
UPDATE SET
TblTarget.[ShortName] = TblSource.[ShortName],
TblTarget.[Name] = TblSource.[Name],
TblTarget.[LegalAddress] = TblSource.[LegalAddress],
TblTarget.[LastModified] = GETDATE()
WHEN NOT MATCHED BY SOURCE THEN
DELETE;
SELECT GETDATE() AS [endTime];
DROP INDEX [CodeStatus] ON [dbo].[LegalContractorTemps];
现在上面显示的代码 运行 大约需要 2 分钟。
我找到了 this 答案,但我无法将它应用到我的案例中,因为我需要 WHEN NOT MATCHED
子句,而且无论如何我都必须执行全面扫描(无论是否我将使用 MERGE
).
我会考虑进行修改后的冲洗和填充,而不是完全 MERGE
。
我最成功的方法是使用分区切换。您构建了三个相同的 table;您的用户从中提取的主要 table,用于应用 CRUD
操作的暂存 table,以及仅在过渡期间使用的暂存 table在你更新之后。
这将需要一些重新工具来将您的 LastModified
逻辑转移到您在更新期间执行的 CRUD
操作中。
然后,在登台 table 准备好黄金时间后,截断昨天的控股副本 table。接下来,将数据从主要 table 切换到现在为空的控股 table。将数据从 staging 切换到 main。可能将所有这些都包装在一个显式事务中。
繁荣。您的 table 是最新的。并且您在馆藏 table 中有一份昨天数据的备份副本,以防万一。
这些文章中的大量额外细节:
Comparison: Switching Tables vs. sp_rename
Why You Should Switch in Staging Tables Instead of Renaming Them
您能否推荐一些方法,使 SQL 中的 MERGE 操作运行得更快?
我认为这个问题都是关于知识和经验的,不应该被认为是基于意见的,因为任何可以使操作更快的东西绝对适合这个问题并且操作越快越好答案是。
在我的特定情况下,我有大约 170 万条记录,我在重复作业中获取这些记录,并使用这些记录来更新现有记录。为了尽可能少地锁定真实的table(它是[LegalContractors]
),我使用了一个临时的table(它是[LegalContractorTemps]
),我将所有的来自非 SQL(但 C#)代码的记录,之后我 运行 MERGE
。
这是我正在尝试的:
DELETE FROM [dbo].[LegalContractorTemps] WHERE [Code] IS NULL;
DELETE FROM [dbo].[LegalContractorTemps]
WHERE [Id] IN (
SELECT [Id]
FROM [dbo].[LegalContractorTemps] [Temp]
JOIN (
SELECT [Code], [Status], MAX([Id]) as [MaxId]
FROM [dbo].[LegalContractorTemps]
GROUP BY [Code], [Status]
HAVING COUNT([Id]) > 1
) [TempGroup]
ON ([Temp].[Code] = [TempGroup].[Code] AND [Temp].[Status] = [TempGroup].[Status] AND [MaxId] != [Id])
);
CREATE UNIQUE INDEX [CodeStatus]
ON [dbo].[LegalContractorTemps] ([Code], [Status]);
SELECT GETDATE() AS [beginTime];
MERGE [dbo].[LegalContractors] AS TblTarget
USING [dbo].[LegalContractorTemps] AS TblSource
ON (TblSource.[Code] = TblTarget.[Code] AND TblSource.[Status] = TblTarget.[Status])
WHEN NOT MATCHED BY TARGET THEN
INSERT ([Code], [ShortName], [Name], [LegalAddress], [Status], [LastModified])
VALUES (TblSource.[Code], TblSource.[ShortName], TblSource.[Name], TblSource.[LegalAddress], TblSource.[Status], GETDATE())
WHEN MATCHED AND
(TblTarget.[ShortName] != TblSource.[ShortName] OR
TblTarget.[Name] != TblSource.[Name] OR
TblTarget.[LegalAddress] != TblSource.[LegalAddress]) THEN
UPDATE SET
TblTarget.[ShortName] = TblSource.[ShortName],
TblTarget.[Name] = TblSource.[Name],
TblTarget.[LegalAddress] = TblSource.[LegalAddress],
TblTarget.[LastModified] = GETDATE()
WHEN NOT MATCHED BY SOURCE THEN
DELETE;
SELECT GETDATE() AS [endTime];
DROP INDEX [CodeStatus] ON [dbo].[LegalContractorTemps];
现在上面显示的代码 运行 大约需要 2 分钟。
我找到了 this 答案,但我无法将它应用到我的案例中,因为我需要 WHEN NOT MATCHED
子句,而且无论如何我都必须执行全面扫描(无论是否我将使用 MERGE
).
我会考虑进行修改后的冲洗和填充,而不是完全 MERGE
。
我最成功的方法是使用分区切换。您构建了三个相同的 table;您的用户从中提取的主要 table,用于应用 CRUD
操作的暂存 table,以及仅在过渡期间使用的暂存 table在你更新之后。
这将需要一些重新工具来将您的 LastModified
逻辑转移到您在更新期间执行的 CRUD
操作中。
然后,在登台 table 准备好黄金时间后,截断昨天的控股副本 table。接下来,将数据从主要 table 切换到现在为空的控股 table。将数据从 staging 切换到 main。可能将所有这些都包装在一个显式事务中。
繁荣。您的 table 是最新的。并且您在馆藏 table 中有一份昨天数据的备份副本,以防万一。
这些文章中的大量额外细节:
Comparison: Switching Tables vs. sp_rename
Why You Should Switch in Staging Tables Instead of Renaming Them