妥当な時間内に約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互換モードです。
すみません、この質問を忘れていました。しかし、問題は解決しました。 MERGEステートメントを使用すると、クエリ時間が1時間未満に短縮されました。インデックスを無効にしても効果はありませんでした。
あなたは、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つのこと:
インデックスを削除するのではなく、この特定の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 );