私はプログラミング言語におけるNULLは、「何もない値」または「何も示していない値」という定義で理解しています。 ところがSQLにおけるNULLでは、この単純な理解が通用しないことを知りました。
先に結論を言ってしまうと、「NULLとは不明な値のことである」という解釈で落ち着いたのですが。 これの何が問題なのか、解説します。
MySQLにおけるNULLとは、「存在しない不明な値」のことである
MySQLのリファレンスを読むと、NULLとは不明な値であることがわかります。
NULL値に慣れるまでは驚くかもしれません。
概念的には、NULLは「存在しない不明な値」を意味し、ほかの値とは多少異なる方法で扱われます。
https://dev.mysql.com/doc/refman/5.6/ja/working-with-null.html より
不明な値を直訳で解釈すると、「何が入っているのか分からない値」という意味になります。
NULLを算術(計算)で使うとNULLになる
一般的なプログラミング言語では、NULLを使った計算では警告や例外が発生するか、もしくは「0」という値に自動変換されて計算されます。
<?php $a = 10 - null; echo $a; // 10
ところがMySQLでは「10」にもならないし、エラーにもなりません。
mysql> SELECT 10 - NULL; +-----------+ | 10 - NULL | +-----------+ | NULL | +-----------+ 1 row in set (0.00 sec)
この挙動はPostgreSQLでも同様で、「10 - NULL」はNULLになります。
postgres=# \pset null '(null)' Null display is "(null)". postgres=# SELECT 10 - NULL; ?column? ---------- (null) (1 row)
NULLは算術比較できない
MySQLのリファレンスを読み進めていくと、算術比較はできませんと書いてあります。
=、<、または <> などの算術比較演算子を使用して NULL をテストすることはできません。 https://dev.mysql.com/doc/refman/5.6/ja/working-with-null.html より
つまりNULLは不明な値なので、算術比較の結果もまた、不明になってしまうのだろうと私は解釈しました。
NULLとなりえる値を、計算で使うのはやめよう
MySQLやPostgreSQLにおけるNULLの挙動を総括すると、たとえテーブル定義の型が整数型(INT型)であったとしても、「NOT NULL」制約のないカラムをSQLでそのまま計算に使わないほうが良いと言えます。
ただ計算に使えない数値というのも困りますので、対処法を考えてみます。 私が思いつく限り、対処法は大きく分けて2つあります。
テーブル定義(DDL)にNOT NULLを追加する
SQL上で計算に使う値であれば、テーブル定義を「NOT NULL DEFAULT 0」にして、必ず数値がある状態にします。
値が「0」であれば算術に使うことができるため、想定外の計算トラブルは抑制できます。
IFNULL()などのSQL関数を使って、NULL値を回避する
MySQLにはIFNULL()という、SQL関数があります。 IFNULLとは、「もしNULLであれば」を判定する関数です。
これを使って、NULLであれば「0」に変換して計算します。
mysql> SELECT 10 - IFNULL(NULL, 0); +----------------------+ | 10 - IFNULL(NULL, 0) | +----------------------+ | 10 | +----------------------+
テーブル定義で「NOT NULL」制約を使えないのであれば、NULL判定をしてデフォルト値を与えましょう。 NULLを回避することによって、計算結果がNULLになってしまう問題を防げます。
さいごに
MySQLやPostgreSQLにおけるNULLとは、特異な値であることを学びました。 だからMySQLのリファレンスにも、「NULL値に慣れるまでは驚くかもしれません。」と、あえて書いてあるのだと推測します。
なおデータベースにおけるNULLの扱い多くのRDBMSで共通ですが、Oracleだけは特別みたいです。
以上の NULL に関する事柄のほとんどは、PostgreSQL に限らず、多くの RDBMS で共通ですが、重要な例外が1つあります。
Oracleでは、NULL を含む文字列結合において NULL を返しません。
つまり select 'abc' || null from dual; の結果は abc になります。
https://oss-db.jp/dojo/dojo_08 より
本記事ではデータベースにおけるNULLを取り上げましたが、NULL値の挙動や扱い方は、プログラミング言語によっても変わります。 プログラム内でNULLを使うときは、「各プログラミング言語の参考書やリファレンスで、NULLの挙動を確認しておくのが良さそう」です。