如何使用多列索引优化 MERGE
Posted
技术标签:
【中文标题】如何使用多列索引优化 MERGE【英文标题】:How to optimize MERGE using multiple column index 【发布时间】:2015-05-07 21:23:55 【问题描述】:我是 MERGE 的新手和索引新手,所以请耐心等待...
我有一个存储过程,它构建一个 #changes
临时表,根据 #changes
更新一个 prod_tbl
表,然后将之前和之后的值插入到 auto_update_log
表中,该表随着重复行快速增长。为了防止这种情况,我想使用 MERGE 语句。速度是最重要的,线程安全也是最重要的,因为这个表会被操作一整天。
auto_update_log
上没有任何现有索引,也没有任何类型的键。我的想法是使用键列(来自auto_update_log
表)和所有前后列创建一个多列索引,以帮助加快 MERGE 的速度。将有 6 个前后列加上与auto_update_log
的 PK 相关的一键列。
示例日志表:
CREATE TABLE dbo.sample_auto_update_log (
id INT NOT NULL, --Primary key from [prod_tbl]
item_a_before VARCHAR(25) NULL, --[prod_tbl].[item_a]
item_a_detail VARCHAR(25) NULL, --Value from elsewhere in the DB that applies
item_a_after VARCHAR(25) NULL, --The new value SET for [prod_tbl].[item_a]
update_count INT NOT NULL DEFAULT (0),
update_datetime DATETIME NOT NULL DEFAULT (GETDATE())
);
示例合并:
MERGE sample_auto_update_log WITH (HOLDLOCK) AS t
USING #changes AS s
ON (t.id = s.id AND t.item_a_before = s.item_a_before AND t.item_a_after = s.item_a_after)
WHEN MATCHED THEN
UPDATE
SET update_count = update_count + 1, update_datetime = GETDATE()
WHEN NOT MATCHED THEN
INSERT (id, item_a_before, item_a_detail, item_a_after)
VALUES (s.id, s.item_a_before, s.item_a_detail, s.item_a_after);
SQL Fiidle
问题: 如何使用索引或其他方法最好地优化 MERGE?
【问题讨论】:
老实说,您最好不要使用 MERGE 进行 upserts。 MERGE 有许多错误,像这样的 upsert 就是其中之一。 mssqltips.com/sqlservertip/3074/… @SeanLange ,你有什么好的选择吗?我已经阅读了几篇文章,包括您发布的文章,似乎很多这些问题不适用于我的情况。例如,我没有在我的目标或源表中使用主键,没有变量或表变量,没有在触发器中使用它,我已经在使用(HOLDLOCK)
提示来防止竞争条件等。我也可能有夸大了线程安全的重要性——性能是一个更大的问题,因为用户不太可能同时访问相同的记录,但这会运行很多次。
为什么不直接发出两条语句加入您的临时表?第一个是更新,然后是插入。
真的只是因为我阅读MERGE可以更快。我想使用这里建议的方法:***.com/a/21209131/550595,但这似乎只适用于单行。不过,我可能只使用两条语句,如果这成为问题,那么我将编写一个删除重复项并增加计数器的作业。
【参考方案1】:
考虑以下方法。
在更新数据的过程中将简单快速的INSERT
插入auto_update_log
。在此阶段不要关心 auto_update_log
中的重复项。
有另一个后台进程定期(每隔几分钟,或任何适合您的系统)汇总auto_update_log
中累积的内容,并使用不重复的简明摘要更新最终的log
表。使用MERGE
使用适当的支持索引更新摘要。将auto_update_log
添加到摘要中后对其进行清理。
换句话说,auto_update_log
成为允许批量更新摘要的临时临时表。摘要中的信息会延迟,因此您需要决定是否可以接受。
使用您在问题中提出的示例MERGE
语句,我会考虑在(id, item_a_before, item_a_after)
上添加索引 - 用于比较的那些字段。或者只是在 id
加上 item_a_before, item_a_after
作为包含的列。在两个表auto_update_log
和您的临时表#changes
中。
拥有索引可以加快查找必须更新的行的过程,但在添加行时更新索引本身需要时间。因此,最终,您需要尝试衡量不同方法的性能。
【讨论】:
这基本上是我的备份计划。我打算创建一个作业/SP,它使用 CTE 对这些字段进行分区并识别重复项,然后更新原始行的计数器并删除重复项,或者根据 CTE 的内容重写表。这项工作只需要每晚打扫。我只是更喜欢清理数据,而不是事后清理它,但这没什么大不了的——我已经花了太多时间在这上面。感谢您的建议,我会将其标记为答案,因为这就是我要做的。 您可以先尝试简单地添加索引,如我在答案第二部分中所述。在任何情况下,您都需要衡量更改前后的性能。以上是关于如何使用多列索引优化 MERGE的主要内容,如果未能解决你的问题,请参考以下文章