web-dev-qa-db-ja.com

SQL Server 2008で大きなテーブルのVARCHARフィールドをNVARCHARフィールドに変更する効率的な方法は?

新しいフィールドを大きなテーブルに追加するときに気づいていますが、中央のどこかではなく、フィールドの最後に追加することをお勧めします。フィールドタイプを変更するときに、このようなことが当てはまるかどうか疑問に思いますか?

いくつかのVARCHARタイプのフィールドを持つ約100万レコードのテーブルがあります。これらをNVARCHARに変更したいと思いますが、理解すると、フィールドがテーブルの中央にあり、SQL Serverが大量のコピー/並べ替えを行う必要があるため、これには時間がかかり、リソースがかかります。

これを達成する効率的な方法は何ですか?

7
ElHaix

質問に直接答えるには、操作を実行する2つの方法があります。

  • テーブルに含まれるvarchar列の数が少ない場合(1つまたは2つ)、疑似一時列を作成する方が現実的です
  • Varchar列の数が多い場合、上記の方法はあまり実用的ではないため、疑似テーブルを作成します。これは、ErWinやER/Studioなどの一部のデータベースツールのメタデータ更新スクリプトで最も使用されます(私は両方のツールを使用し、適用前に生成されたスクリプトを確認しました)

大きなテーブルに関する注意:テーブルに数千以下のレコードがある場合、一度に操作を実行できます。 100万レコードのテーブルの場合は、バッチで実行する方が現実的です(たとえば、毎回数千または数百のレコードとしましょう)。

Pseudo-tempカラム

疑似一時列(別の適切な名前があるかどうかは忘れました)は、変換の結果を格納するために使用される列です。この場合、それらはプロセス後の最後の列にもなります。

  1. 目的の長さで新しい列を作成します。新しい定義にチェック制約またはデフォルトを含めることを忘れないでください
  2. 古い列のデータを新しい列に格納するために、更新(または更新、上記の観察を参照)を実行します。
  3. ログのバックアップを実行し、チェックポイントを実行して、ログが極端に大きくならないようにします。
  4. 古い列に制約が関連付けられている場合は、それらを削除します。
  5. 古い列をドロップします。
  6. 新しい列の名前を古い列の名前に変更します
  7. 影響を受けるインデックスを再構築します(または影響を受ける列がクラスター化された主キー制約の一部である場合はすべて再構築します n)varcharをPKとして使用することはまれですが、いくつか見ました)。

これは---(Aaronの答え で詳述されているのと同じプロセスです。

疑似一時テーブル

変更が少数の列にある場合は、古いテーブルのスキーマに基づいて新しいテーブルを作成する方が現実的です。

  1. テーブルの制約(PK、FKなど)なしで新しいテーブルを作成します。現時点では列1のみを使用してください(NOT NULL、DEFAULT、CHECKなど)
  2. 古いテーブルのデータを新しいテーブルに挿入します(上記の大きなテーブルに関する注意を参照)。 SET IDENTITY_INSERTは必須です。
  3. 次に、すべてのテーブル制約(PK、FK、チェック)とトリガーを古いテーブルにドロップします。新しいテーブルでその制約とトリガーを再作成します。
  4. 新しいテーブルで、古いテーブルの他のすべてのインデックスを(メンテナンスウィンドウに応じて、一度にまたは一度に1つずつ)再作成します。テーブルにクラスター化インデックスがない場合を除き、これは手順3の後で、または少なくともPK制約の作成後に行う必要があります。
  5. すべてが正しく行われたかどうか(プロセスのトリガーまたは制約を忘れていない場合)を確認し、問題がなければ、古いテーブルを削除します。
  6. 新しいテーブルの名前を古いテーブルの名前に変更します

ステップ4に関する注意:重複インデックスが検出された場合(重複インデックスの検出は非常に長い件名です。SQLSkills.comのKimberly Trippのブログを参照してください)、これがチャンスですその場合はそれらを取り除く。

パフォーマンスへの影響

VARCHARからNVARCHARに変更すると、少なくとも2008R2より前のSQL Serverでは、パフォーマンスにいくつかの影響があります。 SQL 2008 R2の場合、Aaron BertrandがUnicode圧縮機能に関するいくつかのブログ投稿を公開しています。これは、NVARchar列がVARCHAR列に格納できるコンテンツの格納に使用される場合にバランスを取ることができます。記事に値するので完全には読みませんでしたが、主題は興味深いです。

通常、NVARCHAR列は(IOW、2008R2より前)、すべての文字を1文字あたり2バイトで列に格納します。たとえば、文字列 'MSSQL'はVARCHAR列に5バイト、NVARCHAR列に10バイトで格納されます。非LOB文字列の列は最大8000bytesを格納するように制限されているため、NVARCHRは4000に制限されているのに対し、VARCHARは8000文字を格納できます。

その事実の影響:

  • インデックスキーは900バイトに制限されているため(CREATE INDEXのドキュメントを参照)、NVARCHAR(500)列にインデックスを付けようとしても、コマンドは失敗しません(これがインデックスキーの1つの列だけの場合)。ただし、UPDATEまたは450を超える行を挿入する-(インデックスキーのその他の列の合計サイズ(該当する場合))文字で操作が失敗します。
  • 操作するバイト数が増えるほど、実行する作業が増えます。 2倍のバイトを読み取り/書き込み/比較/キャッシュします。
  • テーブルの大きさ、保存されているテーブルのサイズに対する文字列列の影響、およびデータベースのサイズに対するテーブルの関与の仕方に応じて、(使用される)データベースのサイズとそれに影響するすべての変数の増加が予想されます。直接かどうか(バックアップ/復元時間、インデックスのメンテナンスなど)。

編集:gbnが述べたように、NVARCHAR列をフルフィルする必要がある明確な要件がある場合は、VARCHARを使用するだけで何かを作成する価値はありません。

7
Fabricio Araujo

片道可能性があるする:

  1. NULL可能なNVARCHAR列を追加する
  2. バッチを使用して、一度に多数の行を更新します(例:1000または10000行)
  3. ログ、チェックポイント、バッチ間で何をしているかをバックアップ
  4. すべての行が更新されたら、古い列を削除し、新しい列の名前を変更します
  5. インデックスを再構築する

これは長期的には速くはならず、メンテナンスウィンドウも必要です(一時的なトリガーを配置しない限り、ユーザーが既に更新した行を更新したくないためです)。巨大なトランザクションといくつかの更新後、それがかかる時間についてより多くの予測可能性を提供します。

新しいテーブルを作成し、名前を変更したら同じことを行うことができます。これにより、手順5の必要性が回避されますが、データチャーンがさらに発生し、制約、外部キー、トリガーにより問題が発生する可能性があります。テーブルに関係する可能性のあるものなど。

20
Aaron Bertrand