首页 > 解决方案 > 允许使 SQL 中的 MERGE 操作更快地工作的方法

问题描述

您能否推荐一些可以使 SQL 中的 MERGE 操作更快地工作的方法?

我认为这个问题完全是关于知识和经验的,不应该被认为是基于意见的,因为任何可以使操作更快的东西绝对适合这个问题,操作越快,答案就越好。

在我的特定情况下,我有大约 170 万条记录,我在重复性工作中获取这些记录,并使用这些记录来更新现有记录。为了[LegalContractors]尽可能少地锁定真实表(它是),我使用了一个临时表(它是[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 分钟。

我找到了这个答案,但我无法将它应用到我的案例中,因为我需要该WHEN NOT MATCHED子句并且无论如何我都必须执行完整扫描(无论我是否会使用MERGE)。

标签: performancetsqlmergesql-merge

解决方案


我会考虑做一个修改后的冲洗和填充,而不是做一个MERGE

我最成功的方法是使用分区切换。您构建了三个相同的表;用户从中提取的主表、用于应用CRUD操作的临时表以及仅在更新后的过渡期间使用的暂存表。

这将需要一些重新工具来将您的LastModified逻辑直接转移到CRUD您在更新期间执行的操作中。

然后,在暂存表准备好黄金时间后,截断昨天的暂存表副本。接下来,将数据从主表切换到现在为空的保留表。将数据从 staging 切换到 main。可能将所有这些包装在显式事务中。

繁荣。您的表是最新的。并且您在保留表中有昨天数据的备份副本,以防万一。

这些文章中有大量额外的细节:

比较:交换表与 sp_rename

为什么应该切换临时表而不是重命名它们


推荐阅读