IS NULL / IS NOT NULL の使い方
shain社員
| name | busho_id |
|---|---|
| 大野 | NULL |
| 渡辺 | NULL |
| 中村 | 2✕ |
| 井上 | 7✕ |
| … | |
IS NULL値が無い行だけ
| name | busho_id |
|---|---|
| 大野 | NULL |
| 渡辺 | NULL |
WHERE busho_id IS NULL
- 取り消し線= 条件に合わないので結果に出てこない行
- 社員 18 行 → 値が無い行だけ 2 行(16 行が消えます)
`busho_id = NULL` と書くと、この 2 行すら返りません(0 行になります)。
`NULL` は「値が無い」ではなく「値が分からない」という意味です。 分からないものを = で比べても答えは出ません。だから `= NULL` は動かず、`IS NULL` を使います。この一点を押さえると、NULL 絡みの事故はほぼ防げます。
主要 7 製品で通用 そのまま使える: PostgreSQL / MySQL / MariaDB / SQLite / SQL Server / Oracle Database / IBM Db2
このページの実行結果と図は、すべて本物のデータベースに同じ SQL を流して得たものです。手で書いた「たぶんこうなる」ではありません。
01真・偽のほかに「どちらとも言えない」がある
SQL の条件は、真・偽の 2 つではなく 真・偽・unknown(どちらとも言えない)の 3 つになります。これを三値論理といいます。
NULL が絡む比較は、ほぼすべて unknown になります。NULL = 1 も NULL <> 1 も NULL = NULL も、答えは unknown です。「分からない値」と何かを比べても、分からないままだからです。
`WHERE` は真になった行だけを残します。 unknown の行は残りません。ここが事故の源です。
SELECT name, busho_id何を出すかFROM shainどの表を見るかWHERE busho_id IS NULL値が入っていない行だけ残すORDER BY shain_id並び順
「値が入っている行」なら IS NOT NULL。書き方はこの 2 つだけで、= NULL は使いません(エラーにならず黙って 0 行を返すので、間違いに気づけません)。
SELECT
(NULL = NULL) AS null_eq_null,
(NULL <> 1) AS null_ne_1,
(NULL IS NULL) AS null_is_null;| null_eq_null | null_ne_1 | null_is_null |
|---|---|---|
| NULL | NULL | true |
(1 行)
はじめの 2 つは true でも false でもなく、空欄(unknown)です。IS NULL だけが true を返します。
02⚠️ よくある間違い:NOT IN は NULL が 1 つ混ざると全滅する
これは NULL 絡みで最も痛い罠です。
x NOT IN (1, 2, NULL) は「x は 1 でもなく 2 でもなく NULL でもない」という意味です。ところが「x が NULL ではない」は unknown なので、全体が真になりません。結果は常に 0 行です。
副問い合わせの結果に NULL が 1 つでも混ざっていると、これが起こります。件数が 0 になって初めて気づく、という形で表面化します。
対処は 2 つあります。NOT EXISTS に書き換えるか、副問い合わせ側で IS NOT NULL を足すかです。NOT EXISTS のほうが安全なので、迷ったらこちらを使ってください。
SELECT name
FROM shain
WHERE busho_id NOT IN (SELECT busho_id FROM shain WHERE taishoku_on IS NOT NULL);| name |
|---|
| 鈴木 |
| 佐藤 |
| 田中 |
| 中村 |
| 小林 |
| 加藤 |
| 吉田 |
| 井上 |
| 木村 |
(9 行)
「退職者がいる部署以外」を出したかったのに 0 行です。副問い合わせの結果に NULL が混ざっているためです。
SELECT s.name
FROM shain s
WHERE NOT EXISTS (
SELECT 1 FROM shain t
WHERE t.taishoku_on IS NOT NULL AND t.busho_id = s.busho_id
)
ORDER BY s.shain_id;| name |
|---|
| 鈴木 |
| 佐藤 |
| 田中 |
| 中村 |
| 小林 |
| 加藤 |
(11 行 … うち先頭 6 行を表示)
03並べ替えると NULL は端に寄る
ORDER BY では NULL を「いちばん大きい値」として扱うか「いちばん小さい値」として扱うかが製品によって違います。
PostgreSQL は昇順のとき NULL を最後に置きます。位置を自分で決めたいときは `NULLS FIRST` / `NULLS LAST` と書きます。
ただしこの書き方に対応していない製品があります。どこでも動かしたいなら、CASE で「NULL なら 1、それ以外は 0」という列を作って並べる方法があります。
SELECT name, busho_id
FROM shain
ORDER BY busho_id NULLS FIRST, shain_id;| name | busho_id |
|---|---|
| 渡辺 | NULL |
| 大野 | NULL |
| 鈴木 | 1 |
| 佐藤 | 2 |
| 田中 | 2 |
(18 行 … うち先頭 5 行を表示)
⚠️ この書き方は MySQL / MariaDB / SQL Server では使えません(ORDER BY ... NULLS LAST)。製品ごとの対応表
04COALESCE で「代わりの値」に置き換える
画面に出すときは、NULL のままだと空欄になって意味が伝わりません。`COALESCE(列, 代わりの値)` を使うと、NULL のときだけ別の値にできます。
引数は 3 つ以上書けます。左から順に見て、最初に `NULL` でなかったものが返ります。
⚠️ 置き換えるのは表示のときだけにしてください。集計の途中で COALESCE(salary, 0) のように 0 を入れると、平均が実際より低く出ます(次の節)。
SELECT name, COALESCE(taishoku_on::text, '在籍中') AS jotai
FROM shain
ORDER BY shain_id;| name | jotai |
|---|---|
| 鈴木 | 在籍中 |
| 佐藤 | 在籍中 |
| 田中 | 在籍中 |
| 中村 | 在籍中 |
| 小林 | 在籍中 |
(18 行 … うち先頭 5 行を表示)
05⚠️ よくある間違い:集約関数は NULL を数えない
count(列) sum() avg() は、`NULL` の行を最初から無かったものとして扱います。
つまり avg(列) の分母は「値が入っている行の数」であって、全体の行数ではありません。ここを勘違いすると、平均が想定と食い違います。
「NULL を 0 とみなして平均したい」なら、COALESCE で明示的に 0 にしてから平均します。どちらが正しいかは要件次第なので、無意識に決めないでください。
行数そのものを数えたいときは count(*) です。こちらは NULL に関係なく行を数えます。
SELECT
count(*) AS zen_gyosu,
count(taishoku_on) AS chi_ari,
avg(salary)::numeric(10,0) AS heikin
FROM shain;| zen_gyosu | chi_ari | heikin |
|---|---|---|
| 18 | 2 | 542003 |
(1 行)
count(*) と count(taishoku_on) の差が「NULL の行数」です。
自分で打ってみる
このページの例と同じデータが入った本物のデータベースを、この場(あなたのブラウザの中)で起動できます。打った SQL がサーバーへ送られることはありません。
押すと初回だけ約 9MB を読み込みます(2 回目以降はキャッシュされます)。
解いてみる
読んだだけでは書けるようになりません。実行結果で採点します。
関連するトピック
- WHERE 句NULL の判定は WHERE でいちばん事故が多いところです。
- LEFT / RIGHT OUTER JOIN外部結合の結果に出る NULL は「相手がいなかった」という意味です。
- NULL を扱う関数(練習問題)COALESCE など、NULL を別の値に置き換える関数。
- 集約関数(練習問題)集約関数が NULL をどう扱うか。
根拠(一次情報)
- PostgreSQL 18 マニュアル: 比較関数と演算子(IS NULL・三値論理)一次情報・確認 2026-07-28
- PostgreSQL 18 マニュアル: 集約関数(NULL の扱い)一次情報・確認 2026-07-28
- SQLite: NULL Handling(三値論理の扱い)一次情報・確認 2026-07-28