なぜNULLを許可しないのですか?


125

データベース設計に関するこの1つの記事を読んだことを覚えています。また、NOT NULLのフィールドプロパティを持つべきだと言ったことを覚えています。しかし、なぜそうなのかは覚えていません。

私が考えることができるのは、アプリケーション開発者として、NULL および存在しないデータ値(たとえば、文字列の空の文字列)をテストする必要がないということだけです。

しかし、日付、日時、および時刻の場合はどうしますか(SQL Server 2008)。あなたは、いくつかの歴史的な日付またはボトムアウト日付を使用する必要があります。

これに関するアイデアはありますか?


4
この答えは、NULLのの使用に関する洞察力があるdba.stackexchange.com/questions/5176/...
デレク・ダウニー

10
本当に?RDBMSを使用してはいけないのに、なぜNULLを使用できるのですか?NULLに対処する方法を知っている限り、NULLに問題はありません。
Fr0zenFyr

3
これはBIデータモデリングでしたか?通常、ファクトテーブルでnullを許可しないでください。それ以外の場合、nullは適切に使用すれば友だちです。=)
SAMのYI

2
@ Fr0zenFyr、RDBMSが何かを実行できるからといって、必ずしもそうすることは必ずしも良い考えではありません。テーブルで主キーまたは一意キーを宣言することを強制するものはありませんが、例外はほとんどありません。
レナート

3
この主題を完全に扱うには、RDBMSが欠落データを体系的に処理する必要があるというCoddの当初の要件を参照する必要があると思います。現実の世界では、データの場所は作成されますが、そこに入れるデータはありません。データベース設計、アプリケーションプログラミング、またはその両方に関係なく、データアーキテクトはこれに対する何らかの対応策を考え出す必要があります。SQL NULLはこの要件を満たすには完全ではありませんが、何もしないよりはましです。
ウォルターミッティ

回答:


230

この文言は、NULLが悪いとすでに決めていることを暗示しているので、質問の言い回しは不十分だと思います。「NULLを許可する必要がありますか?」

とにかく、ここに私の考えがあります:NULLは良いことだと思います。「NULLが悪い」または「NULLが難しい」という理由だけでNULLの防止を開始すると、データの作成を開始します。たとえば、私の生年月日がわからない場合はどうなりますか?わかるまで何を列に入れますか?あなたが多くの反NULLの人々のようなものなら、あなたは1900-01-01を入力するつもりです。今、私は老人病棟に配置され、おそらく地元のニュース局から電話を受けて、長寿を祝福し、そのような長寿を生きる秘secretなどを尋ねます。

列の値がわからない可能性がある場所に行を入力できる場合、不明な事実を表すために任意のトークン値を選択するよりもNULLの方がはるかに理にかなっていると思います。既に知っているか、リバースエンジニアリングするか、またはその意味を理解するために周りに尋ねる必要があります。

ただし、バランスがあります。データモデルのすべての列がNULL値を許可されるわけではありません。多くの場合、フォームにはオプションのフィールドや、行の作成時に収集されない情報の断片があります。しかし、それはすべてのデータの取り込みを延期できるという意味ではありません。:-)

また、NULLを使用する機能は、実際の重要な要件によって制限される場合があります。たとえば、医療分野では、なぜ価値が不明なのを知ることは生死にかかわる問題になる可能性があります。脈拍がなかったか、またはまだ測定していないため、心拍数はNULLですか?このような場合、心拍数の列にNULLを入れて、NULLの理由でメモまたは別の列を使用できますか?

NULLを恐れる必要はありませんが、いつ、どこで使用すべきか、いつ、どこで使用すべきではないかを学習または指示してください。


3
「未知の事実を表すための任意のトークン値」これはセンチネル値
アレクサンダー

4
しかし、birth_date生年月日を保存する別のテーブルを作成できないのはなぜですか?生年月日が不明な場合は、生年月日をに挿入しないでくださいbirth_date。ヌルは災害です。
エルダーアガラロフ

6
@EldarAgalarovそれはトランプの推論のように聞こえます(「災害」がなぜですか?どのように?誰のために?何かが「災害」であるというあなたの意見はそうではありません)。とにかく生年月日はほんの一例です。15のNULL可能列が存在する可能性のある人員、メンバー、または顧客がいる場合、15のセカンダリテーブルを作成しますか?50人いる場合はどうなりますか?DWファクトテーブルに500がある場合はどうなりますか?データベースのうち、大きな悪い怖いNULLを維持するためのメンテナンスは、任意の「災害」あなたは...の恐れているとして、10倍ほど悪くなった
アーロン・ベルトラン

