2011-09-13 10 views
1

「パブリッシャ」アプリケーションは、非常に複雑なビューを問い合せ、結果を非正規化ビュー・モデル表にマージすることによって、ビュー・モデルを最新の状態に保つことができます、更新、および削除操作を実行します。T-SQL MERGE典型的なパブリッシング・コンテキストでのパフォーマンス

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のサブツリーコストになりますか?テーブル自体からのデータアクセスは非常に効率的であるようです。インデックスを更新(12%)< - - 分類(87%)<

MERGE(0%)<:実際には、MERGEのコストの87%は、チェーンの終わり近くにソート操作からのようです - (...)

この並べ替えには、0行の入力と出力があります。 0行をソートするのに87%のリソースが必要なのはなぜですか?

UPDATE

私は要旨にexecution plan for just the MERGE operation(推定ではない)、実際に投稿しました。

+0

動作するかどうかを知ることができますが、実際の実行計画を投稿することができますか?私はこれが推定された計画ではなく実際のものであると仮定しています。 – JNK

+0

MERGEの実行計画を要点に追加しました。明らかに、私は雇用者の幸福を改善するためにすべてのデータベース名を変更しました。プランXMLにも同様の変更を加えようとしました。私は何かをねじにしなかったことを願っています!これは、SQL Management Studioにはまだ読み込まれます。 –

+0

リソースの87%を占めるわけではありません。実際の計画に示されているコストは、依然として見積もりコストです。実際に何が起こったのかを考慮して調整しません。なんらかの理由で、それは3、348,560行をソートすることになると考えています。 '[Action1007] IS NOT NULL'のフィルタは、実際の行数を0に減らし、その後はすべての原価計算をオフにします。 –

答えて

2

サブトリーのコストは、大きな粒度の塩を使用する必要があります(特に、重大なカーディナリティエラーがある場合)。 SET STATISTICS IO ON; SET STATISTICS TIME ON;出力は、実際のパフォーマンスを示す優れた指標です。

ゼロ行のソートでは、リソースの87%が使用されません。あなたの計画におけるこの問題は、統計的推定の一つです。実際の計画に示されているコストは、依然として見積もりコストです。実際に何が起こったのかを考慮して調整しません。

フィルタが1,911,721行を0に減らす点がありますが、今後の推定行は1,860,310です。その後、すべてのコストは、87%のコスト見積もり3,348,560行のソートで最高になります。

Mergeステートメントの外部で、同等の述部(同じ1,860,310行の見積もりを持つ)を持つFull Outer Joinの見積もり計画を見て、基数見積もりエラーを再現することができます。

SELECT * 
FROM TargetTable T 
FULL OUTER JOIN @tSource S 
    ON S.Key1 = T.Key1 and S.Key2 = T.Key2 
WHERE 
CASE WHEN S.Key1 IS NOT NULL 
    /*Matched by Source*/ 
    THEN CASE WHEN T.Key1 IS NOT NULL 
       /*Matched by Target*/ 
       THEN CASE WHEN [T].[Data1]<>S.[Data1] OR 
           [T].[Data2]<>S.[Data2] OR 
           [T].[Data3]<>S.[Data3] 
         THEN (1) 
        END 
       /*Not Matched by Target*/  
       ELSE (4) 
      END 
     /*Not Matched by Source*/  
     ELSE CASE WHEN [T].[Key1][email protected] 
       THEN (3) 
      END 
END IS NOT NULL 

しかし、フィルタ自体までの計画は非常に最適ではないと言われました。おそらく2つのクラスタード・インデックス・レンジ・シークを持つ計画が必要なときに、完全なクラスタード・インデックス・スキャンを実行しています。 1つは、プライマリキーと一致する単一の行をソース上の結合から取得し、もう1つはT.Key1 = @idの範囲を取得します(これは後でクラスタ化されたキー順にソートする必要性を避けるためです)。)

Original Plan

おそらく、あなたはこの書き換えを試してみて、それがどの良くも悪くも

;WITH FilteredTarget AS 
(
SELECT T.* 
FROM TargetTable AS T WITH (FORCESEEK) 
JOIN @tSource S 
    ON (T.Key1 = S.Key1 
    AND S.Key2 = T.Key2) 
    OR T.Key1 = @id 
) 
MERGE FilteredTarget AS T 
USING @tSource S 
ON (T.Key1 = S.Key1 
    AND S.Key2 = T.Key2) 


-- Only update if the Data columns do not match 
WHEN MATCHED AND S.Key1 = T.Key1 AND S.Key2 = T.Key2 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 

-- Note from original poster: This extra "safety clause" turned out not to 
-- affect the behavior or the execution plan, so I removed it and it works 
-- just as well without, but if you find yourself in a similar situation 
-- you might want to give it a try. 
-- WHEN MATCHED AND (S.Key1 <> T.Key1 OR S.Key2 <> T.Key2) AND T.Key1 = @id THEN 
-- DELETE 

-- Insert when missing in the target 
WHEN NOT MATCHED BY TARGET THEN 
    INSERT (Key1, Key2, Data1, Data2, Data3) 
    VALUES (Key1, Key2, Data1, Data2, Data3) 

WHEN NOT MATCHED BY SOURCE AND T.Key1 = @id THEN 
    DELETE; 
+1

これは素晴らしい仕事でした!マージステートメントで0.0638の実行計画を実行しました。これは、実行時間が1秒以上前と比較して平均で約1.4ミリ秒であることによってバックアップされます。また、余分なWHEN MATCHED-DELETE句は動作や実行計画に影響しないことが分かりましたので、私はそれを取り出してここにあなたの答えを編集しました。本当にありがとう! –

関連する問題