NULL列に一意のインデックスを作成するにはどうすればよいですか?


101

SQL Server 2005を使用しています。NULLを許可しながら、列の値を一意になるように制限したいと考えています。

私の現在のソリューションには、次のようなビューの一意のインデックスが含まれます。

CREATE VIEW vw_unq WITH SCHEMABINDING AS
    SELECT Column1
      FROM MyTable
     WHERE Column1 IS NOT NULL

CREATE UNIQUE CLUSTERED INDEX unq_idx ON vw_unq (Column1)

より良いアイデアはありますか?


16
SQL 2008を使用する機会はありませんか?'where'を使用してフィルターされたインデックスを作成できます
Simon_Weaver

3
あなたはユニークという意味ではなく、NULLを許可しました。あなたはユニークを意味しているように見えますが、複数のNULLを含みます。それ以外の場合、NULLは他の値と同様にインデックスが付けられ、一意性制約は期待どおりに機能します。以下のコメントで@pstが言及しているように、SQL標準に従っていないだけです。
Suncat2000

回答:


26

一意の目的に違反しているため、これを実行できないことを確認してください。

ただし、この人には適切な回避策があるようです:http : //sqlservercodebook.blogspot.com/2008/04/multiple-null-values-in-unique-index-in.html


2
あなたが提供されるリンクの内容が実際に(部分的に)ここから帰属せずにコピーされたようだ。decipherinfosys.wordpress.com/2007/11/30/...
トムJuergens

77
「一意の目的に違反している」ことに同意しません-NULLはSQLの特別な値であり(多くの点でNaNと同様)、それに応じて処理する必要があります。ここでは、それは価値がある何のための「正しい実装」の要求のためのリンクです:それは実際には、さまざまなSQLの仕様を称えるためにSQL Serverでの失敗だconnect.microsoft.com/SQLServer/feedback/details/299229/...

5
2008年の参照用に、CREATE UNIQUE INDEX foo ON dbo.bar(key)WHERE key IS NOT NULLを実行できます。
niico

2
「一意の目的に違反する」にも同意しません。NULLはNULLと等しくないため、null許容列に一意のインデックスを作成し、複数のnullを挿入できるはずです。
Wodzu

105

SQL Server 2008を使用して、フィルターされたインデックスを作成できます:http : //msdn.microsoft.com/en-us/library/cc280372.aspx。(Simonがコメントとしてこれを追加したようですが、コメントが見落とされやすいので、独自の回答に値すると思いました。)

別のオプションは、一意性をチェックするトリガーですが、これはパフォーマンスに影響を与える可能性があります。


84
create unique index UIX on MyTable (Column1) where Column1 is not null
ジョーンSchouの-Rodeの

1
注:現在、SQL Server Management Studioのは、後でそれが混乱よテーブルを変更し、それを再作成することを忘れないでください、それをドロップしようとした場合ので、このようなインデックスを作成する方法を知っていないようだ
Simon_Weaver

3
マイクロソフトはこれをサポートするためにSSMSを更新したようです。SSMS 10.50.1617を使用しています。[インデックスのプロパティ]ダイアログで、[フィルター]ページを選択してフィルターを編集できます。例:「([Column1] IS NOT NULL)」
Phil Haselden、2011

5
インデックスで複数のnullを許可することと、インデックスからnullをフィルタリングすることは別のものです。インデックスをフィルタリングすると、実際にはインデックスからレコードが除外されますが、他のソリューションではnullが有用な一意の値に変換されます。違いに注意してください。
Suncat2000、2012

そのようなフィルターされたインデックスを持つテーブルでストアドプロシージャを使用している場合ANSI_NULLSON、それがであることを確認してください。そうでない場合、データを挿入しようとするとエラーが発生します。
Arne

71

計算された列トリックは、「ヌルバスター」として広く知られています。私のメモはスティーブ・カスのクレジットです:

CREATE TABLE dupNulls (
pk int identity(1,1) primary key,
X  int NULL,
nullbuster as (case when X is null then pk else 0 end),
CONSTRAINT dupNulls_uqX UNIQUE (X,nullbuster)
)