3
@AaronBertrandテーブルに15のNULL可能列が含まれている可能性がある場合、本当にひどい臭いがします しかし、それは疑問を提起します。
プログラミング

2
@Wildcardだから1900-01-01、NULLの日付/時刻の値を持つことを避けるために人々がストアするのを見たことがありませんか?じゃあ また、NULL =不明および不明= false。私は、それを知っている人が生まれていないこと以外に、これがどんな問題を引き起こす可能性があるのか​​わかりません(複雑なRDBMSに固有の多くのことを知って生まれていないように)。繰り返しますが、手を振って「問題!災害!」そうはしません。
アーロンバートランド

57

確立された理由は次のとおりです。

  • NULLは値ではないため、固有のデータ型はありません。Null は、実際の型に依存するコードも型指定されていないNULLを受け取る可能性がある場合に、あらゆる場所で特別な処理が必要です。

  • NULLは、2値(おなじみのTrueまたはFalse)ロジックを中断し、3値ロジックを必要とします。これは、正しく実装することすらはるかに複雑であり、ほとんどのDBAおよびほとんどすべての非DBAによって十分に理解されていません。結果として、アプリケーションの多くの微妙なバグを積極的に招きます。

  • 実際の値とは異なり、特定のNULLセマンティックな意味はアプリケーション委ねられます。

    「適用外」、「不明」、「センチネル」などのセマンティクスは一般的ですが、他にもあります。同じリレーション内であっても、同じデータベース内で同時に頻繁に使用されます。そしてもちろん、暗黙で区別がつかず、互換性のない意味です。

  • 彼らは、リレーショナルデータベースには必要ないで主張として、「ヌルなしで情報が欠落して処理する方法」。さらに正規化することは、NULLのテーブルを取り除くことを試みるための明らかな最初のステップです。

これは、NULLが許可されないという意味ではありません。可能な限りNULLを許可しない多くの理由があると主張してます。

重要なことは、スキーマ設計の改善、データベースエンジンの改善、さらにはデータベース言語の改善を通じて、より頻繁にNULLを回避できるようにするために、一生懸命努力することだと主張しいます。

ファビアンパスカルは、「Nulls Nullified」の多くの議論に応えます。


3
「ヌルなしで欠落している情報を処理する方法」へのリンクは、ヌルなしでできない理由を非常によく示しています。
ジャックダグラス

7
ジャック:正しいですが、「現在の実装ではできません」は現状の議論ではありません:-)
bignose

17
飛行機は完璧ではないので、私たちは飛ぶべきではないと言っているようなものですか?
アーロンバートランド

11
いいえ、ベンダーは、40年前に有効だった可能性のあるnullの言い訳の呼び出しを停止する必要があると言っていますが、妥当な保持期間を長持ちします。I / O時間はもはや80msのオーダーではありません。シングルCPUサイクルは、もはやマイクロ秒のオーダーではありません。メモリの制限は、数メガバイトの大きさではなくなりました。40年前とは異なり、nullなしで作業するために必要なハードウェアの速度と容量は、コストが法外に高くなることなく存在します。彼は先に進む時だと言っています。
アーウィンSmout

2
「NULL混乱」リンクは無効です。
jpmc26

32

私は同意しません、nullはデータベース設計の重要な要素です。あなたが暗示したように、代替手段は、欠落または不明を表す既知の値の急増です。問題は、nullが非常に広く誤解されており、その結果、不適切に使用されていることにあります。

IIRC、Coddは、「存在しない/適用できない」および「存在しない/適用できない」という1つではなく2つのNULLマーカーを使用することで、現在のNULLの実装(存在しない/ないことを意味する)を改善できることを示唆しました。これによって個人的にリレーショナルデザインがどのように改善されるかは想像できません。


2
私は、異なる種類のユーザ定義の集合持つことをお勧めnullP:、彼らと行くには、ユーザー定義した多値ロジック
ジャック・ダグラス

13
これらは唯一のオプションではありません。正規化の代替手段を除外します。値を持つ場合と持たない場合がある列の代わりに、最初のテーブルに対応する行がある場合とない場合がある別のテーブルを使用します。行の有無の意味は表の意味に含まれており、NULLまたはセンチネル値などの特別なケースはありません。
bignose11年

7
NULLの存在は、特別なケーシングまたはセンチネルの値を必要としません。これらは、一部の人々がNULLを扱うことを決定する方法の単なる症状です。
アーロンバートランド

