PHP
MySQL
NULL

PHP, MySQL における NULL と 0

コードレビューをしていたところ、「こんなの初めてっ!」ってくらい大量の is_null() を見た。ところによっては empty() や isset() が使われているので、意識して使い分けてるのか?と思ったけど、どうやら気分だったようなので、以下のような点を説明して直してもらった。

PHP における NULL 判定

isset(), empty(), is_null() の比較

変数の値($var) isset($var) empty($var) is_null($var)
""(空文字) ture ture
" "(スペース) ture ture
FALSE ture ture
TRUE ture
array() (空配列) ture ture ture
NULL true
"0" (文字列の 0) true true
0 (整数の 0) true true
0.0 (実数の 0) true true
var $var (宣言されたがセットされていない変数) true true
NULL byte ("\0") true

単純に NULL かどうかを判定したい場合、
\$var !== null > !isset(\$var) > !empty(\$var) > !\$var > !is_null(\$var)
の順で遅くなっていく。is_null() は関数なので呼び出しのオーバーヘッドがあって遅い。
isset() がよく使われてるような気がするが、NULL 判定だけなら、!== を使うのが良い。

== と === の比較

==/=== TRUE FALSE 1 0 -1 "1" "0" "-1" NULL array() "php" ""
TRUE true/true true/false true/false true/false true/false true/false
FALSE true/true true/false true/false true/false true/false true/false
1 true/false true/true true/false
0 true/false true/true true/false true/false true/false true/false
-1 true/false true/true true/false
"1" true/false true/false true/true
"0" true/false true/false true/true
"-1" true/false true/false true/true
NULL true/false true/false true/true true/false true/false
array() true/false true/false true/true
"php" true/false true/false true/true
"" true/false true/false true/false true/true

あってるかな?

詳細は見にくいけど PHP 型の比較表

適当なプログラムで確認。

sample.php
<?php

$evaluateArray = array (NULL, 0, "0", "php");
$evaluateArrayDisplay = array ('NULL', '0',"\"0\"", "\"php\"");

foreach ($evaluateArray as $k => $var1) {
    foreach ($evaluateArray as $l => $var2) {
        if ($l > $k) {
            if ($var1 == $var2) echo "$evaluateArrayDisplay[$k] == $evaluateArrayDisplay[$l]\n";
            if ($var1 != $var2) echo "$evaluateArrayDisplay[$k] != $evaluateArrayDisplay[$l]\n";
            if ($var1 === $var2) echo "$evaluateArrayDisplay[$k] === $evaluateArrayDisplay[$l]\n";
            if ($var1 !== $var2) echo "$evaluateArrayDisplay[$k] !== $evaluateArrayDisplay[$l]\n";
        }
    }
}

exit;

結果

$ php --version
PHP 7.1.13 (cli) (built: Jan 25 2018 22:40:51) ( NTS )
Copyright (c) 1997-2017 The PHP Group
Zend Engine v3.1.0, Copyright (c) 1998-2017 Zend Technologies
    with Zend OPcache v7.1.13, Copyright (c) 1999-2017, by Zend Technologies


$ php sample.php
NULL == 0
NULL !== 0
NULL != "0"
NULL !== "0"
NULL != "php"
NULL !== "php"
0 == "0"
0 !== "0"
0 == "php"
0 !== "php"
"0" != "php"
"0" !== "php"

ん〜、0 キモい。

MySQL の場合

例えば、こんな Address テーブルがあったとして

id(int) pref_cd(int) city_cd(int) name(varchar) roman(varchar)
745 14 0 神奈川県 NULL
746 14 100 横浜市 yokohama
747 14 101 鶴見区 yokohama_tsurumi

色々 select してみる。

mysql> select version();
+------------+
| version()  |
+------------+
| 5.6.35-log |
+------------+
1 row in set (0.02 sec)

mysql> select * from address where city_cd = 0;
+-----+---------+---------+--------------+-------+
| id  | pref_cd | city_cd | name         | roman |
+-----+---------+---------+--------------+-------+
| 745 |      14 |       0 | 神奈川県     | NULL  |
+-----+---------+---------+--------------+-------+
1 row in set (0.00 sec)

mysql> select * from address where city_cd = '0';
+-----+---------+---------+--------------+-------+
| id  | pref_cd | city_cd | name         | roman |
+-----+---------+---------+--------------+-------+
| 745 |      14 |       0 | 神奈川県     | NULL  |
+-----+---------+---------+--------------+-------+
1 row in set (0.00 sec)

mysql> select * from address where city_cd = '';
+-----+---------+---------+--------------+-------+
| id  | pref_cd | city_cd | name         | roman |
+-----+---------+---------+--------------+-------+
| 745 |      14 |       0 | 神奈川県     | NULL  |
+-----+---------+---------+--------------+-------+
1 row in set (0.00 sec)

mysql> select * from address where city_cd = null;
Empty set (0.00 sec)

mysql> select * from address where city_cd is null;
Empty set (0.00 sec)

mysql> select * from address where isnull(city_cd);
Empty set (0.00 sec)

mysql> select * from address where roman = 0;
+-----+---------+---------+-----------+------------------+
| id  | pref_cd | city_cd | name      | roman            |
+-----+---------+---------+-----------+------------------+
| 746 |      14 |     100 | 横浜市    | yokohama         |
| 747 |      14 |     101 | 鶴見区    | yokohama_tsurumi |
+-----+---------+---------+-----------+------------------+
2 rows in set, 2 warnings (0.00 sec)

mysql> select * from address where roman = '0';
Empty set (0.00 sec)

mysql> select * from address where roman = '';
Empty set (0.00 sec)

mysql> select * from address where roman = null;
Empty set (0.00 sec)

mysql> select * from address where roman is null;
+-----+---------+---------+--------------+-------+
| id  | pref_cd | city_cd | name         | roman |
+-----+---------+---------+--------------+-------+
| 745 |      14 |       0 | 神奈川県     | NULL  |
+-----+---------+---------+--------------+-------+
1 row in set (0.00 sec)

mysql> select * from address where isnull(roman);
+-----+---------+---------+--------------+-------+
| id  | pref_cd | city_cd | name         | roman |
+-----+---------+---------+--------------+-------+
| 745 |      14 |       0 | 神奈川県     | NULL  |
+-----+---------+---------+--------------+-------+
1 row in set (0.00 sec)

PHP と MySQL で NULL や 0 の扱いが少し違う。
PHP に素で書いてある SQL とかあんまりレビューしたくないけど、クエリを作る際に、"= 0" とか "= null" とかが混じってバグってるとか普通にありそうで、見直したくない。