LoginSignup
5
3

More than 3 years have passed since last update.

何故、「where x = null」,「x <> null」は0件になるのか

Last updated at Posted at 2019-05-23

何故、NULL は = で比較したらあかんのか

新人さんたちは、不思議な値「NULL」の概念が上手く理解できず、よく躓いてしまいます。
特に、教えていてアプローチに悩むのが、SQLの「IS NULL」という比較構文です。
私も、理解していない点が多くあったので、わからない点を纏めてみました。

やさしい上級者の方は、恐れ入りますが間違いがあったらご指摘頂けますと幸いです…

経緯

教育を行っていた際、"where x = null"と記述した際、0件なのであれば、
逆となる条件の"where x <> null"はなぜ全件じゃないのか?と聞かれたことが発端でした。

そこで、3値論理というワードを全く知らなかった自分のため、
また、NULLとは何ぞやといった点をわかりやすく教えるため、記述しました。

~(おさらい)本題に行く前に、WHEREってなんだっけ~

SQLの概念と、SELECT,FROM,WHEREの概念を簡単に理解している前提で書きます。

WHEREはDBに保有しているデータに、条件と一致するデータか~と質問する為の構文です。

「あなたは15歳以上ですか?」と10人から確認したい場合、
10人に質問しなければいけませんよね。

DBも同じで、10行(レコード)あった場合、1件づつ、合計10回問いかけを行っています。
いくつか、想定動作と結果をイメージしながら考えてみましょう。

hogeテーブル

id name age
1001 たろう 10
1002 じろう 15
1003 NULL 20

例えば、hogeテーブルから、年齢が15歳以上のデータ(レコード)を取得したいとします。

SQL記述例:

SQL*Plus
--※1のSQL例
SQL> SELECT *
  1> FROM hoge
  2> WHERE age >= 15;

上記のSQLを記述した場合、各行で下記のような計算が行われます。

id name age 計算条件 結果
1001 たろう 10 10は15以上?
1002 じろう 15 15は15以上?
1003 NULL 20 20は15以上?

DBは真の計算結果となるものを返すので、
1002,1003の2件を取得します。

また、新人さんが混乱しやすい問題として、このようなSQLがあります。

SQL*Plus
--※1のSQL例
SQL> SELECT *
  1> FROM hoge
  2> WHERE 1 = 1;

よく、1=1ってなんだ?って身構えられるのですが、
単純に「1と1」を全レコードで比較しているだけです。

id name age 計算条件 結果
1001 たろう 10 1は1?
1002 じろう 15 1は1?
1003 NULL 20 1は1?

この場合、1001,1002,1003の3件が返却されます。

~(おさらい)WHEREでnullを=で比較するとどうなるんだっけ~

SQLで例えば、[カラム名] = NULLのように比較演算子を利用し、NULLと比較をした場合、
カラムにNULLが入っているレコードがあった場合でも、結果は0件です。

SQL*Plus
--※1のSQL例
SQL> SELECT *
  1> FROM hoge
  2> WHERE name is NULL;

ID   NAME
---- -----
1003 NULL

--※2のSQL例
SQL> SELECT * FROM hoge
  2> WHERE name = NULL;

レコードが選択されませんでした。

また、BETWEENを利用した場合についても、同様の動作をします。

SQL*Plus
--※SQL例
SQL> SELECT * FROM hoge
  2> WHERE name BETWEEN 0 and NULL;

--ANDに置き換えて記述した場合(上記と同等のSQL)
SQL> SELECT * FROM hoge
  2> WHERE name >= 0 and name <=NULL;

レコードが選択されませんでした。

(NOTをつけない)INの場合、ORでの探索条件となるので、
NULL値は出ませんが、他の比較値(下記例だと1001のこと)は正しく取得出来ます。

SQL*Plus
--このSQLは1003のNULLさんは対象外となる
SQL> SELECT * FROM hoge
  2> WHERE name IN (NULL,1001);

ID   NAME
---- -----
1003 NULL

--INで記載されている内容と同等のSQL
SQL> SELECT * FROM hoge
  2> WHERE name = NULL -- NULL値を=で比較するため、こちらの比較結果は0件
  3> OR name = "たろう"; -- 「たろう」に合致するレコードはあるので、1件返却される

ID   NAME
---- -----
1001 たろう

問題提起(なんで「カラム名 <> null」は0件なのか)

新人さんが、この結果はおかしい、バグってる!と騒いでいました。

SQL*Plus

SQL> SELECT * FROM hoge
  2> WHERE NOT(name = NULL);

レコードが選択されませんでした。

主張としては、[カラム名] = NULLが0件なのであれば、
その否定は全件なのではないかといったお話でした。
730d57244fcbf5325b5ee094b3c78eea.png

私もこの主張は正しいな…と感じてしまったので調べました。
どうやら、3値論理という概念が深く関係しており、まず3値論理を理解しなければわからないようです。

3値論理とは

3値論理とは真偽値をTRUE,FALSEの2つに加えて、UNKNOWN(不明)という3つの値で管理する方法です。1

ここでいう、UNKNOWNとはどういったデータかというと、おおまかに下記の2種類に分類されます。

①「家の総移動距離」 や 「モグラの飛行時間」のような、論理的に存在できない値
※ハウルの動く城とか、タイヤの付いた家、飛ぶモグラ等の
 前提になると話がすごくややこしくなるので、一般的な概念で考えて下さい…

②「タモリの目の色」 や 「星の数」 や 「一部アイドルの実年齢」のような、わからない(観測できない)値
※こちらも、「タモリ サングラス 外す」等で検索すると画像が出てきますが、2
 そんな画像はない前提でお願いします…

NULL値は、TRUEでもなく、FALSEでもなく、このUNKNOWNという第3値に分類されます。

UNKNOWNを比較してみよう

上記が理解できると、なんとなく分かる人もいるかもしれませんが、

WHERE 星の数 = 100000000000000のような荒唐無稽な質問は
もしかしたらTRUEかもしれませんし、FALSEかもしれませんが、わかりません!!というのが最適解になってしまいます。

星の数 計算条件 結果
NULL null は100000000000000? UNKNOWN

したがって、結果がTRUEではないため、返却されません。
逆にWHERE not(星の数 = 100000000000000)であってもこの結果はUNKNOWNです。

星の数 計算条件 結果
NULL null は100000000000000ではない? UNKNOWN

だってわからないんですもん。

おまけ

少し応用的な話になってしまいますが、NOT INの挙動を聞くと、
1回目は大体まちがって認識されているのですが、上記らへんを押さえていれば、実は簡単に解けます。

SQL*Plus

SQL> SELECT * FROM hoge
  2> WHERE name NOT IN(NULL,1001);

--同等のSQL
SQL> SELECT * FROM hoge
  2> WHERE name <> NULL 
  3> AND name <> 1001;

not in はその名の通り、羅列した要素のいずれとも合致していないという条件のため、
AND と<>で構成されることになります。

このとき、<> NULLは0件で、AND演算なので、必ず0件になります。


  1. https://codezine.jp/article/detail/532 

  2. タモリの目の色は何色なのか、画像から解析できませんでした。 

5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3