回答:
なぜこのように機能するのですか?昔、誰かが標準が何を言っているのかを知らず、気にせずに設計を決定したためです(結局、NULL
sにはあらゆる種類の奇妙な振る舞いがあり、さまざまな振る舞いを自由に強制できます)。この決定は、すなわちを口述このケースNULL = NULL
。
それはあまり賢い決断ではありませんでした。どのような彼らが行われているが、ANSI標準へのデフォルトの動作の付着を持っている、と彼らは本当にこの奇妙な行動を望んでいた場合は、同様にDDLオプションを通してそれを許可する必要がありますWITH CONSIDER_NULLS_EQUAL
かWITH ALLOW_ONLY_ONE_NULL
。
もちろん、後知恵は20/20です。
そして、とにかく、最もクリーンで直感的ではない場合でも、回避策があります。
一意のフィルター処理されたインデックスを作成することにより、SQL Server 2008以降で適切なANSI動作を実現できます。
CREATE UNIQUE INDEX foo ON dbo.bar(key) WHERE key IS NOT NULL;
NULL
これらの行は重複チェックから完全に除外されるため、これにより複数の値が許可されます。追加のボーナスとして、これは、複数NULL
のが許可されている場合(特に、インデックス内の唯一の列ではない場合、INCLUDE
列がある場合など)に、テーブル全体で構成されるインデックスよりも小さいインデックスになります。ただし、フィルター選択されたインデックスの他の制限のいくつかに注意する必要があります。
正しい。SQLサーバーでの一意の制約またはインデックスの実装では、NULLを1つだけ許可します。また、これは技術的にはNULLの定義に適合しないことを修正しますが、「技術的に」正しくない場合でも、より有用にするために行ったものの1つです。PRIMARY KEY(一意のインデックス)はNULLを許可しないことに注意してください(もちろん)。
最初に-「Null value」というフレーズの使用をやめます。それはあなたを迷わせるだけです。代わりに、「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の両方を同時にテーブルに入れることはできません。
最も安全で柔軟性の低いアプローチは、コードフィールドでヌルマーカーを許可しないことです。そのため、データに矛盾が生じる可能性はありません。最も柔軟なアプローチは、複数のヌルマーカーを許可し、値が入力されたときに一意性を心配することです。
Sybaseプログラマーは、テーブルで1つのヌルマーカーのみを許可するという、多少安全で柔軟性に欠けるアプローチを採用しました。マイクロソフトはこの振る舞いを継続しており、後方互換性があると思います。
¹私はどこかでCoddが2つのヌルマーカーの実装を検討したことを確信しています。正しく覚えていますか?
PS nullに関する私のお気に入りの引用:Louis Davidson、「Professional SQL Server 2000 Database Design」、Wrox Press、2001、52ページ。「1つの文に要約すると、NULLは悪です。」
null
しても、この目標は達成されません。欠損値は、他の行のいずれかの値と同じになることがあるためです。
CHECK (Value IN ('A','B','C','D'))
か?次に、SQL-Serverの実装とSQL標準の両方で、テーブルに5行(値ごとに1行にNULLを加えた1行)を含めることができます。その後、ほぼ間違いなく、データベースはその制約と一致しますが、デザイナーの意図とは一致しません最大4行のテーブル。1つ以上の行が削除されない限り、制約に違反しないNULLに変更できる値はありません。
CREATE TABLE #T(A INT NULL UNIQUE);INSERT INTO #T VALUES (1),(NULL);UPDATE #T SET A = 1 WHERE A IS NULL;
エラーが発生します。設計の動機に関するあなたの理論によるとNULL
、最初のケースでは挿入を防ぐべきだったはずです-不完全な知識は値が異なるという保証がないことを意味するためです。
これは技術的に正確ではないかもしれませんが、哲学的には私が夜寝るのに役立ちます...
他のいくつかの人が言ったり示唆したように、NULLを不明と考えると、あるNULL値が実際に別のNULL値と等しいかどうかを判断できません。このように考えると、式NULL == NULLは不明、つまりNULLと評価されるはずです。
一意制約には、列の値を比較するための明確な値が必要です。つまり、等価演算子を使用して単一の列の値を他の列の値と比較する場合、有効であるためにはfalseと評価する必要があります。Unknownは偽物として扱われることが多いにもかかわらず、実際には偽ではありません。2つのNULL値は等しいか、または等しくない可能性があります...単に決定的に決定することはできません。
一意の制約は、互いに区別できると判断できる値を制限するものと考えると役立ちます。これが意味することは、次のようなSELECTを実行した場合です。
SELECT * from dbo.table1 WHERE ColumnWithUniqueContraint="some value"
一意の制約がある場合、ほとんどの人は1つの結果を期待します。ColumnWithUniqueConstraintで複数のNULL値を許可した場合、比較値としてNULLを使用してテーブルから単一の個別の行を選択することはできません。
それを考えると、NULLの定義に関して正確に実装されているかどうかに関係なく、複数のNULL値を許可するよりもほとんどの状況で確実にはるかに実用的であると思います。
UNIQUE
制約の主な目的の1つは、レコードの重複を防ぐことです。値が「不明」である複数のレコードが存在するテーブルを作成する必要があるが、2つのレコードが同じ「既知」値を持つことは許可されていない場合、不明な値には、人工的な一意の識別子を割り当てる必要がありますテーブルに追加されました。
UNIQUE
制約があり、単一のヌル値が含まれる列のまれなケースがいくつかあります。たとえば、テーブルに列の値とローカライズされたテキストの説明の間のマッピングが含まれる場合、行NULL
を使用すると、他のテーブルのその列がであるときに表示される説明を定義できますNULL
。の動作NULL
により、その使用例が許可されます。
そうでなければ、UNIQUE
多くの同一のレコードの存在を許可するための列に制約があるデータベースの基礎はありませんが、キー値が区別できない複数のレコードを許可しながらそれを防ぐ方法はありません。NULL
それ自体と等しくないことを宣言しても、NULL
値は互いに区別できません。