17


5

主キーでNULLが許可されています-なぜ、どのDBMSで?

私の質問にさらにhttps://stackoverflow.com/questions/3905703/why-to-use-not-null-primary-key-in-tsql["TSQLで「not null主キー」を使用する理由は?]]。 ..

他の議論から理解したように、一部のRDBMS(SQLite、 MySQL) permit "unique" NULL in the primary key.

なぜこれが許可され、どのように役立つのでしょうか?

背景:同僚やデータベースの専門家とのコミュニケーションには、基本概念、アプローチ、および異なるDBMSでの実装の違いを知ることが有益だと思います。

ノート

  • MySQLが修復され、「NOT NULL PK」リストに返されます。

  • 「NULL PK」リストにSQLiteが追加されました(Paul Hadfieldに感謝)。

_ _ 主キー値の一意性を判断するために、_NULL値は、他のNULLを含む他のすべての値とは異なると見なされます。

INSERTまたはUPDATEステートメントがテーブルの内容を変更して、2つ以上の行に同一の主キー値が含まれるようにしようとすると、制約違反になります。 SQL標準によると、PRIMARY KEYは常にNOT NULLを意味する必要があります。 残念ながら、長年のコーディングの見落としのため、これはSQLiteには当てはまりません。

列がINTEGER PRIMARY KEYでない限り、* SQLiteはPRIMARY KEY列でNULL値を許可します*。 SQLiteを標準に準拠するように変更することもできます(将来的には変更する可能性があります)が、見落としが発見されるまでに、SQLiteは非常に広く使用されていたため、問題を修正するとレガシーコードが壊れることを恐れました。

そのため、今のところ、PRIMARY KEY列でNULLを許可し続けることにしました。 ただし、開発者は、SQLiteが将来SQL標準に準拠するように変更される可能性があることを認識し、それに応じて新しいプログラムを設計する必要があります。

6 Answer


24


ヌル可能列Knを含む主キーがあるとします。

その2番目の行でKnがnullであり、テーブルにすでにKn nullの行が含まれているという理由で2番目の行を拒否する場合、実際にはシステムが比較 "row1.Kn = row2 .Kn "をTRUEとして指定します(これらの行のキー値が実際に等しいことを何らかの方法でシステムに検出させるため)。 ただし、この比較は「null = null」という比較に要約されます。また、標準では、nullはそれ自体を含めて何とも等しくないことを明示的に指定しています。

したがって、必要なものを考慮に入れると、SQLはNULLの処理に関する独自の原則から逸脱することになります。 SQLには無数の矛盾がありますが、この特定のものは委員会を通過することはありませんでした。


5


MySQLの古いバージョンがこの点で異なるかどうかはわかりませんが、最新バージョンでは、null以外の列に主キーが必要です。 CREATE TABLE`のマニュアルページを参照してください: " PRIMARY KEY`はすべてのキー列が必要な一意のインデックスです「NOT NULL」として定義されます。 それらが「NOT NULL」として明示的に宣言されていない場合、MySQLはそれらを暗黙的に(そして静かに)宣言します。」


4


リレーショナルデータベース理論に関する限り:

  • テーブルの主キーは、それぞれを一意に識別するために使用されます 表の行

  • 列のNULL値は、値がわからないことを示します is

  • したがって、「わからない」という値を決して使用しないでください。 テーブル内の行を一意に識別します。

モデル化するデータに応じて、NULLの代わりに「構成」値を使用できます。 0、「N / A」、「1980年1月1日」、および類似の値を使用して、ダミーの「欠落していることがわかっている」データを表しました。

すべてではありませんが、ほとんどの場合、DBエンジンはUNIQUE制約またはインデックスを許可し、NULL列値を許可しますが、(理想的には)1つの行のみに値nullを割り当てることができます(そうでない場合、一意の値ではありません)。 これを使用して、関係理論にきちんと適合しない、刺激的で実用的な(場合によっては必要な)状況をサポートできます。


3


まあ、それはあなたがデータベース内でネイティブにhttp://en.wikipedia.org/wiki/Null_Object_pattern[Null Object Pattern]を実装することを可能にするかもしれません。 したがって、DBと非常に密接に相互作用するコードで類似したものを使用している場合は、nullチェックを特別なケースにせずに、キーに対応するオブジェクトを検索できます。

