15

私はこれまでのキャリアの中で何度かこの問題に直面してきましたが、地元の同業者の誰もそれに答えられないようです。ユーザーがプロセスの途中で停止する場合があることを除いて、候補キーである「説明」フィールドを持つテーブルがあるとします。したがって、おそらく 25% のレコードでこの値は null ですが、NULL 以外のすべてのレコードでは一意である必要があります。

もう 1 つの例は、レコードの複数の「バージョン」を維持する必要があるテーブルであり、ビット値はどれが「アクティブな」バージョンであるかを示します。そのため、「候補キー」は常に読み込まれますが、同一のバージョン (アクティブ ビットが 0) が 3 つ存在し、アクティブなバージョン (アクティブ ビットが 1) が 1 つだけ存在する場合があります。

これらの問題を解決する別の方法があります (最初のケースでは、ストアド プロシージャまたはビジネス レイヤーのいずれかでルール コードを適用し、2 番目のケースでは、アーカイブ テーブルにトリガーを入力し、履歴が必要なときにテーブルを UNION します)。 . 代替手段は必要ありません (明らかにより良い解決策がない限り)。SQL のいずれかのフレーバーがこの方法で「条件付きの一意性」を表現できるかどうか疑問に思っています。私はMS SQLを使用しているので、その中でそれを行う方法があれば、素晴らしい. 私は主に問題に学術的に興味があります。

4

5 に答える 5

36

SQL Server 2008 を使用している場合、インデックス フィルターが解決策になる可能性があります。

http://msdn.microsoft.com/en-us/library/ms188783.aspx

これは、複数の NULL 値を持つ一意のインデックスを強制する方法です

CREATE UNIQUE INDEX [IDX_Blah] ON [tblBlah] ([MyCol]) WHERE [MyCol] IS NOT NULL
于 2010-07-19T17:36:23.770 に答える
3

まだ完成していない説明の場合、完成した説明と同じ表にはありません。ファイナルテーブルには、説明に一意のインデックスまたは主キーがあります。

アクティブ/非アクティブの場合も、「アーカイブ」または「履歴」テーブルの場合と同じように別々のテーブルがある場合がありますが、少なくともMS SQL Serverでそれを行う別の可能な方法は、インデックス付きビューを使用することです。 :

CREATE TABLE Test_Conditionally_Unique
(
    my_id   INT NOT NULL,
    active  BIT NOT NULL DEFAULT 0
)
GO
CREATE VIEW dbo.Test_Conditionally_Unique_View
WITH SCHEMABINDING
AS
    SELECT
        my_id
    FROM
        dbo.Test_Conditionally_Unique
    WHERE
        active = 1
GO
CREATE UNIQUE CLUSTERED INDEX IDX1 ON Test_Conditionally_Unique_View (my_id)
GO

INSERT INTO dbo.Test_Conditionally_Unique (my_id, active)
VALUES (1, 0)
INSERT INTO dbo.Test_Conditionally_Unique (my_id, active)
VALUES (1, 0)
INSERT INTO dbo.Test_Conditionally_Unique (my_id, active)
VALUES (1, 0)
INSERT INTO dbo.Test_Conditionally_Unique (my_id, active)
VALUES (1, 1)
INSERT INTO dbo.Test_Conditionally_Unique (my_id, active)
VALUES (2, 0)
INSERT INTO dbo.Test_Conditionally_Unique (my_id, active)
VALUES (2, 1)
INSERT INTO dbo.Test_Conditionally_Unique (my_id, active)
VALUES (2, 1)    -- This insert will fail

NULL/値の説明にもこれと同じ方法を使用できます。

于 2010-07-19T18:30:53.970 に答える
0

私はあなたの使用目的やテーブルを完全には認識していませんが、1対1の関係を試してみることができます。この「時々」一意の列を新しいテーブルに分割し、新しいテーブルのその列にUNIQUEインデックスを作成し、元のテーブルPKを使用してFKを元のテーブルに戻します。この新しいテーブルには、「一意の」データが存在するはずの場合にのみ行があります。

古いテーブル:

TableA
ID    pk
Col1  sometimes unique
Col...

新しいテーブル:

TableA
ID
Col...

TableB
ID   PK, FK to TableA.ID
Col1 unique index
于 2010-07-19T18:44:04.227 に答える
0

コメントありがとうございます。この回答の最初のバージョンは間違っていました。

SQL Server で null 許容一意制約を効果的に許可する計算列を使用するトリックを次に示します。

create table NullAndUnique 
    (
    id int identity, 
    name varchar(50),
    uniqueName as case 
        when name is null then cast(id as varchar(51)) 
        else name + '_' end,
    unique(uniqueName)
    )

insert into NullAndUnique default values
insert into NullAndUnique default values -- Works
insert into NullAndUnique default values -- not accidentally :)
insert into NullAndUnique (name) values ('Joel')
insert into NullAndUnique (name) values ('Joel') -- Boom!

基本的にが nullのid場合に使用します。は、 name が のように数値である可能性がある場合を回避するためnameのもので、 と衝突する可能性があります。+ '_'1id

于 2010-07-19T17:27:59.000 に答える
-1

オラクルはそうします。完全にヌルのキーは、Oracle のインデックスで Bツリーによってインデックス付けされず、Oracle は Bツリー インデックスを使用して一意の制約を適用します。

ACTIVE_FLAG が 1 に設定されていることに基づいて ID_COLUMN をバージョン管理したいとします。

CREATE UNIQUE INDEX idx_versioning_id ON mytable 
  (CASE active_flag WHEN 0 THEN NULL ELSE active_flag END,
   CASE active_flag WHEN 0 THEN NULL ELSE id_column   END);
于 2010-07-19T18:01:12.473 に答える