【发布时间】:2011-07-16 01:23:40
【问题描述】:
我有一个包含一些持久数据的表。现在,当我查询它时,我还有一个非常复杂的 CTE,它计算结果所需的值,我需要将缺失的行插入到持久表中。最后,我想选择由 CTE 标识的所有行组成的结果,但如果它们已经在表中,则包含表中的数据,并且我需要是否刚刚插入行的信息。
简化后的工作方式如下(如果您想尝试,以下代码将作为普通查询运行):
-- Set-up of test data, this would be the persisted table
DECLARE @target TABLE (id int NOT NULL PRIMARY KEY) ;
INSERT INTO @target (id) SELECT v.id FROM (VALUES (1), (2)) v(id);
-- START OF THE CODE IN QUESTION
-- The result table variable (will be several columns in the end)
DECLARE @result TABLE (id int NOT NULL, new bit NOT NULL) ;
WITH Source AS (
-- Imagine a fairly expensive, recursive CTE here
SELECT * FROM (VALUES (1), (3)) AS Source (id)
)
MERGE INTO @target AS Target
USING Source
ON Target.id = Source.id
-- Perform a no-op on the match to get the output record
WHEN MATCHED THEN
UPDATE SET Target.id=Target.id
WHEN NOT MATCHED BY TARGET THEN
INSERT (id) VALUES (SOURCE.id)
-- select the data to be returned - will be more columns
OUTPUT source.id, CASE WHEN $action='INSERT' THEN CONVERT(bit, 1) ELSE CONVERT(bit, 0) END
INTO @result ;
-- Select the result
SELECT * FROM @result;
我不喜欢 WHEN MATCHED THEN UPDATE 部分,我宁愿放弃多余的更新,但在 OUTPUT 子句中我没有得到结果行。
这是完成和返回数据的最有效方式吗?
或者如果没有MERGE,是否会有更有效的解决方案,例如通过使用SELECT 预先计算结果,然后对new=0 的行执行INSERT?我很难解释查询计划,因为它基本上归结为“聚集索引合并”,与单独的SELECT 后跟INSERT 变体相比,这在性能方面对我来说相当模糊。我想知道 SQL Server(带有 CU1 的 2008 R2)是否真的足够聪明,可以看到 UPDATE 是空操作(例如,不需要写入)。
【问题讨论】:
-
我对 with、merge 和冗余更新有完全相同的设置,并且我还在寻找一种实际上不执行更新但仍返回 ID 的解决方案。如果您找到解决方案,请添加它=)
-
RE:非更新更新您可能最好对不属于集群键(如果可能)的列进行无操作更新,如此处所述sqlblog.com/blogs/paul_white/archive/2010/08/11/…
-
@David Mårtensson,感谢您添加赏金。 :)
-
@Lucero - 让我看看我是否关注你。您想将一堆行插入@target 并输出插入@result 的行。对吗?
-
如果我理解的话,他想要插入的 ID 和存在的行的 ID,这就是合并的地方。如果没有合并,您将无法获得已经存在的其他行的 ID。问题的关键是它是否可以在没有实际更新的情况下完成,因为这会降低性能。
标签: tsql sql-server-2008 sql-update sql-merge