Oracleでは、主キーを構成する列でNULL値を使用できません。同じことが他のほとんどの「エンタープライズレベル」システムにも当てはまるようです。
同時に、ほとんどのシステムではnique nullable列の制約も許可されています。
一意制約にNULLを含めることができるのに、主キーに含めることができないのはなぜですか?これには根本的な論理的理由がありますか、それとも技術的な制限ですか?
主キーは、行を一意に識別するためのものです。これは、キーのすべての部分を入力と比較することにより行われます。
定義ごとに、NULLを正常な比較の一部にすることはできません。自身との比較(NULL = NULL
)でも失敗します。これは、NULLを含むキーが機能しないことを意味します。
さらに、オプションの関係を示すために、外部キーにNULLを使用できます。(*) PKでも同様に許可すると、これが壊れます。
(*)注意事項:null許容の外部キーを持つことは、クリーンなリレーショナルデータベース設計ではありません。
A
とB
の2つのエンティティがある場合、A
はオプションでB
に関連付けることができます。解決策は、解決テーブルを作成することです(たとえば、AB
)。そのテーブルはA
とB
をリンクします:isリレーションシップがある場合、レコードが含まれますではない.
主キーは、テーブルのevery rowの一意の識別子を定義します。テーブルに主キーがある場合、そこから任意の行を選択する保証された方法があります。
ユニーク制約は、必ずしもすべての行を識別するわけではありません。 if行の列に値があること、thenが一意でなければならないことを指定するだけです。これは、every rowを一意に識別するのに十分ではありません。これは主キーが行う必要のあることです。
基本的に、複数列の主キーのNULLには何も問題はありません。しかし、設計者が意図していなかった可能性があるため、これを試みると多くのシステムがエラーをスローします。
一連のフィールドとして保存されているモジュール/パッケージバージョンの場合を考えます。
CREATE TABLE module
(name varchar(20) PRIMARY KEY,
description text DEFAULT '' NOT NULL);
CREATE TABLE version
(module varchar(20) REFERENCES module,
major integer NOT NULL,
minor integer DEFAULT 0 NOT NULL,
patch integer DEFAULT 0 NOT NULL,
release integer DEFAULT 1 NOT NULL,
ext varchar(20),
notes text DEFAULT '' NOT NULL,
PRIMARY KEY (module, major, minor, patch, release, ext));
主キーの最初の5つの要素は、リリースバージョンの定期的に定義された部分ですが、一部のパッケージには、通常は整数ではないカスタマイズされた拡張機能があります(「rc-foo」、「Vanilla」、「beta」、 fourフィールドが不足していると、夢を見るかもしれません)。パッケージに拡張子がない場合、上記のモデルではNULLであり、そのままにしておくと害はありません。
しかし、is NULLとは何ですか?情報のlackを表すことになっていますが、不明です。とはいえ、おそらくこれはもっと理にかなっています:
CREATE TABLE version
(module varchar(20) REFERENCES module,
major integer NOT NULL,
minor integer DEFAULT 0 NOT NULL,
patch integer DEFAULT 0 NOT NULL,
release integer DEFAULT 1 NOT NULL,
ext varchar(20) DEFAULT '' NOT NULL,
notes text DEFAULT '' NOT NULL,
PRIMARY KEY (module, major, minor, patch, release, ext));
このバージョンでは、Tupleの「ext」部分はNULLではありませんが、デフォルトでは空の文字列になります。これは、NULLとは意味的に(そして実際的に)異なります。 NULLは不明ですが、空の文字列は「存在しないもの」の意図的な記録です。つまり、「空」と「ヌル」は異なるものです。 「ここに値がない」と「ここの値がわからない」の違いです。
バージョン拡張子のないパッケージを登録すると、know拡張子がないため、空の文字列が実際に正しい値になります。 NULLが正しいのは、拡張子があるかどうかわからない場合、または知っていたがそれが何であるかわからなかった場合のみです。文字列値が標準であるシステムでは、この状況は簡単に処理できます。これは、0または1を挿入する以外に「空の整数」を表す方法がないためです。独自の意味)*。
ちなみに、Postgresでは両方の方法が有効です(「エンタープライズ」RDMBSについて説明しているため)。ただし、NULL ==「わからない」ので、ミックスにNULLをスローすると、比較結果がかなり変わる可能性があります。 NULLを含む比較の結果は、未知のものを知ることができないため、NULLになります。 DANGER!それについて慎重に考えてください:これは、一連の比較を通じてNULL比較結果が得られることを意味します伝播。これは、並べ替え、比較などの際に微妙なバグの原因になる可能性があります。
Postgresはあなたが大人であると仮定し、あなた自身でこの決定を下すことができます。 OracleとDB2は、あなたが何かおかしいことをしていることに気づかずにエラーをスローしたと仮定します。これは通常正しいことですが、常にではありません-実際に場合によってはNULLがわからないため、意味のある不明な要素を持つ行を残します。比較が不可能なのは正しい動作です。
いずれの場合でも、スキーマ全体で許可するNULLフィールドの数を排除するよう努力する必要があります。また、主キーの一部であるフィールドに関しては二重に排除する必要があります。ほとんどの場合、NULL列の存在は、非正規化(意図的に非正規化されたものではない)スキーマ設計の指標であり、受け入れられる前に非常に慎重に検討する必要があります。
[* 注:整数の結合であるカスタム型と、「不明」ではなく「空」を意味する「ボトム」型を作成することができます。残念ながら、これは比較演算に少し複雑さをもたらします。最初は多くのNULL
値を許可するべきではないので、通常、真に正しい型であることは実際には努力する価値がありません。つまり、RDBMSにBOTTOM
に加えてデフォルトのNULL
タイプを含めて、「値なし」のセマンティクスを「不明な値」と偶然に混同する習慣を防ぐことができれば素晴らしいと思います。 ]
NULL == NULL-> false(少なくともDBMSでは)
したがって、実際の値を持つ追加の列があっても、NULL値を使用してリレーションシップを取得することはできません。
トニー・アンドリュースによる答えはまともなものです。しかし、本当の答えは、これはリレーショナルデータベースコミュニティで使用されている慣習であり、必須ではないということです。たぶんそれは良い慣習であり、そうでないかもしれません。
NULLと何かを比較すると、UNKNOWN(3番目の真理値)になります。そのため、ヌルで示唆されているように、平等に関するすべての伝統的な知恵は窓から消えます。それは一見したところそうです。
しかし、これは必ずしもそうだとは思わず、SQLデータベースでさえ、NULLが比較の可能性をすべて破壊するとは考えていません。
データベースでクエリSELECT * FROM VALUES(NULL)UNION SELECT * FROM VALUES(NULL)を実行します
表示されるのは、値がNULLの1つの属性を持つ1つのTupleだけです。そのため、ここで共用体は2つのNULL値を等しいと認識しました。
3つのコンポーネントを持つ複合キーを、3つの属性(1、3、NULL)=(1、3、NULL)<=> 1 = 1 AND 3 = 3 AND NULL = NULLのタプルと比較する場合、この結果はUNKNOWNです。
しかし、たとえば、新しい種類の比較演算子を定義できます。 ==。 X == Y <=> X = Y OR(X IS NULL AND Y IS NULL)
この種の等価演算子を使用すると、nullコンポーネントを持つ複合キーまたはnull値を持つ非複合キーが問題なくなります。
私は今でもこれが技術によってもたらされる根本的/機能的な欠陥だと信じています。顧客を識別できるオプションフィールドがある場合、ダミー値をハックする必要があります。これは、NULL!= NULLであるため、特にエレガントではありませんが、「業界標準」です