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 型の比較表](http://php.net/manual/ja/types.comparisons.php)

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

```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;
```

結果

```bash
$ 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 してみる。

```sql
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" とかが混じってバグってるとか普通にありそうで、見直したくない。