''はPostgreSQLのnullとは異なり(Oracleではありません)、2つ折りマーカーを提供し、数値列には0を使用できます。ただし、0の問題は、外部キーでは機能しないことです。
クリストラバーズ

13

私はDBAではなく、心から開発者であり、私たちのニーズに基づいてデータベースを維持および更新することから始めましょう。そうは言っても、いくつかの理由で同じ質問がありました。

  1. NULL値は、開発をより困難にし、バグを起こしやすくします。
  2. NULL値は、クエリ、ストアドプロシージャ、およびビューをより複雑にし、バグを起こしやすくします。
  3. NULL値はスペースを占有します(固定列長に基づいて?バイト、可変列長に2バイト)。
  4. Null値は、インデックス付けと数学に影響を与える可能性があります。

私は非常に長い時間を費やして、インターネット上のさまざまな回答、コメント、記事、アドバイスをふるいにかけています。言うまでもなく、ほとんどの情報は@AaronBertrandの応答とほぼ同じでした。それが、この質問に答える必要性を感じた理由です。

まず、すべての将来の読者のために何かをまっすぐに設定したいです... NULL値は、未使用データではなく不明データを表します。終了日フィールドを持つ従業員テーブルがある場合。終了日のヌル値は、現在不明な将来の必須フィールドであるためです。すべての従業員は、アクティブまたは退職しても、ある時点でそのフィールドに日付が追加されます。私の意見では、それがNullableフィールドの唯一の理由です。

同じ従業員テーブルが何らかの認証データを保持する可能性が高いと言われています。エンタープライズ環境では、従業員がHRおよびアカウンティングのデータベースにリストされることが一般的ですが、認証の詳細を常に持っている必要はありません。ほとんどの応答では、これらのフィールドを無効にしたり、場合によってはアカウントを作成しても資格情報を送信したりすることはできないと考えるようになります。前者は開発チームにNULLをチェックするコードを記述させ、それに応じてNULLを処理させ、後者は大きなセキュリティリスクをもたらします!システムでまだ使用されていないアカウントは、ハッカーのアクセスポイントの数を増やすだけであり、さらに使用されないもののために貴重なデータベーススペースを占有します。

上記の情報が与えられた場合、使用されるヌル値を許可するデータを処理する最良の方法は、ヌル値を許可することです。それは悲しいですが真実であり、あなたの開発者はあなたを嫌います。ヌル可能データの2番目のタイプは、関連するテーブル(IE:アカウント、資格情報など)に配置され、1対1の関係を持つ必要があります。これにより、必要でない限り、ユーザーは資格情報なしで存在できます。これにより、余分なセキュリティリスク、貴重なデータベーススペースが削除され、よりクリーンなデータベースが提供されます。

以下は、必要なNULL入力可能列と1対1の関係の両方を示す非常に単純なテーブル構造です。

不明なNullableおよび1対1の関係

この質問は何年も前に尋ねられて以来、私はパーティーに少し遅れていることを知っていますが、これがこの問題とそれに対処する最善の方法を明らかにするのに役立つことを願っています。


2
TerminationDate従業員レコードにないように変更するだけですが、従業員TerminatedEmployeeが終了したときにアプリケーションによって従業員が移動(コピーではなく)されるテーブルがあります。テーブルにリンクされたアカウントがないため、これは明らかにAccountテーブルでうまく機能しTerminatedEmployeeます。それでも電話番号が必要な場合は、外部キーを逆にして、従業員テーブルと終了した従業員テーブルに電話番号のIDを持たせるようにします。
Programster

2
これがなぜ悪いのか、文字通り何日も続けることができました。冗長なテーブル、不適切なSQLプラクティス、開発者が従業員データ、レポートの問題、存在しない(移動された)従業員への直接URIの問題、およびリストを探すために2つの場所を探す必要があるようにするなど。いつか値を持つフィールドにNULLを設定することはまったく問題ありません。それは、入力されず使用されないフィールドを持つことの別の話です。この作業を行うための多くの潜在的な問題と回避策は、フィールドでNULLをチェックするという小さな問題に値しません。
ニコラスアギレ

1
同意しません。冗長な唯一のことは、終了日のヌルフィールドが決して埋められないことです。開発者は、必要なデータの適切なテーブルを調べるだけで、パフォーマンスを改善できます。何らかの理由で、解雇された従業員と解雇されていない従業員の両方が必要な場合、結合によって解決されますが、90%の時間でおそらくどちらか一方が必要になります。私が指定したレイアウトの方が優れていると思います。なぜなら、従業員の解雇日を持ち、従業員がまだアカウントを持っていることは不可能だからです。
Programster

