sql >> データベース >  >> RDS >> Database

IDENTITY列を広げることによる影響の最小化–パート2

    [パート1|パート2|パート3|パート4]

    このシリーズの最初のパートでは、IDENTITY列をintからbigintに変更すると物理ページがどうなるかを示しました。物事を単純にするために、インデックスや制約のない非常に単純なヒープを作成しました。残念ながら、私たちのほとんどはそのような贅沢を持っていません。変更する必要があるが、最初から簡単に再作成できない重要なテーブルには、複数の属性が直接邪魔になっている可能性があります。この投稿では、インメモリOLTPや列ストアなどのエキゾチックなものに立ち入ることなく、より一般的なものを紹介したいと思いました。

    主キー

    うまくいけば、すべてのテーブルに主キーがあります。ただし、IDENTITY列が含まれている場合、基になるデータ型を変更するのはそれほど簡単ではありません。クラスター化された主キーとクラスター化されていない主キーの両方の簡単な例を見てみましょう。

    CREATE TABLE dbo.Test1
    (
      ID INT IDENTITY(1,1),
      CONSTRAINT PK_1 PRIMARY KEY NONCLUSTERED (ID)
    );
     
    CREATE TABLE dbo.Test2
    (
      ID INT IDENTITY(1,1),
      CONSTRAINT PK_2 PRIMARY KEY CLUSTERED (ID)
    );

    列を変更しようとすると:

    ALTER TABLE dbo.Test1 ALTER COLUMN ID BIGINT;
    GO
    ALTER TABLE dbo.Test2 ALTER COLUMN ID BIGINT;

    ALTERごとにエラーメッセージのペアが表示されます(最初のペアのみが表示されます):

    メッセージ5074、レベル16、状態1
    オブジェクト「PK_1」は列「ID」に依存しています。
    メッセージ4922、レベル16、状態9
    ALTER TABLE ALTERCOLUMNIDが1つまたはより多くのオブジェクトがこの列にアクセスします。

    概要:主キーを削除する必要があります 、クラスター化されているかどうか。

    インデックス

    まず、上記のようないくつかのテーブルを取り上げ、主キーの代わりに一意のインデックスを使用します。

    CREATE TABLE dbo.Test3
    (
      ID INT IDENTITY(1,1),
      INDEX IX_3 UNIQUE NONCLUSTERED (ID)
    );
     
    CREATE TABLE dbo.Test4
    (
      ID INT IDENTITY(1,1),
      INDEX IX_4 UNIQUE CLUSTERED (ID) 
    );

    上記と同様のALTERコマンドを実行すると、同じエラーメッセージが表示されます。これは、インデックスを無効にしても当てはまります:

    ALTER INDEX IX_3 ON dbo.Test3 DISABLE;
    GO
    ALTER INDEX IX_4 ON dbo.Test4 DISABLE;

    含まれる列やフィルターなど、他のさまざまなタイプのインデックスの組み合わせについても同様の結果が得られます。

    CREATE TABLE dbo.Test5
    (
      ID INT IDENTITY(1,1),
      x CHAR(1)
    );
    CREATE INDEX IX_5 ON dbo.Test5(x) INCLUDE(ID);
     
    CREATE TABLE dbo.Test6
    (
      ID INT IDENTITY(1,1),
      x CHAR(1)
    );
    CREATE INDEX IX_6 ON dbo.Test6(x) WHERE ID > 0;

    概要:インデックスを削除して再作成する必要があります 、クラスター化されているかどうかに関係なく、キーまたはINCLUDEのIDENTITY列を参照します。 IDENTITY列がクラスター化インデックスの一部である場合、これはすべてのインデックスを意味します 、それらはすべて定義によりクラスタリングキーを参照するためです。そして、それらを無効にするだけでは十分ではありません。

    計算列

    これは比較的まれなはずですが、IDENTITY列に基づいて計算された列を見てきました。例:

    CREATE TABLE dbo.Test7
    (
      ID INT IDENTITY(1,1),
      NextID AS (ID + 1)
    );

    今回、変更しようとすると、同じペアのエラーが発生しますが、テキストがわずかに異なります:

    メッセージ5074、レベル16、状態1
    列'NextID'は列'ID'に依存しています。
    メッセージ4922、レベル16、状態9
    ALTER TABLE ALTERCOLUMNIDが1つまたはより多くのオブジェクトがこの列にアクセスします。

    これは、計算列の定義をターゲットのデータ型に一致するように変更した場合にも当てはまります。

    CREATE TABLE dbo.Test8
    (
      ID INT IDENTITY(1,1),
      NextID AS (CONVERT(BIGINT, ID) + 1)
    );

    概要:計算列の定義を変更するか、完全に削除する必要があります。

    インデックス付きビュー

    インデックス付きのビューでも、使用量のかなりの部分が見られます。 IDENTITY列を参照しないインデックス付きビューを作成しましょう(ベーステーブルに他のインデックスや制約がないことに注意してください):

    CREATE TABLE dbo.Test9
    (
      ID INT IDENTITY(1,1),
      x CHAR(1)
    );
    GO
     
    CREATE VIEW dbo.vTest9A
    WITH SCHEMABINDING
    AS
      SELECT x, c = COUNT_BIG(*)
        FROM dbo.Test9
        GROUP BY x;
    GO
     
    CREATE UNIQUE CLUSTERED INDEX IX_9A ON dbo.vTest9A(x);
    に作成します。

    もう一度、ALTERを試してみますが、今回は成功します 。 SCHEMABINDINGは基になるテーブルへの変更を防ぐことになっているので、これに驚いたことを告白しますが、この場合、ビューで明示的に参照されている列にのみ適用されます。少し異なるビューを作成する場合:

    CREATE VIEW dbo.vTest9B
    WITH SCHEMABINDING
    AS
      SELECT ID, c = COUNT_BIG(*)
        FROM dbo.Test9
        GROUP BY ID;
    GO
    CREATE UNIQUE CLUSTERED INDEX IX_9B ON dbo.vTest9B(ID);

    列の依存関係が原因で失敗します:

    メッセージ5074、レベル16、状態1
    オブジェクト'vTest9B'は列'ID'に依存しています。
    メッセージ4922、レベル16、状態9
    ALTER TABLE ALTERCOLUMNIDが1つまたはより多くのオブジェクトがこの列にアクセスします。

    概要:IDENTITY列を明示的に参照するビューのすべてのインデックスを削除する必要があります 、およびすべてのインデックス クラスタ化されたインデックスのIDENTITY列を参照するビュー。

    インバウンド外部キー

    おそらく、IDENTITY主キーの最も問題のある側面は、サロゲートの性質上、複数の関連するテーブルでこのサロゲートキーを使用することがポイント全体である場合が多いことです。今、私は参照整合性を回避することを提唱しようとはしていませんが、ここでも少し邪魔になる可能性があります。上記から、主キーまたは一意性制約の一部である列を変更することはできないことがわかっています。別のテーブルが外部キー制約でここを指すには、これら2つのいずれかが存在する必要があります。したがって、次の2つのテーブルがあるとします。

    CREATE TABLE dbo.TestParent
    (
      ID INT IDENTITY(1,1),
      CONSTRAINT PK_Parent PRIMARY KEY CLUSTERED(ID)
    );
    GO
     
    CREATE TABLE dbo.TestChild
    (
      ParentID INT NOT NULL,
      CONSTRAINT FK_Parent FOREIGN KEY(ParentID) REFERENCES dbo.TestParent(ID)
    );

    列のデータ型の変更を検討する前に、制約を削除する必要があります:

    ALTER TABLE dbo.TestParent DROP CONSTRAINT PK_Parent;

    そしてもちろん、外部キー制約を削除せずにそれを行うことはできません。これにより、次のエラーメッセージが生成されるためです。

    メッセージ3725、レベル16、状態0
    制約「PK_Parent」はテーブル「TestChild」、外部キー制約「FK_Parent」によって参照されています。
    メッセージ3727、レベル16、状態0
    可能性があります制約を削除しません。以前のエラーを参照してください。

    このエラーは、最初に外部キー制約を無効にしても残ります:

    ALTER TABLE dbo.TestChild NOCHECK CONSTRAINT FK_Parent;

    さらに、データ型を変更するために参照列も必要になることを考慮してください。さらに、これらの列は、子テーブルの変更を同様に妨げる可能性のある上記の要素の一部に関与している可能性があります。物事を完全に調和させて同期させるには、次のことを行う必要があります。

    • 関連する制約とインデックスを親テーブルにドロップします
    • 関連する外部キー制約を子テーブルにドロップします
    • FK列を参照する(および関連する計算列/インデックス付きビューを処理する)子テーブルにインデックスをドロップします
    • 親テーブルとすべての子テーブルのデータ型を変更します
    • すべてを再作成する

    概要:着信外部キーを削除する必要があります そして、潜在的に、これはカスケード効果の全体を持っているでしょう。外部キーを無効にするだけでは不十分であり、最終的には子テーブルでもデータ型を変更する必要があるため、永続的な解決策にはなりません。

    結論

    私たちはゆっくりと動いているように見えることを知っています。この投稿では、私は解決策に向かってではなく、解決策から離れているように見えることを認めます。私はそこに着きます。この種の変更を困難にするものを含め、最初に提示する情報はたくさんあります。上記の要約から抜粋して、次のことを行う必要があります。

    • メインテーブルに関連するインデックスを削除して再作成します
    • IDENTITY列を含む計算列を変更または削除する
    • IDENTITY列を参照するインデックス付きビューにインデックスをドロップします
    • IDENTITY列を指すインバウンド外部キーを処理する

    残念ながら、これらの多くはキャッチ22です。インデックスは列に依存しているため、列を変更することはできません。また、列が変更されるまでインデックスを変更することはできません。 ALTERINDEXがREBUILD WITH (ONLINE = ON, CHANGE_COLUMN (COLUMN = ID, NEW_TYPE = BIGINT))をサポートしていれば素晴らしいと思いませんか ?そして、CASCADE_CHANGE_TO_REFERENCING_KEYS,COLUMNS,INDEXES,VIEWS,ETC ?まあ、そうではありません(私はチェックしました)。したがって、これらを簡単にする方法を見つける必要があります。パート3にご期待ください。

    [パート1|パート2|パート3|パート4]


    1. INNERJOINでCROSSAPPLYを使用する必要があるのはいつですか?

    2. SQLite GLOB

    3. Oracle文字列関数(全リスト)

    4. 私のDBAは病気です-SysAdminsのためのデータベースフェイルオーバーのヒント