web-dev-qa-db-ja.com

非常に大きなテーブルのすべての行を更新するときに、SQL Serverのディスク領域の使用を減らす最も効果的な手法は何ですか?

SQL Serverテーブルが

  • 多数の行
  • 大きな値のデータ型の列はありません
  • 複数のインデックス
  • 可能な最大のトランザクションログサイズに使用できるよりも多くの割り当てられたスペース
  • クラスタ化インデックス付きの単一列の主キー(この質問に対するオプションの考慮事項)
  • 1kの平均レコードサイズ(この質問に対するオプションの考慮事項)

そして、更新ステートメント

  • すべての行に対して実行する必要があります
  • インデックス付けされていない列に値を設定します(この質問に対するオプションの考慮事項)

この更新に必要なピークディスクスペースの消費を減らす(データファイル、ログファイル、tempdb-該当する場合)に必要なテクニックは何ですか?

この質問では、次のことが許可されています。

  • 変更をバッチで適用する
  • シングルユーザーモードで実行する
  • 復旧モデルの変更
6
BrianCooksey

ほんの数週間前に同様のプロセスを経験しました。数回の試行の後、いくつかの大きなテーブル(そのうちの1つは1億行を超え、80 GB近く)を使用して、処理を高速化し、トランザクションログを小さく保つために、次の手順を思いつきました。

以下は、一度に1000行の更新のバッチ処理のサンプルです。

 UPDATE TOP(1000) your_table
 SET    col1 = new_value
 WHERE  <your_condition>
 WHILE  @@rowcount > 0
 BEGIN
     UPDATE TOP(1000) your_table
     SET    col1 = new_value
     WHERE  <your_condition>;
 END;
 GO
  • 非クラスター化インデックスを復元する

  • トランザクションアイソレーションレベルの設定READ COMMITTED

  • ALTER DATABASE [my_db] SET RECOVERY FULL;

6
Yaroslav

基本的に、1つの大規模なトランザクションが発生しないように、更新を1000行または10000行の小さなチャンクにバッチ処理するオプションがあります。 ID列または日付列がある場合、これは簡単に使用できますが、少しトリッキーではありませんが、それでも実行可能です。

すべての行が更新されるまで、トップリミッターで更新を使用し、ステートメントを複数回実行します。

4
mrdenny

クラスタ化インデックスがない場合(理論的には1つあるため):列で更新する内容に注意してください。レコードサイズを大きくすると、多くの転送されたレコードで終了する可能性があります(dm_db_index_physical_stats-forwarded_record_countを参照)。

ただし、クラスター化インデックスでは、更新された行サイズに注意する必要があります。ページの分割や断片化につながる可能性があります。定期的なメンテナンスがある場合、トランザクションログのサイズが大きくなる可能性があります。

可能であれば、復旧モデルを単純に変更します。変更するレコードの総数に関係なく、トランザクションログとして必要なのはバッチチャンクサイズのみです。ジョブ全体を再開できることを確認してください。最初からすべての行ではなく、変更されていない行のみが更新されます。

この列にインデックスがあった場合は、更新のためにそれを無効にし、後でそれを再構築する必要があります。

これらの列の統計情報を確認することもできますが、これはパフォーマンスに関する質問なので、質問していません。

0
Jens W.