これはクールなトリックのように見えます。奇妙なことに、nullbusterを検索しても、それほど多くのものは表示されません。PKを使用してインデックスでさらに作業することができる場合、これが検索の高速化にも役立つのではないかと思っています。今週末、大きなテーブルでテストして見ます。
David Storfer、2011年

@ DavidStorfer、2つの異なるテーブルのIDが衝突する可能性があるため、これを行うことはできません。
user393274 2012

改善:ISNULL(X、CONVERT(VARCHAR(10)、pk))
Faiz

5
@Faiz:改善は見る人の目にあります。私はオリジナルの外観を好む。
14

@NunoG、これは、消える可能性のある外部サイトをリンクするだけでなく、要件に準拠した優れたソリューションを提供するため、受け入れられる答えになるはずです。
フレデリック

-3

厳密に言えば、同じ値(これにはNULLを含む)が2回以上あると一意の制約に明らかに違反するため、一意のnull許容列(または列のセット)はNULL(またはNULLのレコード)に1回しかなりません。

ただし、これは「一意のnull許容列」の概念が有効であることを意味するものではありません。これをリレーショナルデータベースに実際に実装するには、この種類のデータベースは正常に機能するように正規化されていることを念頭に置く必要があります。通常、正規化には、エンティティ間の関係を確立するためのいくつかの(非エンティティ)追加のテーブルの追加が含まれます。 。

「一意のnull許容列」を1つだけ考慮して基本的な例を考えてみましょう。このような列に簡単に拡張できます。

次のような表で表される情報があるとします。

create table the_entity_incorrect
(
  id integer,
  uniqnull integer null, /* we want this to be "unique and nullable" */
  primary key (id)
);

これを行うには、uniqnullを離して2番目のテーブルを追加し、uniqnull値とthe_entityの間の関係を確立します(uniqnullをthe_entityの「内部」にするのではなく)。

create table the_entity
(
  id integer,
  primary key(id)
);

create table the_relation
(
  the_entity_id integer not null,
  uniqnull integer not null,

  unique(the_entity_id),
  unique(uniqnull),
  /* primary key can be both or either of the_entity_id or uniqnull */
  primary key (the_entity_id, uniqnull), 
  foreign key (the_entity_id) references the_entity(id)
);

uniqnullの値をthe_entityの行に関連付けるには、the_relationにも行を追加する必要があります。

the_entityの行に関連付けられているuniqnull値がない場合(つまり、the_entity_incorrectにNULLを配置する場合)、the_relationに行を追加しません。

uniqnullの値はすべてのthe_relationで一意であることに注意してください。また、the_relationの主キーと外部キーがこれを強制するため、the_relationには最大で1つの値しか存在できないことに注意してください。

次に、uniqnullの値5をthe_entity ID 3に関連付ける場合は、次のようにする必要があります。

start transaction;
insert into the_entity (id) values (3); 
insert into the_relation (the_entity_id, uniqnull) values (3, 5);
commit;

また、the_entityのid値10に対応するuniqnullがない場合は、次のようにします。

start transaction;
insert into the_entity (id) values (10); 
commit;

この情報を非正規化し、the_entity_incorrectのようなテーブルが保持するデータを取得するには、次のことを行う必要があります。

select
  id, uniqnull
from
  the_entity left outer join the_relation
on
  the_entity.id = the_relation.the_entity_id
;

「左外部結合」演算子は、一致する列がthe_relationに存在しない場合、the_entityからのすべての行が結果に表示されることを保証し、uniqnull列にNULLを入れます。

十分に正規化されたデータベース(および対応する非正規化のビューと手順)の設計に数日(または数週間または数か月)費やす努力により、数年(または数十年)の苦痛と無駄なリソースを節約できます。


6
承認された回答のコメントに50の賛成票ですでに述べたように、一意としてインデックス付けされた列に複数のnullがあることは、MS SQLサーバーによってサポートされる必要があります。これを許可しないSQL標準を実装するのは失敗です。nullは値ではありません。nullはnullと等しくありません。これは、長年の基本的なSQLルールです。だからあなたの最初の文は間違っており、ほとんどの読者は読んでわざわざしないでしょう。
フレデリック
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.