SQLを学び始めると、必ず出会うのが「NULL」という特別な値です。これはプログラミングにおける「null」や「None」とは少し異なる振る舞いをすることがあり、最初は戸惑うかもしれません。このセクションでは、SQLにおけるNULLの基本的な意味と、それを検索する方法について学びましょう!
NULLとは何か?
NULLは、データベースのテーブルにおいて「値が存在しない」または「不明である」ことを示す特別なマーカーです。 重要なのは、NULLは 0(ゼロ)や空文字列('')とは全く異なるということです。
- 0 (ゼロ): 数値としての「0」という具体的な値。
- 空文字列 (”): 長さ0の文字列データ。データ自体は存在します。
- NULL: データそのものが存在しない、または不明な状態。
例えば、ユーザー情報を管理するテーブルで、「誕生日」カラムがNULLになっている場合、それは「誕生日が1月0日」や「誕生日が空」という意味ではなく、「誕生日が登録されていない」または「誕生日が不明」ということを示します。
NULLの検索: IS NULL / IS NOT NULL
NULLを検索する際に、最も注意すべき点があります。それは、通常の比較演算子(=, <>, != など)ではNULLを正しく判定できないということです。
例えば、「emailカラムがNULLのユーザーを探したい」と思って次のようなクエリを書いたとします。
-- これは意図した通りに動作しません!
SELECT * FROM users WHERE email = NULL; このクエリは、emailカラムがNULLのユーザーを返してくれません。なぜなら、SQLにおいてNULLは「値ではない」ため、NULL = NULL という比較は常に「不明 (UNKNOWN)」と評価されるからです。同様に email <> NULL や email != NULL も意図通りには動作しません。
NULL値を正しく検索するためには、専用の演算子 IS NULL と IS NOT NULL を使用します。
IS NULL: NULLである行を検索
カラムの値がNULLである行を検索するには IS NULL を使います。
-- emailカラムがNULLのユーザーを検索
SELECT user_id, name FROM users WHERE email IS NULL;このクエリは、emailアドレスが登録されていない(NULLである)ユーザーのIDと名前を返します。
IS NOT NULL: NULLでない行を検索
逆に、カラムの値がNULLではない(何らかの値が入っている)行を検索するには IS NOT NULL を使います。
-- 価格 (price) がNULLでない商品を検索
SELECT product_name, price FROM products WHERE price IS NOT NULL;このクエリは、価格が設定されている(NULLではない)商品の名前と価格を返します。
IS NULL または IS NOT NULL を使いましょう! = NULL や <> NULL は使いません。 NULLと集計関数
COUNT, SUM, AVG などの集計関数を使う際にも、NULLの扱いに注意が必要です。基本的に、ほとんどの集計関数は計算の対象とするカラムの値がNULLである場合、その行を無視します。
| 関数 | NULLの扱い | 例 |
|---|---|---|
COUNT(*) | NULLを含むすべての行数をカウントします。 | SELECT COUNT(*) FROM users; |
COUNT(列名) | 指定した列の値がNULLでない行数をカウントします。 | SELECT COUNT(email) FROM users; (emailがNULLの行はカウントされない) |
SUM(列名) | NULLを無視して合計値を計算します。 | SELECT SUM(score) FROM results; (scoreがNULLの行は合計に含まれない) |
AVG(列名) | NULLを無視して平均値を計算します。(合計値 ÷ NULLでない行数) | SELECT AVG(age) FROM members; (ageがNULLの行は平均計算の母数にも分子にも含まれない) |
MIN(列名) | NULLを無視して最小値を検索します。 | SELECT MIN(price) FROM products; |
MAX(列名) | NULLを無視して最大値を検索します。 | SELECT MAX(salary) FROM employees; |
注意点:
COUNT(*)だけは特別で、行全体を見るためNULLかどうかに関わらずカウントします。SUMやAVGなどで、NULLを0として扱って計算したい場合は、COALESCEやIFNULLといった関数を使ってNULLを0に変換する必要があります(これらの関数は別の機会に詳しく学びます)。例えば、AVG(COALESCE(score, 0))のように使います。- 全ての値がNULLのカラムに対して
SUMやAVGを適用した場合、結果がNULLになることがあります(データベースによって挙動が異なる場合があります)。COUNTは0を返します。
NULLとソート (ORDER BY)
ORDER BY句を使って結果を並び替える際、NULL値がどのように扱われるかは、使用しているデータベースシステム(MySQL, PostgreSQL, SQL Serverなど)によってデフォルトの挙動が異なります。
- PostgreSQL, Oracle: デフォルトでは、昇順(
ASC)の場合はNULLが最後に、降順(DESC)の場合はNULLが最初に来ます。 - MySQL, SQL Server, SQLite: デフォルトでは、昇順(
ASC)の場合はNULLが最初に、降順(DESC)の場合はNULLが最後に来ます。
SQL標準では、NULLS FIRST または NULLS LAST を ORDER BY 句に追加することで、NULLの順序を明示的に指定できます。これにより、データベース間の挙動の違いを吸収できます。(ただし、MySQLの古いバージョンなど、一部のデータベースではこの構文がサポートされていない場合があります。)
-- PostgreSQL, Oracle などで NULL を最初に持ってくる例
SELECT name, registration_date
FROM users
ORDER BY registration_date ASC NULLS FIRST;
-- PostgreSQL, Oracle などで NULL を最後に持ってくる例
SELECT name, registration_date
FROM users
ORDER BY registration_date DESC NULLS LAST;NULLS FIRST / NULLS LAST が使えないデータベース(例: MySQL)では、少し工夫が必要になることがあります。例えば、IS NULL を使ってNULLかどうかを先に判定させる方法があります。
-- MySQL で NULL を最後に持ってくる例 (昇順の場合)
SELECT name, registration_date
FROM users
ORDER BY registration_date IS NULL ASC, registration_date ASC;
-- registration_date IS NULL は NULL なら 1, そうでなければ 0 を返すので、先に 0 (非NULL) が来て、次に 1 (NULL) が来る
-- MySQL で NULL を最初に持ってくる例 (降順の場合)
SELECT name, registration_date
FROM users
ORDER BY registration_date IS NULL DESC, registration_date DESC;
-- registration_date IS NULL は NULL なら 1, そうでなければ 0 を返すので、先に 1 (NULL) が来て、次に 0 (非NULL) が来る あるいは、MySQLでは-(マイナス記号)を使ってソート順を反転させるテクニックもありますが、少し直感的ではないかもしれません。
ORDER BY を使う際は、NULLがどのようにソートされるか、データベースのドキュメントを確認するか、NULLS FIRST / NULLS LAST (対応していれば) を使って明示的に指定するのが安全です。 まとめ
今回はNULLの扱いについて学びました。
- NULLは「値がない」「不明」を示す特別なマーカーであり、0や空文字列とは異なります。
- NULLを検索するには
=や<>ではなく、IS NULLまたはIS NOT NULLを使用します。 - ほとんどの集計関数 (
SUM,AVG,MIN,MAX,COUNT(列名)) は、計算時にNULL値を無視します。COUNT(*)はNULLもカウントします。 -
ORDER BYでのNULLの扱いはデータベースによって異なるため、NULLS FIRST/NULLS LASTで明示的に指定するか、挙動を確認しましょう。
NULLはSQLを扱う上で非常に重要で、時に予期せぬ結果の原因にもなります。このセクションで学んだ基本をしっかり押さえて、NULLと上手に付き合っていきましょう!