私の質問に加えて 「TSQLで「null以外の主キー」を使用する理由」 ...
他の議論から理解したように、いくつかのRDBMS(たとえば、SQLite、 MySQL)主キーで「一意の」NULLを許可します。
なぜこれが許可され、どのように役立つのでしょうか?
背景:基本的な概念、アプローチ、およびさまざまなDBMSでの実装の違いを知ることは、同僚やデータベースの専門家とのコミュニケーションにとって有益であると思います。
主キー値の一意性を判断するために、NULL値は、他のNULLを含む他のすべての値とは異なると見なされます。
INSERTまたはUPDATEステートメントがテーブルの内容を変更して、2つ以上の行が同一の主キー値を特徴とするようにしようとした場合、それは制約違反です。 SQL標準によれば、PRIMARYKEYは常にNOTNULLを意味する必要があります。残念ながら、長年のコーディングの監視により、SQLiteではそうではありません。
列がINTEGERPRIMARY KEYでない限り、SQLiteはPRIMARY KEY列でNULL値を許可します。 SQLiteを標準に準拠するように変更することはできますが(将来的に変更する可能性があります)、見落としが発見されるまでにSQLiteは非常に広く使用されていたため、問題を修正するとレガシーコードが破損することを恐れていました。
したがって、今のところ、PRIMARYKEY列でNULLを引き続き許可することを選択しました。ただし、開発者は、将来SQL標準に準拠するようにSQLiteを変更する可能性があることに注意し、それに応じて新しいプログラムを設計する必要があります。
Null許容列Knを含む主キーがあるとします。
2番目の行でKnがnullであり、テーブルにKn nullの行がすでに含まれているという理由で、2番目の行を拒否する場合は、実際には、システムが比較「row1.Kn = row2」を処理する必要があります。 .Kn "はTRUEを与えます(これらの行のキー値が実際に等しいことをシステムに検出させたいため)。ただし、この比較は「null = null」の比較に要約され、標準では、nullはそれ自体を含む何にも等しく比較されないことをすでに明示的に指定しています。
したがって、必要なことを可能にするために、SQLはnullの処理に関する独自の原則から逸脱することになります。 SQLには無数の矛盾がありますが、この特定の矛盾は委員会を通過することはありませんでした。
MySQLの古いバージョンがこれで異なるかどうかはわかりませんが、最新バージョンでは、主キーはnullではない列にある必要があります。 CREATE TABLE
のマニュアルページ を参照してください: "PRIMARY KEY
は、すべてのキー列をNOT NULL
として定義する必要がある一意のインデックスです。明示的に宣言されていない場合NOT NULL
、MySQLはそれらを暗黙的に(そして静かに)宣言します。」
リレーショナルデータベース理論に関する限り:
モデル化するデータによっては、NULLの代わりに「構成された」値を使用できます。 0、「N/A」、「1980年1月1日」などの値を使用して、ダミーの「欠落していることがわかっている」データを表しました。
すべてではないにしても、ほとんどのDBエンジンはUNIQUE制約またはインデックスを許可します。これにより、NULL列値が許可されますが、(理想的には)1つの行にのみ値nullを割り当てることができます(そうでない場合、一意の値にはなりません)。これは、関係理論にうまく適合しない、苛立たしい実用的な(しかし時々必要な)状況をサポートするために使用できます。
そうですね、データベース内に Null Object Pattern をネイティブに実装できる可能性があります。したがって、DBと非常に密接に相互作用するコードで類似したものを使用している場合は、nullチェックを特別に指定しなくても、キーに対応するオブジェクトを検索できます。
これが価値のある機能であるかどうかはわかりませんが、すべての場合にnull pkeyを許可しないことの長所が、(良くも悪くも)実際にnullキーを使用したい人を妨害することの短所を上回るかどうかは本当に問題です。これは、キーがnullでないことを保証できることから、重要な改善(キー検索の高速化など)を実証できる場合にのみ価値があります。一部のDBエンジンはこれを表示しますが、表示しない場合もあります。そして、forceingからの本当のプロがいない場合、なぜ人為的にクライアントを制限するのですか?
他の回答で説明されているように、NULLは、「この列に入力する必要のある情報が不明である」ことを意味することを意図していました。ただし、「この属性は存在しません」という別の意味を示すために頻繁に使用されますまた。これは、特定のイベントが発生した時刻として解釈されるタイムスタンプフィールドを確認する場合に特に便利な解釈です。この場合、イベントがまだ発生していないことを示すためにNULLがよく使用されます。
SQLがこの解釈を十分にサポートしていないのは問題です。これが正しく機能するためには、nullのように動作しない(「never」のように)別の値(「never」など)が必要です。 「never」に等しく、他のすべての値よりも高く比較する必要があります)。しかし、SQLにはこの概念がなく、それを追加する便利な方法がないため、この目的でnullを使用することが最良の選択であることがよくあります。
これにより、発生しなかった可能性のあるイベントのタイムスタンプをテーブルの主キーの一部にする必要がある場合に問題が残ります(一般的な要件は、要件とともにソフト削除を使用する場合に、削除タイムスタンプとともに自然キーを使用することです。削除後にアイテムを再作成できるようにするため)、主キーにnull可能な列を含める必要があります。残念ながら、これはほとんどのデータベースでは許可されていません。代わりに、人工の主キー(行シーケンス番号など)と、実際の主キーであるはずの一意の制約に頼る必要があります。
これを明確にするためのシナリオ例:users
テーブルがあります。各ユーザーに個別のユーザー名が必要なため、username
を主キーとして使用することにしました。ユーザーの削除をサポートしたいのですが、監査目的でユーザーの存在を履歴的に追跡する必要があるため、ソフト削除を使用します(スキーマの最初のバージョンでは、ユーザーに「削除済み」フラグを追加し、削除されたことを確認しますフラグは、アクティブユーザーのみが予想されるすべてのクエリでチェックされます)。
ただし、追加の要件は、ユーザー名が削除された場合、新しいユーザーが登録できるようにする必要があることです。これを実現するための魅力的な方法は、削除されたフラグをnull可能なタイムスタンプ(nullはユーザーが削除されていないことを示す)に変更し、これを主キーに配置することです。 null許容列を許可する主キーの場合、これには次の効果があります。
deleted
列がnullの場合に、既存のユーザー名で新しいユーザーを作成すると、重複キーエントリとして拒否されます。deleted
列は削除が発生したときのタイムスタンプになります。deleted
タイムスタンプを持つ)を正常に作成できます。ただし、これは実際には標準SQLでは実現できないため、代わりに別の主キー(この場合は生成された数値ユーザーID)を使用し、UNIQUE制約を使用して(username
、deleted
)の一意性を強制する必要があります。
一部のシナリオでは、主キーをnullにすることが有益な場合があります。私のプロジェクトの1つでは、データベースの同期中にこの機能を使用しました。1つはサーバー上にあり、多くは異なるユーザーのデバイス上にあります。すべてのユーザーが常にインターネットにアクセスできるわけではないという事実を考慮して、メインデータベースのみがエンティティにIDを与えることができると判断しました。 SQLiteには、行に番号を付けるための独自のメカニズムがあります。追加のIDフィールドを使用した場合、より多くの帯域幅を使用します。 IDとしてnullを指定すると、インターネットにアクセスしていないときにクライアントデバイスにエンティティが作成されたことが通知されるだけでなく、コードの複雑さが軽減されます。唯一の欠点は、クライアントデバイスでは、以前にメインデータベースと同期されていない限り、IDでエンティティを取得できないことです。ただし、私のユーザーは一意のIDではなく、パラメーターのエンティティを気にするので、これは問題ではありません。