1. ホーム
  2. データベース
  3. エムエスエル

SQLステートメントにおけるNULL値の扱い方

2022-01-06 22:32:40

日常的にデータベースを利用する中で、NULL値を気にすることはないでしょうか。

実は、NULL値はデータベースにおいて非常に特殊で興味深い存在なので、見ていきましょう。

データベースに問い合わせる際、ある列(例えばユーザー登録年USER_AGE)がNULLかどうかを知りたい場合、SQLの問い合わせ文はどのように書くのでしょうか。

次のようになります。

SELECT * FROM TABLE WHERE USER_AGE = NULL

それともあれか?

SELECT * FROM TABLE WHERE USER_AGE IS NULL

もちろん、正しい書き方は2番目のもの(WHERE USER_AGE IS NULL)でしょう。

しかし、なぜこのような書き方をするのでしょうか?データベースのデータを比較するときに、"IS"というキーワードは使いませんよね?

例えば、あるカラムの値が1に等しいかどうかを知りたい場合、WHERE文は次のようになります。

WHERE USER_AGE = 1

では、なぜNULL値に対してISキーワードを使用するのでしょうか。なぜNULLをこのように扱わなければならないのでしょうか?

なぜなら、SQLでは、NULLは"unknown"を意味するからです。つまり、NULL値はquot;unknown"な値を表しています。

NULL=不明です。

ほとんどのデータベースでは、NULlと空の文字列は異なります。

例えば、Oracleは空の文字列をサポートしておらず、自動的にNULL値に変換してしまいます。

他のほとんどのデータベースでは、NULL 値と文字列は異なる方法で扱われます。

  • ヌル("")文字列は、"値なし"を意味しますが、値は知られています。
  • NULLは"unknown value"を意味し、値が不明であることを示します。

他の多くのデータベースが異なる扱いをするのに対し、Oracleはどちらの値もNULLを使って表現するという点で特別です。

しかし、NULLが未知の値を表すということさえ覚えておけば、SQLのクエリ文は簡単に書けるようになります。

例えば、このようなクエリ文があった場合。

SELECT * FROM SOME_TABLE WHERE 1 = 1

この問い合わせは、式 "1 = 1" が真でなければならないため、全ての行を返します(SOME_TABLEが空のテーブルではないと仮定しています)。

このように書くと

SELECT * FROM SOME_TABLE WHERE 1 = 0

式 "1 = 0" は偽で、このクエリ文はいかなるデータも返しません。

しかし、このように書くと

SELECT * FROM SOME_TABLE WHERE 1 = NULL

この時点で、データベースは2つの値(1とNULL)が等しいかどうか分からないので、"NULL" または "unknown" と仮定し、いずれのデータも返しません。

3項論理

SQLクエリ文のWHEREは、一般的に3つの結果を持ちます。

  • 真になることがあります(この時点でデータが返されます)。
  • はfalseにすることができます(この時点ではデータは返されません)。
  • は、NULL または unknown にすることもできます (この時点ではデータは返されません)。

それなら、false や NULL を気にする必要はないじゃないか、どちらもデータを返さないじゃないか、と思うかもしれません。

次に、NOT( )メソッドを見て、問題があるところを紹介します。

このようなクエリ文があったとします。

SELECT * FROM SOME_TABLE WHERE NOT(1 = 1)

データベースは、まず1=1を計算しますが、これは明らかに真です。

次に、データベースはNOT()条件を適用するので、WHEREは偽を返します。

ですから、上のクエリはデータを返しません。

しかし、この文に変更すると

SELECT * FROM SOME_TABLE WHERE NOT(1 = 0)

データベースはまず1=0を計算するので、これは偽でなければならない。

次に、データベースは反対の結果を与えるNOT()条件をtrueに適用します。

つまり、この文はデータを返すのです。

しかし、この文をもう一度、次のように変えてみたらどうでしょうか。

SELECT * FROM SOME_TABLE WHERE NOT(1 = NULL)

データベースはまず1 = NULLを計算しますが、NULLの値がわからないので、1がNULLと等しいかどうかはわかりません。

つまり、この計算ではtrueもfalseも返さず、NULLが返されるのです。

次に、NOT() は、前の計算で返された結果の解析を続けています。

NOT() が NULL に遭遇すると、別の NULL を生成します。未知数の反対は別の未知数です。

つまり、どちらのクエリでも

SELECT * FROM SOME_TABLE WHERE NOT(1 = NULL)
 
SELECT * FROM SOME_TABLE WHERE 1 = NULL

どちらも正反対なのに、データを返しません。

NULLとNOT IN

もし、こんなクエリ文があったら。

SELECT * FROM TABLE WHERE 1 IN (1, 2, 3, 4, NULL)

明らかに、WHEREは真を返し、この文は括弧付きのリストに1が存在するので、データを返すことになります。

しかし、このように書くと

SELECT * FROM SOME_TABLE WHERE 1 NOT IN (1, 2, 3, 4, NULL)

明らかに、WHEREはfalseを返し、このクエリは1が括弧付きのリストに入っているためデータを返しません。

しかし、この文をこう変えてみるとどうでしょう。

SELECT * FROM SOME_TABLE WHERE 5 NOT IN (1, 2, 3, 4, NULL)

このWHEREはtrueを返さないので、データを返しません。5という数字は、NULL値があるので、括弧付きのリストに存在するかどうかわかりません(データベースはNULL値が何であるかを知りません)。

このWHEREはNULLを返すので、クエリ全体では何のデータも返されません。

SQL文でNULL値を扱う方法について、ご理解いただけたと思います。

以上、SQL文におけるNULL値の扱い方について詳しく説明しました。SQLのNULL値についてもっと詳しく知りたい方は、スクリプトハウスの他の関連記事もご覧ください