NULLを理解してSQLマスターになろう

# NULL
無記入状態であることを表すキーワード
決してNULLという値が存在するわけではない
NULLは定義域に含まれていない

DBMSではNULLの扱いには注意が必要である
NULLが含まれている場合は、挙動がイメージと乖離する場合が多い

# 3値論理
SQLでは真理値は３つある
1. TRUE
1. FALSE
1. UNKNOWN

UNKNOWNとは不明という意味である。
そしてSQLではこのUNKNOWNをNULLとして表現している。

# 3値論理の真理値表

**OR**
| P | Q | P OR Q |
| ---- | ---- | ---- |
| T | T | T |
| T | F | T |
| T | U | T |
| F | T | T |
| F | F | F |
| F | U | U |
| U | T | T |
| U | F | U |
| U | U | U |

TRUE > UNKNOWN > FALSE という関係が成り立つ。
つまり１つでもTRUEがあればTRUEとなる。
FALSEだけの場合、FALSEとなる。

**AND**
| P | Q | P OR Q |
| ---- | ---- | ---- |
| T | T | T |
| T | F | F |
| T | U | U |
| F | T | F |
| F | F | F |
| F | U | F |
| U | T | U |
| U | F | F |
| U | U | U |

FALSE > UNKNOWN > TRUE という関係が成り立つ。
つまり１つでもFALSEがあれば、FALSEとなる。
TRUEだけの場合、TRUEとなる。

# NULLかどうかの判定
#### col1がNULLの場合
```sql
col1 IS NULL
```
TRUE

#### col1がNULLでない場合
```sql
col1 IS NULL
```
FALSE

# NULLと述語比較
NULLとの比較では必ずNULLが返却される。
NULLは値でも変数でもなく無記入状態(=値が無い)である。
比較述語を適用できるのは値だけであるため、例外としてNULLが返却される。

以下の式は全てNULLに評価される
```
1 = NULL
2 > NULL
3 < NULL
4 <> NULL
NULL = NULL
NULL > NULL
NULL < NULL
NULL <> NULL
```

# NULLを含む場合の挙動
## WHERE句
NULLを含むカラムを述語比較を用いてWHERE句の条件に指定したときのことを考える。
WHERE句はレコードごとに評価される。
そのためNULLが含まれないレコードにおいてはTRUEが返された場合、そのレコードの処理を行い、それ以外が返されたレコードはスキップされる。
つまりFALSE, NULLが返されたレコードはスキップされる。

## CASE式
これも同じくCASE式では判定結果がTRUEと評価された場合のみ有効となる。
そのため以下の式では絶対に0が返ることは無い。
```sql
CASE
    WHEN col = 1 THEN 1
    WHEN col = NULL THEN 0
END
```

正しい形は以下。
```sql
CASE
    WHEN col = 1 THEN 1
    WHEN col IS NULL THEN 0
END
```

## NOT INとNOT EXISTS
#### NOT IN
NOT INを含むクエリがどう展開されるかを考える。
```sql
SELECT
    *
FROM
    TABLE1
WHERE
    col1
NOT IN
    (
    SELECT
        col2
    FROM
        TABLE2
    WHERE
        col3 = 'XXX'
    )
;
```

サブクエリが実行されることでリストを取得するが、この中にNULLが含まれている。
```sql
SELECT
    *
FROM
    TABLE1
WHERE
    col1
NOT IN ('A', 'B', NULL)
;
```

INはORと同値である。またWHERE NOT ORはドモルガンの法則でAND <>に変換される。
```sql
SELECT
    *
FROM
    TABLE1
WHERE
    col1 <> 'A'
AND
    col1 <> 'B'
AND
    col1 <> NULL
;
```
AND演算にNULLが含まれている場合、TRUEにはならない。
WHERE句が全てFALSEかNULLとなり、このクエリからは何も返されない。

#### NOT EXISTS
```sql
SELECT
    *
FROM
    TABLE1 AS T1
WHERE
NOT EXSITS
    (
    SELECT
        *
    FROM
        TABLE2 AS T2
    WHERE
        T1.col1 = T2.col2
    ANDD
        col3 = 'XXX'
    )
;
```

col2 = NULL のレコードについて、クエリがどのように展開されるか考える。

```sql
SELECT
    *
FROM
    TABLE1 AS T1
WHERE
NOT EXSITS
    (
    SELECT
        *
    FROM
        TABLE2 AS T2
    WHERE
        T1.col1 = NULL
    ANDD
        col3 = 'XXX'
    )
;
```

AND演算にNULLが含まれるとNULLとなる。
```sql
SELECT
    *
FROM
    TABLE1 AS T1
WHERE
NOT EXSITS
    (
    SELECT
        *
    FROM
        TABLE2 AS T2
    WHERE FALSE または UNKNOWN
    )
;
```

EXISTSはTRUEかFALSEしか返さないので、FALSEを返す。

```sql
SELECT
    *
FROM
    TABLE1 AS T1
WHERE
NOT FALSE (=TRUE)
;
```

結果としてNULLの行もreturnされる。


# COALESCE関数
SQL独特の関数で、NULLを扱う

## 仕様
引数を左から順番にチェックしていき、最初にNULLではない値を返す
ただし全ての値がNULLだった場合はNULLを返す
SQL文の中で、NULLを何か別の値に変えて扱いたい場合に便利

## 構文
```sql
COALESCE(data1, data2, data3, ...)
```

## 例
#### ABCを返す場合
```sql
COALESCE(NULL, 'ABC', NULL, 'DEF', 'HIJ')
```

#### DEFを返す場合
```sql
COALESCE(NULL, NULL, NULL, 'DEF', 'HIJ')
```

#### HIJを返す場合
```sql
COALESCE(NULL, NULL, NULL, NULL, 'HIJ')
```

最後の引数にデフォルト値を仕込んでおくという使い方はよく見られる。