列のIdentityプロパティの削除がサポートされていないのはなぜですか


11

SQL Server 2000以降、ID列を "非ID"にする機能が削除されたことを読んだことがあります。そして、これは「設計どおり」でした(欠けている機能だけでなく)。

これが私がブログで見つけた例です。システムテーブルの更新が含まれます。(そしてその機能はSQL Server 2000の後で削除されました。)私はシステムテーブルを介してこれを行うのは良い考えではないことを理解しています。なぜこれを別の方法で実行する機能がないのか疑問に思っています。

これを回避すると、かなりの量の作業が必要になります。(ダウンタイムに耐えられない環境で、数億行を新しいテーブルにコピーする。)

だから、「なんで」と聞いてみました。

SQL Server 2005以降のバージョンで何が変更されて、これは悪いことでしたか?それとも、それは常に悪いことでしたか?

ID列を再び通常の列にすることにより、どの「ベストプラクティス」(または同様の原則)に違反しますか?

-


これを行う理由」の要求に答えるための更新:これは非常に高レベルの要約です:テーブルにパーティションを追加し始めます。(古いデータをアーカイブ/パージできるようにするためです。)それは簡単です。ただし、レコードが別のパーティションに移動して、削除されないようにする必要がある場合があります(パーティションがアーカイブ/削除のために表示される場合)。(行を別のパーティションに移動するためのスペースが常にあるように、パーティション列を2増やしています。)

しかし、パーティション列がID列の場合、値を削除して再挿入する必要があります(ID列の値を更新する方法はありません)。これにより、レプリケーションで問題が発生します。

そのため、ID列の代わりにシーケンスを使用したいと考えています。しかし、その切り替えは、大規模なデータベースでは非常に困難です。

回答:


22

あなたの質問は本質的に:

そもそも、私が本来許されるべきではなかったこの危険なことを、もはやできないのはなぜですか。

その質問への回答はほとんど無関係です(ただし、この機能を要求するこれらのConnectアイテムでいくつかのMicrosoftコメントを確認できます:#294193および#252226)。完全を期すために、私の概要は次のとおりです。IDプロパティを削除する機能は、最初からシステムテーブルを操作する機能を持っていることの意図しない副作用でした。これは、多くの場合に使用されることを意図したものではなく、たいていは非常に悪い結果を伴うため、削除されました。これは、文書化されておらず、サポートされていないシステムテーブルハックでした。システムテーブルのデータを変更する機能は削除されませんでした。MicrosoftがID列である列からハッキングすることを望まなくなったためです。システムテーブルの改ざんは非常に危険であるため、削除されました。IDENTITYプロパティ自体を削除することは、特に対象を絞った機能の削除ではなく、可能であった古代の時代にさえ、このアプローチを完全に信頼することはできなかったでしょう。

とはいえ、代わりにこの質問に答えてみませんか?

最小限のダウンタイムまたはダウンタイムなしで列のIDENTITYプロパティを削除するにはどうすればよいですか?

これは、Connectの回避策#252226ALTER TABLE ... SWITCH自分のPaul Whiteから最初に学んだ技術を使用して、簡単に行うことができます。この単純なテーブルを前提とした簡単な例:

CREATE TABLE dbo.Original
(
  ID INT IDENTITY(1,1) PRIMARY KEY,
  name SYSNAME
);
GO

INSERT dbo.Original(name) VALUES(N'foo'),(N'bar');
GO

SELECT * FROM dbo.Original;
GO

結果:

ID  name
--  ----
1   foo
2   bar

次に、シャドーテーブルを作成して、それに切り替え、古いテーブルを削除し、新しいテーブルの名前を変更して、通常のアクティビティを再開します。

CREATE TABLE dbo.New
(
  ID INT PRIMARY KEY,
  name SYSNAME
);
GO

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
  ALTER TABLE dbo.Original SWITCH TO dbo.New;
  DROP TABLE dbo.Original;
  EXEC sys.sp_rename N'dbo.New', N'Original', 'OBJECT';
COMMIT TRANSACTION;
GO

INSERT dbo.Original(ID,name) VALUES(3,N'splunge');
UPDATE dbo.Original SET ID = 6 WHERE ID = 1;
GO

SELECT * FROM dbo.Original;
GO

結果:

ID  name
--  -------
2   bar
3   splunge
6   foo

次にクリーンアップします。

DROP TABLE dbo.Original;

これはメタデータ操作のみであり、データの移動はありません。メタデータが更新されている間、他のユーザーのみがブロックされます。しかし、確かに、これは非常に単純な例です。外部キーがある場合、またはレプリケーション、変更データキャプチャ、変更追跡などの他の機能を使用している場合は、この変更を行う前にそれらの一部を無効にするか削除する必要がある場合があります(すべての組み合わせをテストしたわけではありません)。特に外部キーについては、すべての(または選択した)外部キー制約を削除して再作成するスクリプトを生成する方法を示すこのヒントを参照してください。

さらに、SQL Serverがこの列にデータを入力しないようにアプリケーションコードを更新し、列の順序または指定する必要がある列に依存する可能性のある挿入または選択ステートメントを確認する必要があります。一般的に、このテーブルについて言及する場合は、コードベース全体をgrepします。

これを処理する別の方法については、Itzik Ben-Ganによるこのスクリプト(出典:この古代の記事)も参照してください。ただし、ここではデータの移動が伴うため、「ダウンタイムなし」または「最小限のダウンタイム」要件は提供されません。


3
私は2人のコネクト項目を賛成するように来ている人を奨励したいと思います。IDブール値をオンまたはオフにするALTER COLUMN機能を実装するのはどれほど難しいでしょうか。回避するには苦痛です。
usr

何らかの理由..SWITCH..で、少なくとも1つのパーティションが定義されているテーブルでしか機能しないと思いました。
RBarryYoung

SWITCHEnterprise Editionは必要ありませんが、テーブルのパーティション分割は必要ですが、追加したいだけです。
Dan Guzman
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.