例えば、students(学生)テーブルとenrollments(コース登録)テーブルを結合するとしよう。もしenrollmentsテーブルにある学生の情報がなかったとしても、例えばLEFT JOINを使えば、studentsテーブルの行はちゃんと出てくるけど、enrollmentsの情報は抜けてる。その場合、具体的なデータの代わりにNULLが出てくるんだ。
こんな感じになるよ:
studentsテーブル:
| id | name |
|---|---|
| 1 | Eva |
| 2 | Peter |
| 3 | Anna |
enrollmentsテーブル:
| student_id | course_name |
|---|---|
| 1 | 数学 |
| 1 | 情報科学 |
| 2 | 物理 |
LEFT JOINを使ったクエリ:
SELECT students.id, students.name, enrollments.course_name
FROM students
LEFT JOIN enrollments ON students.id = enrollments.student_id;
結果:
| id | name | course_name |
|---|---|---|
| 1 | Eva | 数学 |
| 1 | Eva | 情報科学 |
| 2 | Peter | 物理 |
| 3 | Anna | NULL |
やあ、NULL!見ての通り、Annaはどのコースにも登録してないから、コース情報がなくて、何もない代わりにNULLが出てくる。
NULLはクエリにどう影響する?
NULLは「ゼロ」でも「空文字」でもなくて、値がないってこと。これがいろいろ面白い(時にはイラっとする)影響をもたらすんだ:
NULLとの比較:
例えばWHERE course_name = NULLみたいに書いても、NULLの行は返ってこない。なぜかって?NULLは直接比較できないから。
NULLがあるかどうか調べるには、専用の演算子を使う必要がある:
WHERE course_name IS NULL
数学演算:
NULLを使った演算は全部NULLになる。例えば:
SELECT 5 + NULL; -- 結果: NULL
集約関数:
SUM()やAVG()みたいな集約関数はNULLを無視するけど、COUNT(*)は「存在する行」としてカウントするよ。
NULLとどう付き合う?
COALESCE()でNULLを分かりやすい値に置き換える
COALESCE()関数を使えば、NULLを他の値に変えられる。例えば、コースがなかったら"コースなし"って表示したいとき:
SELECT
students.id,
students.name,
COALESCE(enrollments.course_name, 'コースなし') AS course_name
FROM
students LEFT JOIN enrollments
ON students.id = enrollments.student_id;
結果:
| id | name | course_name |
|---|---|---|
| 1 | Eva | 数学 |
| 1 | Eva | 情報科学 |
| 2 | Peter | 物理 |
| 3 | Anna | コースなし |
これでだいぶ見やすくなったよね?
NULL値のフィルタリング
NULLの行を見たくないなら、WHERE ... IS NOT NULLを使えばOK。例えば:
SELECT
students.id,
students.name,
enrollments.course_name
FROM
students LEFT JOIN enrollments
ON students.id = enrollments.student_id
WHERE
enrollments.course_name IS NOT NULL;
結果:
| id | name | course_name |
|---|---|---|
| 1 | Eva | 数学 |
| 1 | Eva | 情報科学 |
| 2 | Peter | 物理 |
Annaは結果から消える。なぜなら彼女はコース登録がないから。
NULLを考慮したカウント:COUNTの例
さっきも言ったけど、関数によってNULLの扱いが違う。例えば:
全部の行をカウントしたい(course_nameがNULLでも):
SELECT COUNT(*) FROM students; -- 全部の行をカウント(`course_name` = NULLも含む)
NULLじゃない行だけカウントしたいとき:
SELECT COUNT(course_name) FROM enrollments;
CASEで条件分岐
COALESCE()がイマイチだったり、もっと柔軟にやりたいならCASEを使ってみて。例えば:
SELECT
students.id,
students.name,
CASE
WHEN enrollments.course_name IS NULL THEN 'コースなし'
ELSE enrollments.course_name
END AS course_name
FROM
students LEFT JOIN enrollments
ON students.id = enrollments.student_id;
結果はCOALESCE()と同じになるけど、CASEならもっと複雑なルールも書けるよ。
NULLが絶対にないならINNER JOINを使おう
一番シンプルにNULLを避ける方法は、そもそもNULLが出ないようにINNER JOINを使うこと。これは両方のテーブルにマッチする行だけ返すJOINだよ:
SELECT
students.id,
students.name,
enrollments.course_name
FROM
students INNER JOIN enrollments
ON students.id = enrollments.student_id;
サプライズなし!コース登録してる学生だけが出てくる。
結果:
| id | name | course_name |
|---|---|---|
| 1 | Eva | 数学 |
| 1 | Eva | 情報科学 |
| 2 | Peter | 物理 |
もし全部のデータ(NULLも含めて)を見せたいならINNER JOINは向いてないけど、必要なときはこれで十分!
GO TO FULL VERSION