web-dev-qa-db-ja.com

UNIQUE制約でNULLが1つしか許可されないのはなぜですか?

技術的には、NULL = NULLはFalseです。そのロジックにより、NULLはどのNULLとも等しくなく、すべてのNULLは区別されます。これは、すべてのNULLが一意であり、一意のインデックスが任意の数のNULLを許可する必要があることを意味するのでしょうか?

37
user87166

なぜこのように機能するのですか?昔から、誰かが標準が言っていることを知らなかったり気にしたりせずに設計を決定しました(結局のところ、NULLsにはあらゆる種類の奇妙な動作があり、自由にさまざまな動作を強制できます)。その決定により、thisの場合はNULL = NULLになります。

それはそれほど賢明な決定ではありませんでした。彼らがすべきことは、デフォルトの動作をANSI標準に準拠させることであり、彼らがこの独特の動作を本当に望んでいる場合は、WITH CONSIDER_NULLS_EQUALWITH ALLOW_ONLY_ONE_NULLなどのDDLオプションで許可する必要があります。

もちろん、後視は20/20です。

そして、とりあえず、最もクリーンで直観的ではない場合でも、回避策があります。

一意のフィルター処理されたインデックスを作成することにより、SQL Server 2008以降で適切なANSI動作を得ることができます。

CREATE UNIQUE INDEX foo ON dbo.bar(key) WHERE key IS NOT NULL;

これらの行は重複チェックから完全に除外されているため、これにより複数のNULL値が許可されます。追加のボーナスとして、これは、複数のNULLsが許可されている場合(特に、それがインデックスの唯一の列ではない場合、INCLUDE列など)。ただし、フィルター選択されたインデックスの他のいくつかの制限に注意する必要がある場合があります。

53
Aaron Bertrand

正しい。 SQLサーバーに一意の制約またはインデックスを実装すると、NULLは1つだけ許可されます。また、これは技術的にNULLの定義に適合しないことも修正しましたが、「技術的に」正しくない場合でも、NULLをより有用にするために実行したことの1つです。 PRIMARY KEY(これも一意のインデックス)はNULLを許可しないことに注意してください(もちろん)。

8
Kenneth Fisher

まず、「Null値」という語句の使用をやめると、迷うだけです。代わりに、「nullマーカー」という語句を使用します。この列のマーカーは、この列の実際の値が欠落しているか適用されていないことを示します(ただし、マーカーは、これらのオプションのどちらが実際に当てはまるかを示していないことに注意してください)。

ここで、次のことを想像してみてください(データベースがモデル化された状況の完全な知識を持っていない場合)。

Situation          Database

ID   Code          ID   Code
--   -----         --   -----
1    A             1    A
2    B             2    (null)
3    C             3    C
4    B             4    (null)

私たちがモデル化している整合性ルールは、「コードは一意でなければならない」です。実際の状況はこれに違反しているため、データベースでは、項目2と4の両方を同時にテーブルに入れることはできません。

最も安全で、柔軟性が最も低いアプローチは、Codeフィールドにnullマーカーを許可しないことです。そのため、データに矛盾が生じる可能性はありません。最も柔軟なアプローチは、複数のnullマーカーを許可し、値を入力するときに一意性を考慮することです。

Sybaseプログラマーは、テーブルにnullマーカーを1つだけ許可するという、いくぶん安全で柔軟性のないアプローチを採用しました-それ以来、コメンテーターは不満を述べています。マイクロソフトはこの振る舞いを続けてきたと思いますが、後方互換性のためだと思います。


Coどこかで、Coddが2つのヌルマーカー(1つは不明、もう1つは適用不可)の実装を検討したが、それを拒否したが、参照が見つからないことをどこかで読んだと思います。私は正しく覚えていますか?

追伸nullについての私のお気に入りの引用:Louis Davidson、「Professional SQL Server 2000 Database Design」、Wrox Press、2001年、52ページ。「1つの文に要約:NULLは悪」

3

これは技術的に正確ではないかもしれませんが、哲学的にそれは私が夜に眠るのに役立ちます...

他のいくつかの人が言ったり触れたりしたように、NULLを不明であると考える場合、1つのNULL値が別のNULL値と実際に等しいかどうかを判断できません。このように考えると、式NULL == NULLはNULLに評価され、不明を意味するはずです。

一意制約には、列の値を比較するための明確な値が必要です。つまり、等号演算子を使用して単一の列の値を他の列の値と比較する場合、有効であると評価されなければなりません。 Unknownは、しばしば偽物として扱われますが、実際には偽ではありません。 2つのNULL値が等しいか、等しくない可能性があります...明確に決定することはできません。

一意の制約は、互いに区別できると判断できる値を制限するものと考えると役立ちます。この意味は、次のようなSELECTを実行した場合です。

SELECT * from dbo.table1 WHERE ColumnWithUniqueContraint="some value"

一意の制約があるため、ほとんどの人は1つの結果を期待します。 ColumnWithUniqueConstraintで複数のNULL値を許可した場合、比較値としてNULLを使用してテーブルから単一の個別の行を選択することは不可能です。

それを考えると、NULLの定義に関して正確に実装されているかどうかに関係なく、複数のNULL値を許可するよりも、ほとんどの状況で間違いなくはるかに実用的であると私は信じています。

2
EricJ