回答:
主キーは、行を一意に識別するためのものです。これは、キーのすべての部分を入力と比較することによって行われます。
定義ごとに、NULLを比較の成功の一部にすることはできません。それ自体との比較(NULL = NULL
)も失敗します。つまり、NULLを含むキーは機能しません。
さらに、オプションの関係をマークするために、外部キーでNULLを使用できます。(*)これをPKでも許可すると、これは壊れます。
(*)注意:NULL可能な外部キーを使用することは、リレーショナルデータベースの設計をきれいにすることではありません。
2つのエンティティがA
ありB
、A
オプションでに関連付けることができる場合B
、解決策は解決テーブルを作成することです(たとえば、としましょうAB
)。そのテーブルは以下とリンクA
しB
ます。関係がある場合はレコードが含まれ、存在しない場合は含まれません。
主キーは、テーブルのすべての行の一意の識別子を定義します。テーブルに主キーがある場合、そこから任意の行を選択する確実な方法があります。
ユニーク制約は必ずしもすべての行を識別するわけではありません。それだけであることを指定した場合、行はその列の値を持っている、そして、彼らは一意である必要があります。これは、すべての行を一意に識別するには十分ではありません。これは、主キーが行う必要があることです。
基本的に、複数列の主キーの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」など、他の誰か誰4フィールド)がアップ夢かもしれませんが不十分です。パッケージに拡張子がない場合、上記のモデルではNULLであり、そのままにしておいても害はありません。
しかし、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) DEFAULT '' NOT NULL,
notes text DEFAULT '' NOT NULL,
PRIMARY KEY (module, major, minor, patch, release, ext));
このバージョンでは、タプルの "ext"部分はNOT NULLですが、デフォルトでは空の文字列になります。これは、意味的に(そして実際的に)NULLとは異なります。NULLは不明ですが、空の文字列は「存在しないもの」の意図的な記録です。つまり、「空」と「null」は別物です。「ここに値がない」と「ここにある値がわからない」の違い。
バージョン拡張がないパッケージを登録すると、パッケージに拡張がないことがわかります。したがって、実際には空の文字列が正しい値です。NULLが正しいのは、拡張子があるかどうかがわからない場合、または拡張子があることはわかっていても、拡張子がわからない場合のみです。この状況は、文字列値が標準であるシステムで処理するのが簡単です。0または1を挿入する以外に「空の整数」を表す方法がないため、後で行われる比較でロールアップされます(これは独自の意味)*。
ちなみに、Postgresではどちらの方法も有効です(「エンタープライズ」RDMBSについて説明しているため)。ただし、NULL == "わからない"ため、比較結果にNULLを入れると、比較結果がかなり異なる場合があります。 NULLを含む比較の結果は、未知のものがわからないためNULLになります。危険!それについて注意深く考えてください。これは、NULL比較結果が一連の比較を通じて伝播することを意味します。これは、ソート、比較などの際の微妙なバグの原因となる可能性があります。
Postgresは、あなたが成人であると想定し、この決定を自分で行うことができます。OracleとDB2は、愚かなことをしていることに気づかず、エラーをスローすると想定しています。これは通常は正しいことですが、常にそうであるとは限りません。場合によっては、実際にはわからずNULLになる場合があるため、意味のある比較が不可能な不明な要素を含む行を残すことは、正しい動作です。
いずれの場合も、スキーマ全体で許可するNULLフィールドの数を排除するように努力する必要があります。主キーの一部であるフィールドに関しては、二重にそうする必要があります。ほとんどの場合、NULL列の存在は、(意図的に非正規化されたものとは対照的に)正規化されていないスキーマ設計を示しており、受け入れる前に非常に注意深く考える必要があります。
[* 注:整数の和集合であるカスタムタイプと、「不明」ではなく意味的に「空」を意味する「ボトム」タイプを作成することが可能です。残念ながら、これは比較操作に多少の複雑さをもたらし、通常NULL
は最初から多くの値を許可するべきではないため、通常は本当に型正しであることは実際に努力する価値はありません。とは言っても、「値なし」のセマンティクスを「不明な値」と偶然に混同する習慣を防ぐために、RDBMSにデフォルトのBOTTOM
タイプが含まれていればすばらしいと思いますNULL
。]
NULL == NULL-> false(少なくともDBMSでは)
したがって、実際の値を持つ追加の列があっても、NULL値を使用して関係を取得することはできません。
where pk_1 = 'a' and pk_2 = 'b'
、通常の値を使用して、where pk_1 is null and pk_2 = 'b'
nullがある場合に切り替えることができます。
where (a.pk1 = b.pk1 or (a.pk1 is null and b.pk1 is null)) and (a.pk2 = b.pk2 or (a.pk2 is null and b.pk2 is null))
/
トニー・アンドリュースの答えはまともです。しかし、本当の答えは、これはリレーショナルデータベースコミュニティで使用されている規約であり、必要ではないということです。多分それは良い慣習ではないかもしれません。
何かをNULLと比較すると、UNKNOWN(3番目の真理値)になります。したがって、ヌルで提案されているように、平等に関するすべての伝統的な知恵は窓の外に出ます。まあそれは一見それがどのように見えるかです。
しかし、これは必ずしもそうだとは思いません。SQLデータベースでさえ、NULLが比較の可能性をすべて破壊するとは考えていません。
データベースでクエリSELECT * FROM VALUES(NULL)UNION SELECT * FROM VALUES(NULL)を実行します
表示されるのは、値がNULLである1つの属性を持つ1つのタプルだけです。したがって、ユニオンはここで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はNULLかつYはNULL)
この種の等価演算子を使用すると、nullコンポーネントを持つ複合キーまたはnull値を持つ非複合キーが問題なくなります。
私は今でも、これは技術によってもたらされた根本的/機能的な欠陥だと信じています。顧客を特定できるオプションのフィールドがある場合は、NULL!= NULLであるという理由だけでダミー値をハックする必要があります。特にエレガントではありませんが、「業界標準」です。