以下のクエリでは、両方の実行プランが一意のインデックスで1,000シークを実行すると推定されています。
シークは同じソーステーブルでの順序付けされたスキャンによって実行されるため、一見同じ順序で同じ値をシークする必要があります。
両方のネストされたループには <NestedLoops Optimized="false" WithOrderedPrefetch="true">
このタスクのコストが最初の計画では0.172434で、2番目の計画では3.01702である理由を誰もが知っていますか?
(質問の理由は、明らかにはるかに低い計画コストのために、最初のクエリが最適化として私に提案されたためです。 )
セットアップ
CREATE TABLE dbo.Target(KeyCol int PRIMARY KEY, OtherCol char(32) NOT NULL);
CREATE TABLE dbo.Staging(KeyCol int PRIMARY KEY, OtherCol char(32) NOT NULL);
INSERT INTO dbo.Target
SELECT TOP (1000000) ROW_NUMBER() OVER (ORDER BY @@SPID), LEFT(NEWID(),32)
FROM master..spt_values v1,
master..spt_values v2;
INSERT INTO dbo.Staging
SELECT TOP (1000) ROW_NUMBER() OVER (ORDER BY @@SPID), LEFT(NEWID(),32)
FROM master..spt_values v1;
クエリ1 「計画の貼り付け」リンク
WITH T
AS (SELECT *
FROM Target AS T
WHERE T.KeyCol IN (SELECT S.KeyCol
FROM Staging AS S))
MERGE T
USING Staging S
ON ( T.KeyCol = S.KeyCol )
WHEN NOT MATCHED THEN
INSERT ( KeyCol, OtherCol )
VALUES(S.KeyCol, S.OtherCol )
WHEN MATCHED AND T.OtherCol > S.OtherCol THEN
UPDATE SET T.OtherCol = S.OtherCol;
クエリ2 「計画の貼り付け」リンク
MERGE Target T
USING Staging S
ON ( T.KeyCol = S.KeyCol )
WHEN NOT MATCHED THEN
INSERT ( KeyCol, OtherCol )
VALUES( S.KeyCol, S.OtherCol )
WHEN MATCHED AND T.OtherCol > S.OtherCol THEN
UPDATE SET T.OtherCol = S.OtherCol;
クエリ1
クエリ2
上記はSQL Server 2014(SP2)(KB3171021)-12.0.5000.0(X64)でテストされました
@Joe Obbishはコメントで、より単純な再現は
SELECT *
FROM staging AS S
LEFT OUTER JOIN Target AS T
ON T.KeyCol = S.KeyCol;
対
SELECT *
FROM staging AS S
LEFT OUTER JOIN (SELECT * FROM Target) AS T
ON T.KeyCol = S.KeyCol;
1,000行のステージングテーブルの場合、上記の両方とも、ネストされたループを含む同じ計画形状を持ち、派生テーブルのない計画は安価に見えますが、10,000行のステージングテーブルと同じターゲットテーブルの場合、コストの違いによって計画が変更されますこのコストの不一致は、プランの比較を難しくするだけでなく、意味を持ちます(フルスキャンとマージ結合は、高価なシークよりも比較的魅力的と思われます)。