web-dev-qa-db-ja.com

大きなテーブル(約650万行)とインデックスの更新

妥当な時間内に約650万行のテーブルを更新しようとしていますが、いくつか問題があります。既存のテーブルに新しい列を追加し、別のテーブルの列のデータに基づいてすべての行の値を設定しています。

UPDATE  TOP (20000) c
SET     c.NewColumn = ISNULL(p.Col1, p.Col2)
FROM    dbo.Child c
    INNER JOIN dbo.Parent p on c.FKId = p.Id
WHERE   c.NewColumn IS NULL

このようにループします article 。更新は2.5時間後も実行されていました。 dbo.Childのインデックスを無効にすると影響があるかどうか疑問に思っています。 NewColumnにはインデックスもありませんが、dbo.Childには他のインデックス(約5)があります。

SQL Serverは、他のインデックスを更新する必要がない(UPDATEの一部ではないため)ことを確認するのに十分スマートですか、またはupdateステートメントを実行している間、インデックスを一時的に無効にするとメリットがありますか?

これはSQL Server 2012ですが、問題のDBは2008互換モードです。

3
Andy

すみません、この質問を忘れていました。しかし、問題は解決しました。 MERGEステートメントを使用すると、クエリ時間が1時間未満に短縮されました。インデックスを無効にしても効果はありませんでした。

0
Andy

あなたは、UPDATEがループで実行されていて、2.5時間後もまだ実行されていたと言います。そのループは行をまったく更新しましたか?長い時間を費やしていたのはループ自体なのか、それとも単一のUPDATEステートメントなのかこれはやや重要な違いであり、現在の所与の情報ではあいまいです。ただし、質問で述べたことに基づいて、次の点に注意してください。

  • いいえ、SQL Serverは新しい列を使用しないインデックスを更新するべきではありません

  • インデックスを無効にすることは、単にそれらをオフにすることの問題ではありません。実際にはすべてのインデックスページが削除され、構造のみが保持されるため、CREATEステートメント全体を再度実行する必要はありません。ただし、インデックスを再度有効にするには、インデックスを再構築する必要があります。

そしてここに探すべきいくつかの事柄があります:

  • ChildテーブルにUPDATEトリガーはありますか?

  • トランザクションログの自動拡張設定とは何ですか?非常に低い数に設定した場合、多数の自動拡張操作によってUPDATEが妨げられる可能性があります。

  • Childテーブルの5つのインデックスのうち、FKIdが先頭の列として含まれているものはありますか?

  • 低いバッチサイズ、または20kだけを試しましたか?これらのタイプの操作には、操作をその数に制限するだけでなく、その数がヒットする可能性があることをクエリオプティマイザーに示唆することなく、ランダムに大きな(10k-50k)バッチサイズを推奨する人が何人かいます。そもそも。したがって、検索する行が5000行しかない場合、c.NewColumn IS NULL基準を満たす他の行があるかどうかを確認するために、それらの行を探し続けます。そして、フィールドがインデックス付けされていないことを考えると、すべてのQ.O.続行しなければならないのは、残りのNULL行の検索場所を指示しない自動生成された統計です。ループの最初の数回の反復が成功したと仮定すると、SQL Serverは、特定の順序で何回でもスキャンすることにより、行を見つける前に、650万行からNULLである20,000行を見つける必要があります。したがって、最初の数回の反復はおそらく迅速に行われましたが、このタイプの操作は、IS NULL基準を満たす20,000を見つける前に、連続する各パスがより多くのレコードをスキャンする必要があるため、急速に遅くなります。

    したがって、考慮すべき2つのこと:

    • 小さいバッチサイズ(おそらく10行)から始めて、そのステートメントを単独で(つまり、ループ内ではなく)実行します。戻ってきますか?そうでない場合は、WAAIT_RESOURCEとは何かに関する@Aaronの質問に答える必要があります。戻ってきた場合は、バッチサイズを100、次に500、1000、2000、5000、10000に増やします。完了するまでにさらに多くのループが必要になりますが、UPDATEに必要なレコード数を見つけるのは簡単です。応答性があると思われるサイズを見つけたら、ループを再度実行できます(2000〜5000の範囲のどこかでうまくいくと思います:)
    • インデックスを削除するのではなく、この特定のUPDATE操作をサポートするために、一時的にインデックスを作成したい場合があります。 filtered index (SQL Server 2008で導入されたため、互換モードで問題が発生しないはずです)を使用すると、残りの行を更新対象にできます。何かのようなもの:

      CREATE NONCLUSTERED INDEX [IX_Child_FKId_temp]
        ON dbo.Child ([FKId] ASC) -- you might need to include [NewColumn] ASC after [FKId]
        WHERE [NewColumn] IS NULL
        WITH ( FILLFACTOR = 100 );
      
2
Solomon Rutzky