ALTER TABLE ... ALTER COLUMN
コマンドは非常に強力です。これを使用して、列のデータ型、長さ、精度、スケール、null可能性、照合など、その他多くのことを変更できます。
確かに、他の方法よりも便利です。新しいテーブルを作成し、変更が必要になるたびにデータを移行します。それにもかかわらず、根底にある複雑さを隠すためにできることはたくさんあります。このコマンドで何が可能であるかについての多数の制限に加えて、常にパフォーマンスの問題があります。
最終的に、テーブルは一連のバイトとして格納され、システム内の他の場所にメタデータが含まれて、それらの各バイトの意味と、テーブルのさまざまな列のそれぞれとの関係を説明します。 SQL Serverに列の定義の一部を変更するように依頼する場合、既存のデータが新しい定義と互換性があることを確認する必要があります。また、現在の物理レイアウトを変更する必要があるかどうかを判断する必要があります。
変更の種類とデータベースの構成に応じて、ALTER COLUMN
コマンドは、次のいずれかのアクションを実行する必要があります。
- システムテーブルのメタデータのみを変更します。
- 既存のすべてのデータの互換性を確認してから、メタデータを変更します。
- 保存されているデータの一部またはすべてを新しい定義に一致するように書き直します。
オプション1は、パフォーマンスの観点から理想的なケースを表しています。システムテーブルへのわずかな変更と最小限のロギングが必要です。操作には、制限的なスキーマ変更が必要ですSch-M
ロックしますが、メタデータの変更自体は、テーブルのサイズに関係なく、非常に迅速に完了します。
注意すべき特殊なケースがいくつかありますが、一般的な要約として、次のアクションではメタデータの変更のみが必要です。
-
NOT NULL
からの移行NULL
へ 同じデータ型の場合。 -
varchar
の最大サイズを大きくする 、nvarchar
、またはvarbinary
列(max
を除く 。
SQLServer2016の改善
この投稿の主題は、メタデータに対してのみ有効になる追加の変更です- SQLServer2016以降 。構文を変更したり、構成設定を変更したりする必要はありません。これらの文書化されていない改善を無料で入手できます。
新しい機能は、固定長のサブセットを対象としています データ型。新しい機能は、次の状況で行ストアテーブルに適用されます。
- 圧縮を有効にする必要があります:
- すべてのインデックスとパーティション 、ベースヒープまたはクラスター化インデックスを含みます。
-
ROW
のいずれか またはPAGE
圧縮。 - インデックスとパーティションは混合物を使用する場合があります これらの圧縮レベルの。重要なのは、非圧縮のインデックスやパーティションがないことです。
-
NULL
からの変更NOT NULL
へ 許可されていません 。 - 次の整数型の変更 サポートされています:
-
smallint
integer
またはbigint
。 integer
bigint
へ 。-
smallmoney
money
へ (内部的に整数表現を使用します)。
-
- 次の文字列とバイナリタイプの変更 サポートされています:
-
char(n)
char(m)
へ またはvarchar(m)
-
nchar(n)
nchar(m)
へ またはnvarchar(m)
-
binary(n)
binary(m)
へ またはvarbinary(m)
- 上記のすべては
n < m
の場合のみ およびm != max
- 照合の変更は許可されていません
-
Column Descriptor の場合、基になるバイナリデータレイアウトは変更されないため、これらの変更はメタデータのみになります。 行形式が使用されます(したがって、圧縮が必要です)。圧縮しない場合、行ストアは元の FixedVarを使用します 表現。物理的なレイアウトを書き直さないと、これらの固定長のデータ型の変更に対応できません。
tinyint
に気付くかもしれません 整数型リストから省略されています。これは、他の整数型はすべて符号付きであるのに対し、符号なしであるため、メタデータのみの変更は不可能であるためです。たとえば、tinyint
の場合、255の値を1バイトに収めることができます。 、ただし、署名された形式のいずれかで2バイトが必要です。署名された形式は、圧縮時に1バイトで-128〜+127を保持できます。
この改善の非常に便利なアプリケーションの1つは、IDENTITY
を使用して列のデータ型を変更することです。 プロパティ。
行圧縮を使用する次のヒープテーブルがあるとします(ページ圧縮も機能します):
DROP TABLE IF EXISTS dbo.Test; GO CREATE TABLE dbo.Test ( id integer IDENTITY NOT NULL, some_value integer NOT NULL ) WITH (DATA_COMPRESSION = ROW);
500万行のデータを追加しましょう。これは、列のデータ型の変更がメタデータのみの操作であるかどうかを(パフォーマンスの観点から)明らかにするのに十分です。
WITH Numbers AS ( SELECT n = ROW_NUMBER() OVER (ORDER BY @@SPID) FROM sys.all_columns AS AC1 CROSS JOIN sys.all_columns AS AC2 ORDER BY n OFFSET 0 ROWS FETCH FIRST 5 * 1000 * 1000 ROWS ONLY ) INSERT dbo.Test WITH (TABLOCKX) ( some_value ) SELECT N.n FROM Numbers AS N;
次に、IDENTITY
を再シードします integer
に収まる値がほぼ不足しているように見せるため :
DBCC CHECKIDENT ( N'dbo.Test', RESEED, 2147483646 );
もう1行正常に追加できます:
INSERT dbo.Test (some_value) VALUES (123456);
しかし、別の行を追加しようとしています:
INSERT dbo.Test (some_value) VALUES (7890);
エラーメッセージが表示されます:
メッセージ8115、レベル16、状態1、行1IDENTITYをデータ型intに変換する算術オーバーフローエラー。
列をbigint
に変換することで、これを修正できます。 :
ALTER TABLE dbo.Test ALTER COLUMN id bigint NOT NULL;
SQL Server 2016の改善により、このコマンドはメタデータのみを変更します 、およびすぐに完了します。前のINSERT
ステートメント(算術オーバーフローエラーをスローしたステートメント)が正常に完了するようになりました。
この新しい機能は、IDENTITY
で列のタイプを変更する際のすべての問題を解決するわけではありません。 財産。それでも、列のインデックスを削除して再作成したり、参照する外部キーを再作成したりする必要があります。これは、この投稿の範囲外です(ただし、Aaron Bertrandは以前にそれについて書いています)。メタデータのみの操作としてタイプを変更できることは、確かに害にはなりません。慎重に計画することで、必要な他の手順を可能な限り効率的に行うことができます。たとえば、最小限のログまたはONLINE
を使用します。 操作。
必ず常に NULL
を指定します またはNOT NULL
ALTER COLUMN
でデータ型を変更する場合 。たとえば、some_value
のデータ型も変更したいとします。 integer NOT NULL
のテストテーブルの列 bigint NOT NULL
へ 。
コマンドを作成するときは、NULL
を省略します またはNOT NULL
修飾子:
ALTER TABLE dbo.Test ALTER COLUMN some_value bigint;
このコマンドは、メタデータのみの変更として正常に完了しますが、NOT NULL
も削除します 制約。列はbigint NULL
になりました 、これは私たちが意図したものではありません。この動作は文書化されていますが、見落としがちです。
間違いを修正しようとするかもしれません:
ALTER TABLE dbo.Test ALTER COLUMN some_value bigint NOT NULL;
これはありません メタデータのみの変更。 NULL
からの変更は許可されていません NOT NULL
へ (条件の復習が必要な場合は、前の表に戻って参照してください)。 SQL Serverは、既存のすべての値をチェックして、nullが存在しないことを確認する必要があります。次に、物理的にすべての行を書き換えます テーブルの。これらのアクションは、それ自体が遅いだけでなく、大量のトランザクションログを生成し、ノックオン効果をもたらす可能性があります。
ちなみに、IDENTITY
の列では、これと同じ間違いは起こり得ません。 財産。 ALTER COLUMN
を記述した場合 NULL
のないステートメント またはNOT NULL
その場合、エンジンは、NOT NULL
を意味していると想定します。 これは、IDプロパティがNULL可能列では許可されていないためです。この振る舞いに頼らないことは、今でも素晴らしい考えです。
常にNULL
を指定してください またはNOT NULL
ALTER COLUMN
を使用 。
データベースのデフォルトと一致しない照合を持つ文字列列を変更する場合は、特に注意が必要です。
たとえば、大文字と小文字が区別され、アクセントに依存する照合を含むテーブルがあるとします(データベースのデフォルトが異なると仮定します):
DROP TABLE IF EXISTS dbo.Test2; GO CREATE TABLE dbo.Test2 ( id integer IDENTITY NOT NULL, some_string char(8) COLLATE Latin1_General_100_CS_AS NOT NULL ) WITH (DATA_COMPRESSION = ROW);
500万行のデータを追加します:
WITH Numbers AS ( SELECT n = ROW_NUMBER() OVER (ORDER BY @@SPID) FROM sys.all_columns AS AC1 CROSS JOIN sys.all_columns AS AC2 ORDER BY n OFFSET 0 ROWS FETCH FIRST 5 * 1000 * 1000 ROWS ONLY ) INSERT dbo.Test2 WITH (TABLOCKX) ( some_string ) SELECT CONVERT(char(8), N.n) COLLATE Latin1_General_100_CS_AS FROM Numbers AS N;
次のコマンドを使用して、文字列列の長さを2倍にします。
ALTER TABLE dbo.Test2 ALTER COLUMN some_string char(16) NOT NULL;
NOT NULL
を指定することを忘れないでください 、しかしデフォルト以外の照合を忘れました。 SQL Serverは、照合をデータベースのデフォルト(Latin1_General_CI_AS
)に変更することを意図していると想定しています。 私のテストデータベース用)。照合を変更すると、操作がメタデータのみになるのを防ぐため、操作は数分間実行され、大量のログが生成されます。
前のスクリプトを使用してテーブルとデータを再作成してから、ALTER COLUMN
を試してください もう一度コマンドを実行しますが、コマンドの一部として既存のデフォルト以外の照合を指定します:
ALTER TABLE dbo.Test2 ALTER COLUMN some_string char(16) COLLATE Latin1_General_100_CS_AS NOT NULL;
これで、メタデータのみの操作として、変更がすぐに完了します。 NULL
と同様 およびNOT NULL
構文では、事故を避けるために明示的にすることが重要です。これは、ALTER COLUMN
だけでなく、一般的に良いアドバイスです。 。
圧縮は、インデックスごとに明示的に指定する必要があり、ヒープの場合はベーステーブルに対して個別に指定する必要があることに注意してください。これは、省略された構文またはショートカットを使用すると、望ましい結果が得られない可能性がある別の例です。
たとえば、次の表では、主キーまたはインラインインデックス定義のいずれにも明示的な圧縮を指定していません。
CREATE TABLE dbo.Test ( id integer IDENTITY NOT NULL PRIMARY KEY, some_value integer NOT NULL INDEX [IX dbo.Test some_value] ) WITH (DATA_COMPRESSION = PAGE);
PRIMARY KEY
名前が割り当てられます。デフォルトはCLUSTERED
です。 、PAGE
になります 圧縮されています。インラインインデックスはNONCLUSTERED
になります まったく圧縮されていません。すべてのインデックスとパーティションが圧縮されているわけではないため、このテーブルは新しい最適化では有効になりません。
はるかに優れた、より明示的なテーブル定義は次のようになります。
CREATE TABLE dbo.Test ( id integer IDENTITY NOT NULL CONSTRAINT [PK dbo.Test id] PRIMARY KEY CLUSTERED WITH (DATA_COMPRESSION = PAGE), some_value integer NOT NULL INDEX [IX dbo.Test some_value] NONCLUSTERED WITH (DATA_COMPRESSION = ROW) );
すべてのインデックスとパーティションが圧縮されているため、このテーブルは新しい最適化の対象になります。前述のように、圧縮タイプの混合は問題ありません。
このCREATE TABLE
を作成するにはさまざまな方法があります 明示的な方法でステートメントを作成するため、個人的な好みの要素があります。重要なポイントは、常に明確にすることです。 あなたが欲しいものについて。これは、個別のCREATE INDEX
に適用されます ステートメントも。
新しいメタデータ専用の拡張イベントがあります-ALTER COLUMN
のみ SQLServer2016以降でサポートされる操作。
拡張イベントはcompressed_alter_column_is_md_only
です デバッグで チャネル。そのイベントフィールドはobject_id
です 、column_id
、およびis_md_only
(true / false)。
このイベントは、SQL Server 2016の新機能により、操作がメタデータのみであるかどうかのみを示します。2016より前にメタデータのみであった列の変更には、is_md_only = false
が表示されます。 まだメタデータのみであるにもかかわらず。
ALTER COLUMN
の追跡に役立つその他の拡張イベント 操作にはmetadata_ddl_alter_column
が含まれます およびalter_column_event
、両方とも Analytic チャネル。
無効にする必要がある場合 何らかの理由で新しいSQLServer2016機能を使用すると、文書化されていないグローバル(または起動)トレースフラグ3618を使用できます。このトレースフラグは、セッションレベルで使用する場合は効果的ではありません。 ALTER COLUMN
でクエリレベルのトレースフラグを指定する方法はありません。 コマンド。
メタデータのみの変更で一部の固定長整数データ型を変更できることは、非常に歓迎される製品の改善です。テーブルがすでに完全に圧縮されている必要がありますが、とにかくそれはより一般的なものになりつつあります。これは、SQL Server 2016 ServicePack1以降のすべてのエディションで圧縮が有効になっているため特に当てはまります。
固定長の文字列型の列は、おそらくそれほど一般的ではありません。これの一部は、スペース使用量などのやや時代遅れの考慮事項が原因である可能性があります。圧縮された場合、固定長の文字列列は末尾の空白を格納しないため、格納の観点からは可変長の文字列列と同じくらい効率的です。操作や表示のためにスペースをトリミングするのは面倒な場合がありますが、データが通常最大長の大部分を占める場合、固定長タイプには、特に並べ替えやハッシュなどのメモリ付与に関して、重要な利点があります。
圧縮が有効になっていることは、すべて良いニュースではありません。 SQL Serverは、既存のすべての値が新しいタイプに正常に変換されることを確認した後、メタデータのみの変更を実行できる場合があることを前述しました。これは、ALTER COLUMN
を使用する場合です。 integer
から変更するには smallint
へ 例えば。残念ながら、これらの操作は現在、圧縮されたオブジェクトのメタデータのみではありません。
Panagiotis Antonopoulosに感謝します (プリンシパルソフトウェアエンジニア)および Mirek Sztajno (シニアプログラムマネージャー)SQL Server製品チームから、この記事の調査と執筆中の支援とガイダンスに感謝します。
この作業で提供される詳細は、Microsoftの公式ドキュメントまたは製品ステートメントと見なされるべきではありません。