今、これが価値のある機能であるかどうかはわかりませんが、実際には、nullキーを使用したい人を(良くも悪くも)妨害することの欠点を、絶対的にすべての場合にnull pkeyを拒否する長所が上回るかどうかの問題です。 これは、キーが非ヌルであることを保証できることから、重要な改善(キー検索の高速化など)を実証できる場合にのみ価値があります。 これを表示するDBエンジンもあれば、表示しないDBエンジンもあります。 そして、これを_forcing_することで本当のプロがいないなら、なぜあなたのクライアントを人為的に制限するのですか?


1


他の回答で説明したように、NULLは「この列に入力する必要がある情報は不明です」という意味でした。 ただし、「この属性は存在しません」という別の意味を示すためにも頻繁に使用されます。 これは、特定のイベントが発生した時刻として解釈されるタイムスタンプフィールドを調べるときに特に便利な解釈です。この場合、イベントがまだ発生していないことを示すためにNULLがよく使用されます。

SQLがこの解釈をうまくサポートしていないのは問題です - これが適切に機能するためには、実際には別の値が必要です ( "never"のようなもの)は、nullのようには動作しません( "never"は "never"に等しく、他のすべての値よりも高く比較する必要があります)。 ただし、SQLにはこの概念がなく、追加する便利な方法がないため、多くの場合、この目的にnullを使用することが最良の選択です。

これにより、発生していない可能性のあるイベントのタイムスタンプがテーブルの主キーの一部である必要があるという問題が残ります(一般的な要件は、要件でソフト削除を使用するときに、削除タイムスタンプとともに自然キーを使用することです)削除後にアイテムを再作成できるようにするため)、主キーにヌル値を許可する列が必要です。 残念ながら、これはほとんどのデータベースでは許可されておらず、代わりに人工的な主キーに頼らなければなりません(例: 行シーケンス番号)と、本来の主キーでなければならないものに対するUNIQUE制約。

これを明確にするためのシナリオ例:「ユーザー」テーブルがあります。 各ユーザーに個別のユーザー名を要求するため、主キーとして「ユーザー名」を使用することにしました。 ユーザーの削除をサポートしたいのですが、監査目的でユーザーの存在を履歴的に追跡する必要があるため、ソフト削除を使用します(スキーマの最初のバージョンでは、ユーザーに 'deleted’フラグを追加し、削除されたことを確認しますアクティブなユーザーのみが期待されるすべてのクエリでフラグがチェックされます)。

ただし、追加の要件は、ユーザー名が削除された場合、新しいユーザーが登録できるようにすることです。 これを達成するための魅力的な方法は、削除済みフラグをNULL可能タイムスタンプ(NULLはユーザーが削除されていないことを示す)に変更し、これを主キーに入れることです。 ヌル可能列を許可する主キーであれば、次の効果があります。

  • 既存のユーザー名で新しいユーザーを作成する場合、そのユーザーは `deleted`カラムがnullの場合、重複したキーエントリとして拒否されます

  • ユーザーを削除するとそのキーが変更されます(カスケードするには変更が必要です) ユーザーを参照する外部キー。これは最適ではありませんが、削除がまれな場合は受け入れられます)。そのため、「deleted」列は削除が発生したときのタイムスタンプになります

  • 新しいユーザー(nullの `deleted`タイムスタンプを持つ)は、 作成されました。

ただし、これは実際には標準SQLでは実現できないため、代わりに別のプライマリキー(この場合はおそらく生成された数値ユーザーID)を使用し、UNIQUE制約を使用して( username、` deleted`)の一意性を強制する必要があります。


0


主キーがnullであると、いくつかのシナリオで役立ちます。 私のプロジェクトの1つで、データベースの同期中にこの機能を使用しました。1つはサーバー上にあり、多くは異なるユーザーデバイス上にあります。 すべてのユーザーが常にインターネットにアクセスできるわけではないという事実を考慮して、メインデータベースのみがエンティティにIDを提供できると判断しました。 SQLiteには、行に番号を付けるための独自のメカニズムがあります。 追加のidフィールドを使用した場合、より多くの帯域幅を使用します。 idがnullの場合、インターネットにアクセスしていないときにエンティティがクライアントデバイス上に作成されたことを通知するだけでなく、コードの複雑さも軽減します。 唯一の欠点は、クライアントデバイス上で、以前にメインデータベースと同期していない限り、そのIDでエンティティを取得できないことです。 ただし、ユーザーは固有のIDではなく、パラメーターのエンティティを管理するため、これは問題になりません。