web-dev-qa-db-ja.com

パフォーマンスSQL Serverの削除と挿入

完了までに1時間近くかかる以下のストアドプロシージャがあります。結果セットは約200Kのマシンです。

私がしているのは、リンクサーバーからIDのセットを抽出し、ローカルサーバーでそれらのIDを削除してから、リモートサーバーからそれらのIDのすべての詳細を抽出して挿入することだけです。 INSERTは90%、DELETEテーブルは9%、SELECT INTOは1%です。

idは主キー/クラスター化インデックスです。

    DECLARE @ProcessDate DATETIME
    SET @ProcessDate = CONVERT(VARCHAR(10), DATEADD(DAY, -3,
                                                    GETDATE()), 111)

    SELECT  DISTINCT id
    INTO    #temp_machines
    FROM    remote_server.db.dbo.table1
    WHERE   dt_modify >= @ProcessDate

    DELETE  FROM local_server.db.dbo.table2
    WHERE   id IN ( SELECT id  FROM  #temp_machines )

    INSERT  INTO local_server.db.dbo.table2
    SELECT  *
    FROM    remote_server.db.dbo.table1
    WHERE   id IN ( SELECT id  FROM  #temp_machines )

パフォーマンスの向上に関する提案はありますか?

1
VK_217

最初のステップ:調整するものがあり、クエリが単にブロックされていないことを確認します。 sp_WhoIsActive または sp_BlitzFirst (免責事項、私はFirst Responder Kitの寄稿者です)などの無料のツールを使用してこれを行うことができます。

2番目のステップ:ローカル変数 は使用しないでください。

3番目のステップ:多分 一時テーブルのインデックス作成 が役立ちます。

4番目のステップ:待機統計を確認 sp_BlitzFirstを使用してください(私がそれに貢献していることに関する同じ免責事項、何とか何とか何とか)。書かれているクエリは問題ないかもしれませんが、tempdbの競合など、他の問題が発生しています。

5
Erik Darling

「リモートサーバー」とはリンクサーバー(または、少なくとも類似のもの)を意味すると仮定すると、1つの潜在的な問題は、同じデータに相当するものに対して2つのクエリを実行することです。また、IN (SELECT ...)を使用することにより、サーバーにそのSELECTステートメントを行ごとに1回再実行させることができます。

私は次のようなことを試します:

DECLARE @ProcessDate DATETIME
SET @ProcessDate = CONVERT(VARCHAR(10), DATEADD(DAY, -3,
                                                GETDATE()), 111)

SELECT  *
INTO    #temp_machines
FROM    table1
WHERE   dt_modify >= @ProcessDate

DELETE  t2
FROM    table2 t2
          INNER JOIN (SELECT DISTINCT id FROM #temp_machines) tm ON (t2.id = tm.id)

INSERT  INTO table2
SELECT  *
FROM    #temp_machines

結局のところ、ある時点でtable1からすべての一致するデータをローカルDBにプルすることになります。事前に取得しておくと、残りのクエリでリモートサーバーを無視できます。

さらに良いことに、table1.idが一意または主キーである場合、DELETEは次のようになります。

DELETE  t2
FROM    table2 t2
          INNER JOIN #temp_machines tm ON (t2.id = tm.id)

table1に特定のid値のインスタンスが複数ある場合でも、これが機能する可能性があります。

2
RDFozz

クエリ構文(匿名化、ナッチ)には表示されませんが、あなたの質問はtable1はリモートサーバー上にあります。おそらく、4部構成の構文を使用して実際に対処しているのでしょう。

[RemoteServerAlias].[DatabaseName].[SchemaName].[TableName]

そのリモートテーブルを更新するとき、SQLには問題を最適化しようとする興味深い時間があります。必要な情報がすべて含まれていないため、非常に疑問のある決定を下すことがあります。そこにある最後のクエリは、リモートテーブルのコンテンツ全体をフィルターする前に、そのサーバーからリモートテーブルの内容全体を確実に取得しています。

最初のクエリが十分に速い場合、つまりSQLがその日付基準を適用している場合は、回避策を提案します。最後にその基準を追加します。

INSERT  INTO table2
SELECT  *
FROM    table1
WHERE   id IN ( SELECT id  FROM  #temp_machines )
AND     dt_modify >= @ProcessDate
1
Paul Atkinson