2
冗長データとは言いませんでした、冗長テーブルと言いました。さらに、従業員テーブルへの変更はすべて、終了したテーブルまで細流化する必要があります。これにより、アプリにエラーが発生しやすくなり、開発者の仕事がはるかに困難になります。さらに、ほとんどの人が終了日フィールドに入力されます。2つ目の同一のテーブル構造を作成し、データを移動することは無駄で問題があります。テーブルデータが移動およびクリーンアップされたことを確認するためのテストを毎回含めないでください。テーブルを移動するだけであっても、テーブルからデータを削除するのは悪い習慣です。あなたは...単一のフィールドとその懸念がある場合
ニコラス・アギーレ

1
...ほとんど常に時間で満たされ、その後、従業員と1対1の関係を持つ終了テーブルを作成します。私は、DBAと開発者の両方として、さまざまなデータベースを終日使用していますが、あなたが提案した構造にまだ出会えていないことがうれしいです。特に開発者の観点からは、どのテーブルから来たのか分からないため、すべてを記述してエラーチェックを行うのは悪夢です。結合を作成したとしても、ソフトウェアに返されるデータにはnullデータのフィールドが含まれるので、それもテストする必要があります。
ニコラスアギレ

13

NULLの混乱を招く開発者に関するすべての問題とは別に、NULLには別の非常に重大な欠点があります。パフォーマンス

NULL'able列は、パフォーマンスの観点から見ると災害です。例として整数演算を検討してください。NULLのない健全な世界では、SIMD命令を使用してデータベースエンジンコードで整数演算をベクトル化し、CPUサイクルあたり1行よりも速い速度でほとんどすべての計算を実行するのは「簡単」です。ただし、NULLを導入した瞬間に、NULLが作成するすべての特殊なケースを処理する必要があります。最新のCPU命令セット(x86 / x64 / ARMおよびGPUロジックも参照)には、これを効率的に行うための機能がありません。

例として除算を検討してください。非常に高いレベルでは、これは非NULL整数で必要なロジックです。

if (b == 0)
  do something when dividing by error
else
  return a / b

NULLを使用すると、これは少し複雑になります。とともに、bif bがnullであり、同様にが必要ですa。チェックは次のようになります。

if (b_null_bit == NULL)
   return NULL
else if (b == 0) 
   do something when dividing by error
else if (a_null_bit == NULL)
   return NULL
else 
   return a / b

NULL演算は、非NULL演算よりも現代のCPUでの実行が大幅に遅くなります(約2〜3倍)。

SIMDを導入すると悪化します。SIMDを使用すると、最新のIntel CPUは、次のように1つの命令で4 x 32ビット整数除算を実行できます。

x_vector = a_vector / b_vector
if (fetestexception(FE_DIVBYZERO))
   do something when dividing by zero
return x_vector;

現在、SIMDランドでNULLを処理する方法もありますが、これにはより多くのベクトルとCPUレジスタを使用し、巧妙なビットマスキングを行う必要があります。優れたトリックを使用しても、NULL整数演算のパフォーマンスの低下は、比較的単純な式であっても5〜10倍遅い範囲に忍び込みます。

上記のようなものは、集約に対してもある程度結合します。

言い換えると、SQLにおけるNULLの存在は、データベース理論と現代のコンピューターの実際の設計との間のインピーダンスの不一致です。NULLが開発者を混乱させるのにはかなりの理由があります-ほとんどの健全なプログラミング言語では整数をNULLにできないためです-これはコンピューターの動作ではありません。


10

興味深い質問。

私が考えることができるのは、アプリケーション開発者として、NULLおよび存在しないデータ値(たとえば、文字列の空の文字列)をテストする必要がないということだけです。

それよりも複雑です。Nullには多くの明確な意味があり、多くの列でnullを許可しないことの1つの本当に重要な理由は、列がnullのとき、これが唯一のことを意味することです(つまり、外部結合に表示されなかったということです)。さらに、データ入力の最小基準を設定することもできます。これは非常に役立ちます。

しかし、日付、日時、および時刻の場合はどうしますか(SQL Server 2008)。あなたは、いくつかの歴史的な日付またはボトムアウト日付を使用する必要があります。

これは、すぐにヌルの問題を示しています。つまり、テーブルに格納されている値は、「この値は適用されない」または「わからない」のいずれかを意味します。文字列では、空の文字列は「これは適用されません」として機能しますが、日付と時刻では、これを意味する有効な値がないため、そのような規則はありません。通常、NULLを使用するとスタックします。

