NULL参照の発明者と主に呼ばれているTonyHoareは、SQLを含め、ほとんどすべての言語が現在「苦しんでいる」10億ドルの間違いと呼んでいます。
トニーの引用(彼のウィキペディアの記事から):
私はそれを私の10億ドルの間違いと呼んでいます。それは1965年のnull参照の発明でした。当時、私はオブジェクト指向言語(ALGOL W)で参照するための最初の包括的な型システムを設計していました。私の目標は、コンパイラーによって自動的にチェックが実行され、参照のすべての使用が完全に安全であることを確認することでした。しかし、実装が非常に簡単だったという理由だけで、null参照を入れたいという誘惑に抵抗できませんでした。これにより、無数のエラー、脆弱性、およびシステムクラッシュが発生し、過去40年間でおそらく10億ドルの苦痛と損害が発生しました。ここで興味深いのは、トニーがその参照を簡単に実装できるように誘惑されたことです。しかし、なぜ彼はそのような参照を必要としたのですか?
NULLのさまざまな意味
完璧な世界では、NULLは必要ありません。すべての人には名と姓があります。すべての人に誕生日や仕事などがあります。それとも彼らですか?
残念ながら、そうではありません。
すべての国が姓名の概念を使用しているわけではありません。
すべての人が仕事をしているわけではありません。あるいは、彼らの仕事がわからないこともあります。または私たちは気にしません。
これは、NULLが非常に役立つ場所です。 NULLは、実際にはモデル化したくないこれらすべての状態をモデル化できます。 NULLは次のようになります:
- 「未定義」の値 つまり、(おそらく技術的な理由で)まだ定義されていないが、後で定義される可能性のある値。他のテーブルで使用するためにデータベースに追加したい人について考えてみてください。後の段階で、その人の仕事を追加します。
- 「不明な」値 、つまり、私たちが知らない(そして決して知らないかもしれない)値。おそらく、この人やその親戚に生年月日を尋ねることはできなくなります。情報は永久に失われます。ただし、それでも人物をモデル化する必要があるため、UNKNOWNの意味でNULLを使用します(これは、後で説明するSQLでの真の意味です)。
- 「オプションの」値 つまり、定義する必要のない値です。 「オプション」の値は、外部結合の場合、外部結合が関係の片側に値を生成しない場合にも表示されることに注意してください。または、GROUP BY列のさまざまな組み合わせが組み合わされる(または空のままになる)GROUPINGSETSを使用する場合も同様です。
- 「削除された」または「回避された」値 つまり、指定したくない値です。おそらく、私たちは通常、一部の法域で行われているように人の結婚状況を登録しますが、このタイプの個人データを登録することが合法ではない他の法域では登録しません。したがって、場合によってはこの値を知りたくありません。
- 特定のコンテキストにおける「特別な」値 つまり、可能な値の範囲内で他の方法ではモデル化できない値です。これは、日付範囲を操作するときによく行われます。ある人の仕事が2つの日付で制限されていると仮定します。その人が現在そのポジションで働いている場合、NULLを使用して、日付範囲の終わりで期間が制限されていないことを示します。
- 「偶発的な」NULL つまり、開発者が注意を払っていなかったためにNULLであるNULL値です。明示的なNOTNULL制約がない場合、ほとんどのデータベースは列がNULL可能であると想定します。また、列がnull許容になると、開発者は「誤って」行にNULL値を入れてしまう可能性がありますが、意図していませんでした。
上で見たように、これらは 50 Shades of NULLのほんの一部です。 。
次の例は、具体的なSQLの例でNULLのさまざまな異なる意味を示しています。
CREATE TABLE company ( id int NOT NULL, name text NOT NULL, CONSTRAINT company_pk PRIMARY KEY (id) ); CREATE TABLE job ( person_id int NOT NULL, start_date date NOT NULL, -- If end_date IS NULL, the “special value” of an unbounded -- interval is encoded end_date date NULL, description text NOT NULL, -- A job doesn’t have to be done at a company. It is “optional”. company_id int NULL, CONSTRAINT job_pk PRIMARY KEY (person_id,start_date), CONSTRAINT job_company FOREIGN KEY (company_id) REFERENCES company (id) ); CREATE TABLE person ( id int NOT NULL, first_name text NOT NULL, -- Some people need to be created in the database before we -- know their last_names. It is “undefined” last_name text NULL, -- We may not know the date_of_birth. It is “unknown” date_of_birth date NULL, -- In some situations, we must not define any marital_status. -- It is “deleted” marital_status int NULL, CONSTRAINT person_pk PRIMARY KEY (id), CONSTRAINT job_person FOREIGN KEY (person_id) REFERENCES person (id) );
人々は常に価値の欠如について議論してきました
NULLがそのような有用な値であるのに、なぜ人々はそれを批判し続けるのですか?
NULL(およびその他)のこれらの以前の使用例はすべて、C.J。Dateによる「TheProblem of Missing Information」(YouTubeのビデオを見る)に関するこの興味深い最近の講演に示されています。
最新のSQLは、Java、C#、PHPなどの汎用言語の開発者が気付いていない多くの素晴らしいことを実行できます。さらに下の例を示します。
ある意味で、C.J。DateはTony Hoareに同意し、(ab)これらのさまざまなタイプの「欠落している情報」すべてにNULLを使用することは非常に悪い選択です。
たとえば、電子機器では、1、0、「競合」、「未割り当て」、「不明」、「ドントケア」、「高インピーダンス」などのモデルに同様の手法が適用されます。ただし、電子機器では、さまざまな特別な値に注意してください。 これらには、単一の特別なNULL値ではなく使用されます。 。これは本当に良いですか? JavaScriptプログラマーは、「null」、「undefined」、「0」、「NaN」、空の文字列「」など、さまざまな「偽の」値の違いについてどのように感じていますか?これは本当に良いですか?
ゼロと言えば、SQLスペースを少し離れて数学に入ると、ローマ人やギリシャ人のような古代の文化でも、ゼロという数字で同じ問題が発生していることがわかります。実際、ゼロについてのWikipediaの記事に見られるように、他の文化とは異なり、ゼロを表す方法すらありませんでした。記事からの引用:
記録によると、古代ギリシャ人は数字としてのゼロの状態について確信が持てないようでした。彼らは、「どうして何も何かになり得ないのか」と自問し、哲学的に、そして中世までに、ゼロと真空の性質と存在についての宗教的な議論につながりました。ご覧のとおり、「宗教的な議論」は明らかにコンピュータサイエンスとソフトウェアにまで及びますが、価値がない場合にどうしたらよいかまだわかりません。
現実に戻る:SQLではNULL
人々(学者を含む)は、「未定義」、「不明」、「オプション」、「削除済み」、「特別」のエンコーディングが必要かどうかについてまだ同意していませんが、現実と悪い部分に戻りましょう。 SQLのNULL。
SQLのNULLを処理するときに忘れられがちなことの1つは、UNKNOWNケースを正式に実装することです。これは、いわゆる3値論理の一部である特別な値であり、一貫性のない方法で実行されます。 UNIONまたはINTERSECT操作の場合。
モデルに戻ると:
たとえば、結婚していると登録されていないすべての人を直感的に見つけたい場合は、次のステートメントを記述します。
SELECT * FROM person WHERE marital_status != 'married'
残念ながら、3値論理とSQLのNULLのため、上記のクエリは明示的な結婚状況を持たない値を返しません。したがって、追加の明示的な述語を記述する必要があります:
SELECT * FROM person WHERE marital_status != 'married' OR marital_status IS NULL
または、比較する前に値をNOTNULL値に強制します
SELECT * FROM person WHERE COALESCE(marital_status, 'null') != 'married'
3値論理は難しいです。また、SQLでのNULLの問題はこれだけではありません。 NULLを使用することのその他の欠点は次のとおりです。
- 実際にいくつかの異なる「存在しない」または「特別な」値をエンコードしたい場合、NULLは1つだけです。有用な特殊値の範囲は、使用されるドメインとデータ型に大きく依存します。ただし、null許容列の意味を正しく解釈するにはドメイン知識が常に必要であり、上記で見たように、間違った結果が返されないようにクエリを慎重に設計する必要があります。
- 繰り返しになりますが、3値論理を正しく理解するのは非常に困難です。上記の例はまだかなり単純ですが、次のクエリで何が得られると思いますか?
SELECT * FROM person WHERE marital_status NOT IN ('married', NULL)
その通りです。この記事で説明されているように、何も生成されません。つまり、上記のクエリは以下のクエリと同じです。
SELECT * FROM person WHERE marital_status != 'married' AND marital_status != NULL -- This is always NULL / UNKNOWN
-
Oracleデータベースは、NULLと空の文字列''を同じものとして扱います。次のクエリが常に空の結果を返す理由にすぐには気付かないため、これは非常に注意が必要です。
SELECT * FROM person WHERE marital_status NOT IN ('married', '')
-
Oracleは(再び)インデックスにNULL値を入れません。これは、多くの厄介なパフォーマンスの問題の原因です。たとえば、NOTIN述語でnull許容列を使用している場合:
SELECT * FROM person WHERE marital_status NOT IN ( SELECT some_nullable_column FROM some_table )
Oracleでは、some_nullable_columnにインデックスがあるかどうかに関係なく、上記の反結合により全表スキャンが実行されます。 3値論理のため、およびOracleはインデックスにNULLを入れないため、エンジンはテーブルにアクセスしてすべての値をチェックし、セットに少なくとも1つのNULL値がないことを確認する必要があります。述語全体が不明です。
結論
ほとんどの言語とプラットフォームでは、まだNULLの問題を解決していません。 NULLはTonyHoareが謝罪しようとする10億ドルの間違いではないと私は主張しますが、NULLは確かに完璧にはほど遠いです。
データベース設計を安全に保ちたい場合は、NULLを使用してエンコードするためにこれらの特別な値のいずれかが絶対に必要でない限り、NULLを絶対に避けてください。これらの値は、「undefined」、「unknown」、「optional」、「deleted」、「special」などです。 The 50 Shades of NULL 。このような状況にない場合は、常にデフォルトでデータベースのすべての列にNOTNULL制約を追加します。デザインがよりクリーンになり、パフォーマンスが大幅に向上します。
DDLのデフォルトがNOTNULLであり、明示的に設定する必要のあるキーワードがNULLABLEである場合…
NULLについてのあなたの見解と経験は何ですか?あなたの意見では、より良いSQLはどのように機能しますか?
Lukas Eder スイスのチューリッヒにあるDataGeekeryGmbHの創設者兼CEOです。 Data Geekeryは、2013年からJavaとSQLを中心にデータベース製品とサービスを販売しています。
2006年にEPFLで修士号を取得して以来、彼はJavaとSQLの相互作用に魅了されてきました。この経験のほとんどは、スイスのEバンキングの分野で、さまざまなバリエーション(JDBC、Hibernate、主にOracleを使用)を通じて得たものです。彼は、さまざまな会議、JUG、社内プレゼンテーション、および彼の会社のブログでこの知識を喜んで共有しています。