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


36

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


コメントは詳細なディスカッション用ではありません。この会話はチャットに移動さました
ポールホワイトはGoFundMonicaを言う

回答:


52

なぜこのように機能するのですか?昔、誰かが標準が何を言っているのかを知らず、気にせずに設計を決定したためです(結局、NULLsにはあらゆる種類の奇妙な振る舞いがあり、さまざまな振る舞いを自由に強制できます)。この決定は、すなわちを口述このケースNULL = NULL

それはあまり賢い決断ではありませんでした。どのような彼らが行われているが、ANSI標準へのデフォルトの動作の付着を持っている、と彼らは本当にこの奇妙な行動を望んでいた場合は、同様にDDLオプションを通してそれを許可する必要がありますWITH CONSIDER_NULLS_EQUALWITH 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列がある場合など)に、テーブル全体で構成されるインデックスよりも小さいインデックスになります。ただし、フィルター選択されたインデックスの他の制限のいくつかに注意する必要があります。


8

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


1
この(SQLサーバーの)技術も、SQL標準に適合しません。この問題に関する7年前のConnect項目があります
ypercubeᵀᴹ

@ypercubeはい。そのため、これは単なる実装であり、NULLの定義には実際には適合しないと述べました。(私は他のもののためにそれを使用しましたが。)私を濾過一意のインデックスについては考えていなかった
ケネス・フィッシャー

3

最初に-「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は悪です。」


1
シングルを許可nullしても、この目標は達成されません。欠損値は、他の行のいずれかの値と同じになることがあるためです。
マーティンスミス14年

1
@MartinSmithが言ったこと。チェック制約がある場合はどうなりますCHECK (Value IN ('A','B','C','D'))か?次に、SQL-Serverの実装とSQL標準の両方で、テーブルに5行(値ごとに1行にNULLを加えた1行)を含めることができます。その後、ほぼ間違いなく、データベースはその制約と一致しますが、デザイナーの意図とは一致しません最大4行のテーブル。1つ以上の行が削除されない限り、制約に違反しないNULLに変更できる値はありません。
ypercubeᵀᴹ

1
標準では、5行ではなく106行でさえ6行を許可するという事実は、このシナリオで両方とも何らかの方法で失敗するということには変わりません。
ypercubeᵀᴹ

@Martin Smith、それはそうかもしれないが、それでもそうではないかもしれない-データベースサーバーはそれを知ることができないので、それを危険にさらすことなく、安全なルートを取る。それは、Sybase(私が推測する)プログラマーが決定したことであり、それ以来ずっとイライラを引き起こしました(少なくとも、私の本棚で最も古い本であるRon Soukupが、Aaron Bertrandが答えで行ったのとほぼ同じコメントをするInside SQL Server 6.5まで遡ります) 。さらに悪いことかもしれない-nullマーカーを強制しなかったかもしれない。:-)
グリーンストーンウォーカー14年

2
@GreenstoneWalker-「安全な」ルートを取りません。欠損値が競合しないことを前提としています。CREATE TABLE #T(A INT NULL UNIQUE);INSERT INTO #T VALUES (1),(NULL);UPDATE #T SET A = 1 WHERE A IS NULL;エラーが発生します。設計の動機に関するあなたの理論によるとNULL、最初のケースでは挿入を防ぐべきだったはずです-不完全な知識は値が異なるという保証がないことを意味するためです。
マーティンスミス14年

2

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

他のいくつかの人が言ったり示唆したように、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値を許可するよりもほとんどの状況で確実にはるかに実用的であると思います。


(SQL-Serverだけでなく、どの実装でも)一意の制約がある場合、Selectは1つの結果を返します。あなたの論点は何ですか?
ypercubeᵀᴹ

-3

UNIQUE制約の主な目的の1つは、レコードの重複を防ぐことです。値が「不明」である複数のレコードが存在するテーブルを作成する必要があるが、2つのレコードが同じ「既知」値を持つことは許可されていない場合、不明な値には、人工的な一意の識別子を割り当てる必要がありますテーブルに追加されました。

UNIQUE制約があり、単一のヌル値が含まれる列のまれなケースがいくつかあります。たとえば、テーブルに列の値とローカライズされたテキストの説明の間のマッピングが含まれる場合、行NULLを使用すると、他のテーブルのその列がであるときに表示される説明を定義できますNULL。の動作NULLにより、その使用例が許可されます。

そうでなければ、UNIQUE多くの同一のレコードの存在を許可するための列に制約があるデータベースの基礎はありませんが、キー値が区別できない複数のレコードを許可しながらそれを防ぐ方法はありません。NULLそれ自体と等しくないことを宣言しても、NULL値は互いに区別できません。


3
人工の一意の識別子は冗談です、ごめんなさい。どのようにVINのためにそれをするつもりですか?あなたはそれが何であるかわからない場合、なぜ何かを構成しますか?余分なディスク容量を占有するだけですか?他の問題(NULLを適切に処理するような方法でアプリケーションを書きたくないなど)を回避するのはナンセンスのようです。何かがNULLである理由を絶対に知る必要がある場合(存在するが、不明vs存在しないことを知るvs. トークンは、扱いにくいトリクルダウンコードにつながるだけです。
アーロンバートランド

多くは、一意性制約の目的に依存します。フィールドが識別子として使用される場合、nullであってはなりません。VINの場合のように、アイテムが2回現れると、そのうちの1つが間違っている必要があるとビジネスルールが示唆する場合でも、一部のアイテムが「わからない」場合、一意性制約は適切なアプローチのように感じられません。既知のVINを持つ車両があり、データベース内の別の車両と競合する場合、少なくとも1つのVINが間違っていることを知っているかもしれませんが、推測するよりも両方のレコードの信じられている値をデータベースに報告させる方が良いでしょうそれは正しい。
supercat 14年

@AaronBertrand:nullの可能性のあるunique-if-not-nullのフィールドがサロゲートキーである必要がある場合があります。フィールドにデータを入力する前に確立できない場合があります(「配偶者ID」など) 「固有の」制約では不十分であること。X.Spouseがnullでない場合、X.Spouse.Spouse = Xが必要です。ちなみに、「配偶者」のようなものは、未婚者のレコードには配偶者として「NULL」ではなく、独自のIDが必要で、その場合X.spouse.spouse = Xルールは全員に適用されます。
supercat
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.