これを回避する方法(リレーションを追加して結合する方法)がありますが、データベースにNULLが存在する場合とまったく同じセマンティッククラリティの問題が発生します。これらのデータベースの場合、これについては心配しません。本当にできることは何もありません。

編集:NULL 不可欠な領域の1つは、外部キーです。ここでは、通常、外部結合の意味のnullと同じ意味を1つだけ持っています。これはもちろん問題の例外です。


10

SQL Nullに関するWikipediaの記事には、NULL値に関する興味深いコメントがあり、データベースに依存しない回答として、特定のRDBMSにNULL値を持つことの潜在的な影響を認識している限り、それらは設計で受け入れられます。そうでない場合、列をヌル可能として指定することはできません。

RDBMSが数学などのSELECT操作やインデックスでもそれらをどのように処理するかに注意してください。


-12

うわー、「パフォーマンスが低下するため、必要がない場合はNULLを許可しない」という正解は、どういうわけか最後に評価された回答です。私はそれを賛成し、詳しく説明します。RDBMSが非スパース列にNULLを許可する場合、その列はビットマップに追加され、個々の行の値がNULLかどうかを追跡します。そのため、すべての列でNULLが許可されていないテーブルの列にNULL機能を追加すると、テーブルを保存するために必要なストレージ領域が増加します。さらに、ビットマップの読み取りと書き込みをRDBMSに要求しているため、すべての操作のパフォーマンスが低下します。

さらに、多くの場合、NULLを許可すると3NFが壊れます。私は同僚の多くのように3NFにこだわりはありませんが、次のシナリオを検討してください。

Personテーブルには、DateOfDeathと呼ばれる、null許容の列があります。人が死亡した場合、DateOfDeathが入力され、そうでない場合はNULLのままになります。IsAliveと呼ばれる、null不可のビット列もあります。この列は、人が生きている場合は1に、人が死んでいる場合は0に設定されます。ストアドプロシージャの大部分はIsAlive列を使用し、DateOfDeathではなく人が生きている場合にのみ気にします。

ただし、IsAlive列は、DateOfDeathから完全に派生できるため、データベースの正規化を中断します。ただし、IsAliveは大部分のSPに組み込まれているため、簡単な解決策は、DateOfDeathをNULL不可にし、その人物がまだ生きている場合にデフォルト値を列に割り当てることです。DateOfDeathを使用するいくつかのSPは、IsAlive列を確認するように書き換えることができ、人物が生きていない場合にのみDateOfDeathを尊重します。繰り返しますが、SPの大部分はIsAlive(ビット)のみを考慮し、DateOfDeath(日付)ではなく、このパターンを使用するとアクセスが大幅に高速化されるためです。

すべてのスキーマでNULLのないNULL許容列を検索するための便利なT-SQLスクリプトは次のとおりです。

select 'IF NOT EXISTS (SELECT 1 FROM ' + QUOTENAME(s.name) + '.' + QUOTENAME(t.name) + ' WHERE ' + QUOTENAME(c.name) + ' IS NULL)
    AND (SELECT COUNT(*) FROM ' + QUOTENAME(s.name) + '.' + QUOTENAME(t.name) + ') > 1 PRINT ''' + s.name + '.' + t.name + '.' + REPLACE(c.name, '''', '''''') + ''''
    from sys.columns c
    inner join sys.tables t ON c.object_id = t.object_id
    inner join sys.schemas s ON s.schema_id = t.schema_id
    where c.is_nullable = 1 AND c.is_computed = 0
    order by s.name, t.name, c.name;

実稼働データベースのコピーでこれを実行すると、実際にはNULLを持たないNULLを許可するようにマークされた列の開発者を見つけることができます。これらの大半はNOT NULLとしてマークできるため、パフォーマンスが向上し、ストレージスペースが減少します。

すべてのテーブルのすべてのNULLを削除することは不可能であり、クリーンなデザインを維持することはできませんが、可能な限り多くのNULLを削除することには大きな利点があります。オプティマイザーはこの情報を使用してはるかに高速に動作し、テーブル内のすべてのNULLを削除できる場合、かなりの量のストレージスペースを取り戻すことができます。

パフォーマンスはDBAがそれほど考慮しているものではないことはわかっていますが、ソリューションでは限られた量のメモリとプロセッサパワーしか投入できません。 。

また、これは真のRDBMS専用であり、回答の技術的な部分はSQL Serverに基づいていることに注意してください。nullのないnull許容列を検索するためにリストされているT-SQLもSQL Serverからのものです。


1
コメントは詳細なディスカッション用ではありません。この会話はチャットに移動さました
ポールホワイト
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.