我的情况是,“发布者”应用程序通过查询非常复杂的视图,然后使用单独的插入、更新和删除操作将结果合并到非规范化视图模型表中,从而使视图模型保持最新。
现在我们已经升级到 SQL 2008,我认为现在是使用 SQL MERGE 语句更新这些的好时机。然而,在编写查询之后,MERGE 语句的子树成本是 1214.54!用旧方法,插入/更新/删除的总和只有 0.104!
我无法弄清楚描述相同确切操作的更直接的方式怎么会如此糟糕。也许你可以看到我看不到的错误。
表上的一些统计信息:它有 190 万行,每个 MERGE 操作插入、更新或删除超过 100 行。在我的测试用例中,只有 1 个受到影响。
-- This table variable has the EXACT same structure as the published table
-- Yes, I've tried a temp table instead of a table variable, and it makes no difference
declare @tSource table
(
Key1 uniqueidentifier NOT NULL,
Key2 int NOT NULL,
Data1 datetime NOT NULL,
Data2 datetime,
Data3 varchar(255) NOT NULL,
PRIMARY KEY
(
Key1,
Key2
)
)
-- Fill the temp table with the desired current state of the view model, for
-- only those rows affected by @Key1. I'm not really concerned about the
-- performance of this. The result of this; it's already good. This results
-- in very few rows in the table var, in fact, only 1 in my test case
insert into @tSource
select *
from vw_Source_View with (nolock)
where Key1 = @Key1
-- Now it's time to merge @tSource into TargetTable
;MERGE TargetTable as T
USING tSource S
on S.Key1 = T.Key1 and S.Key2 = T.Key2
-- Only update if the Data columns do not match
WHEN MATCHED AND T.Data1 <> S.Data1 OR T.Data2 <> S.Data2 OR T.Data3 <> S.Data3 THEN
UPDATE SET
T.Data1 = S.Data1,
T.Data2 = S.Data2,
T.Data3 = S.Data3
-- Insert when missing in the target
WHEN NOT MATCHED BY TARGET THEN
INSERT (Key1, Key2, Data1, Data2, Data3)
VALUES (Key1, Key2, Data1, Data2, Data3)
-- Delete when missing in the source, being careful not to delete the REST
-- of the table by applying the T.Key1 = @id condition
WHEN NOT MATCHED BY SOURCE AND T.Key1 = @id THEN
DELETE
;
那么这如何达到 1200 个子树的成本呢?从表本身访问数据似乎非常有效。事实上,MERGE 87% 的成本似乎来自链末端附近的排序操作:
合并 (0%) <- 索引更新 (12%) <- 排序 (87%) <- (...)
这种排序有 0 行进出它。为什么排序 0 行需要 87% 的资源?
更新
我在 Gist 中发布了仅 MERGE 操作的实际(非估计)执行计划。