Oracle Databaseでデータを扱う上で、多くの方が一度は頭を悩ませるのが「NULL」の存在ではないでしょうか。特に、NULLが関わる比較の挙動は直感的でない部分が多く、SQLでNULL同士を比較してもなぜ意図通りにいかないのか、またNULL比較演算子がどのように働くのかについて、疑問を持つ方は少なくありません。
この記事では、SQLでNULLを含むデータの扱いや、大小比較の結果がどうなるのかを徹底的に解説します。さらに、Oracleにおける空文字とNULLの特殊な関係や、ブランクやfalseとの違いといった基本的な疑問から、「IS NOT NULL」と「<> ''」の挙動の違いといった具体的なコーディングの問題まで、幅広くカバーします。
加えて、NUMBER型でNULLを許容する際の注意点や、NULLを別の値に置き換える方法といった実践的なテクニック、そして0とNULLの関係という細かいけれど重要なポイントについても触れながら、初心者にも分かりやすく解説していきます。
この記事で分かること
- OracleにおけるNULLの基本的な概念と特殊な仕様
- NULLが関わる比較演算子や論理演算子の正しい挙動
- NULLを適切に扱うためのNVLやCOALESCEといったSQL関数
- NULL値に起因する予期せぬエラーやバグの具体的な回避策
本記事の内容
oracle 比較 nullの基礎知識
- 比較演算子の基本的な動作
- SQLでのNULL同士の比較は、なぜ「UNKNOWN」になるのか
- 「0」や「false」とNULLの具体的な違い
- 空文字、ブランク、NULLの違い
- NULLに対して大小比較はできるのか
- SQLでNULLを含むレコードを抽出するための方法
比較演算子の基本的な動作
Oracle SQLにおいて、NULLを扱う際の最も基本的なルールは、通常の比較演算子(=, <>, !=, <, >)が期待通りに機能しないという点です。これは、SQLの世界が「TRUE」「FALSE」の2つの真理値だけでなく、「UNKNOWN(不明)」を加えた3値論理に基づいているためです。
NULLは「値が存在しない」「不明である」という特殊な状態を表します。そのため、ある値が「NULLと等しいか?」と尋ねられても、その答えは「はい(TRUE)」でも「いいえ(FALSE)」でもなく、「分からない(UNKNOWN)」となります。これは、NULL自身がどんな値を持っているか不明であるため、比較のしようがないからです。
例えば、以下のようなSQLを実行しても、NULLの行は一件も返ってきません。
-- nameカラムがNULLのレコードは抽出されない
SELECT * FROM users WHERE name = NULL;
WHERE句の働き
SQLのWHERE句は、評価結果がTRUEになる行のみを抽出するルールになっています。比較結果がUNKNOWNやFALSEになる行は、結果セットに含まれません。これが、NULLとの比較演算子が意図通りに動かない根本的な理由です。
では、NULLである行(またはNULLでない行)を正しく抽出するにはどうすればよいのでしょうか。そのために用意されているのが、IS NULL および IS NOT NULL という専用の演算子です。これらはNULLを判定するために特別に設計されており、正しくTRUEまたはFALSEを返します。
正しいNULLの判定方法
- NULLのレコードを抽出する場合:
WHERE column_name IS NULL - NULLでないレコードを抽出する場合:
WHERE column_name IS NOT NULL
「= NULL」と書いてしまうのは、プログラミングを始めたばかりの方によくある間違いです。SQLにおけるNULLは特別な存在だと、まずはしっかり意識することが大切ですね。
SQLでのNULL同士の比較は、なぜ「UNKNOWN」になるのか
SQLの初心者が特に混乱しやすいのが、「NULL = NULL」の結果です。直感的には「同じもの同士だからTRUEだろう」と考えてしまいがちですが、これも前述の3値論理に基づき、結果はUNKNOWNとなります。
この理由を理解するには、NULLの本質を「未知の値」だと考えると分かりやすいです。例えば、2人の人物の年齢がどちらも「不明(NULL)」だったとします。この2人の年齢は同じでしょうか?
もしかしたら2人とも25歳で同じかもしれませんし、一方が30歳で他方が40歳と異なるかもしれません。情報がないため、「同じである(TRUE)」とも「異なっている(FALSE)」とも断定できません。この「断定できない状態」こそが、SQLにおけるUNKNOWNなのです。
したがって、WHERE NULL = NULL という条件はUNKNOWNと評価され、結果として何も行を返しません。

Oracleの特殊な関数「DECODE」
ほとんどの演算子や関数と異なり、OracleのDECODE関数は例外的にNULLとNULLを等しいものとして扱います。これはOracle独自の仕様であり、他のデータベースとの互換性を考える上では注意が必要です。
-- col1がNULLの場合に'NULLです'と返す
SELECT DECODE(col1, NULL, 'NULLです', 'NULLではありません') FROM my_table;ちなみに、ANSI SQL標準には IS (NOT) DISTINCT FROM というNULL安全な比較演算子が存在します。これはNULL同士を等しいと判定してくれますが、Oracle Databaseではバージョン21cからサポートされた比較的新しい機能であり、古いバージョンでは使用できない点に注意が必要です。
「0」や「false」とNULLの具体的な違い
NULLはしばしば「0(ゼロ)」や「false(偽)」と混同されがちですが、これらは全く異なる概念です。この違いを正確に理解することが、NULLを正しく扱うための鍵となります。
まず、0は数値データ型における具体的な「値」です。例えば「在庫が0個」というのは、「在庫がない」という明確な状態を示しています。一方で「在庫がNULL」というのは、「在庫数がまだ調査されておらず不明である」という状態の不在を意味します。

同様に、falseは真偽値(Boolean)データ型における「偽」という明確な「値」です。これに対し、NULLが比較演算で返すUNKNOWNは、真でも偽でもないとされる「不明な状態」を指します。
これらの違いは、演算の結果に明確に現れます。
| 式 | 結果 | 解説 |
|---|---|---|
| 10 + 0 | 10 | 0は数値なので、算術演算が可能です。 |
| 10 + NULL | NULL | NULLを含む算術演算の結果は、常にNULLになります。 |
| NOT FALSE | TRUE | FALSEの否定は、明確にTRUEです。 |
| NOT UNKNOWN | UNKNOWN | 「不明」を否定しても、結果は「不明」のままです。 |
NULLは「値がない」状態であり、0や空文字、falseといった「具体的な値」とは根本的に異なります。この区別を意識することが非常に重要です。
空文字、ブランク、NULLの違い
Oracle Databaseを扱う上で、他のデータベース経験者が最も戸惑うポイントの一つが、空文字('')の扱いです。多くのデータベース(例: PostgreSQL, SQL Server)では、空文字は「長さ0の文字列」という値として扱われ、NULLとは明確に区別されます。
しかし、Oracleでは、空文字('')はNULLとして扱われます。これはOracleの非常に特徴的な仕様であり、SQLの挙動に大きな影響を与えます。
例えば、VARCHAR2型のカラムに空文字をINSERTしようとすると、実際にはNULLが格納されます。
INSERT INTO employees (name) VALUES ('');
-- この操作の後、nameカラムにはNULLが格納されるこの仕様のため、Oracleでは以下のような比較は意図通りに機能しません。
-- このSQLは、空文字をINSERTした行を含め、どの行も返さない
SELECT * FROM employees WHERE name = '';上記のSQLは、Oracleの内部で WHERE name = NULL と同じように解釈されるため、結果はUNKNOWNとなり、行が返されないのです。空文字(に見える)レコードを探したい場合も、WHERE name IS NULL を使用する必要があります。

将来のバージョンでの変更可能性
Oracleの公式ドキュメントでは、この「空文字をNULLとして扱う」仕様が将来のリリースで変更される可能性があると示唆されています。この仕様に依存したアプリケーション設計は、将来的なリスクを伴う可能性があるため、推奨されていません。
「ブランク」という言葉は、文脈によってはスペース文字(' ')を指すこともありますが、SQLの文脈では明確な定義はありません。もしスペース文字を指しているのであれば、それはNULLや空文字とは異なり、明確な「文字」として扱われます。
NULLに対して大小比較はできるのか

結論から言うと、NULLに対して大小比較演算子(<, >, <=, >=)を使用することは可能ですが、その結果は常にUNKNOWNとなります。これは、等価比較(=, <>)と同様の理由です。
NULLは「未知の値」であるため、特定の数値や日付より大きいか小さいかを判断することができません。例えば、「年齢が不明(NULL)の人」は「20歳以上」でしょうか?答えは「分からない(UNKNOWN)」ですよね。SQLの世界でも全く同じことが起こります。
この仕様は、特に範囲検索を行う際に意図しない結果を招くことがあります。例えば、以下のSQLを考えてみましょう。
-- 100以下の値を持つレコードを抽出したい
SELECT * FROM products WHERE price <= 100;このクエリでは、priceカラムの値がNULLであるレコードは抽出されません。なぜなら、NULL <= 100 の評価結果がUNKNOWNになるため、WHERE句の条件(TRUEであること)を満たさないからです。
「100より大きくはないのだから、含まれるはずだ」と考えてしまうと、NULLのレコードが漏れてしまうバグの原因になります。大小比較でもNULLは特別扱いが必要だと覚えておきましょう。
もし、NULLを特定の数値(例えば0)として扱って比較したい場合は、後述するNVLやCOALESCEといった関数を使用して、明示的に値を変換する必要があります。
-- priceがNULLの場合は0として扱い、100以下のレコードを抽出
SELECT * FROM products WHERE NVL(price, 0) <= 100;このように関数を使うことで、NULLのレコードも比較の対象に含めることができます。ただし、対象カラムのインデックスが使われなくなる可能性があるなど、パフォーマンスへの影響には注意が必要です。
SQLでNULLを含むレコードを抽出するための方法
SQLでデータを抽出する際、NULLの存在を考慮しないと、意図せずデータが漏れてしまうことがよくあります。特に、否定条件(<> や NOT)を使う場合に注意が必要です。
例えば、「'taro'という名前以外」のユーザーを全て抽出したいと考え、以下のようなSQLを書いたとします。
SELECT * FROM users WHERE name <> 'taro';このSQLは、'hanako'や'jiro'といった名前のレコードは正しく抽出しますが、nameカラムがNULLのレコードは抽出しません。理由はこれまで述べてきた通り、NULL <> 'taro' の評価結果がTRUEではなくUNKNOWNになるためです。
「'taro'以外」という条件には、通常「名前が設定されていない(NULL)人」も含まれるべきです。このようなケースでNULLのレコードも正しく抽出するには、OR句を使ってNULLの条件を明示的に追加する必要があります。

正しい否定条件の書き方
SELECT * FROM users WHERE name <> 'taro' OR name IS NULL;このように OR name IS NULL を加えることで、「nameが'taro'と異なる、または、nameがNULLである」という条件になり、NULLのレコードも漏れなく抽出できます。
この問題は、NOT演算子を使った場合も同様です。
-- この書き方でもNULLのレコードは抽出されない
SELECT * FROM users WHERE NOT (name = 'taro');name = 'taro' の評価は、nameがNULLの行ではUNKNOWNになります。そして、NOT UNKNOWN の結果もUNKNOWNになるため、結局その行は抽出対象から外れてしまうのです。
NULLを許容しているカラムに対して否定条件を使うときは、「OR IS NULL」を付けることを常に意識する癖をつけると、多くのバグを防げますよ!
oracle 比較 nullの応用テクニック
- IS NOT NULLとIS NULL <> ''の挙動差
- NULLを別の値に置き換える代表的な方法
- NUMBER型でNULLを許容する場合の注意点
- OracleでNULLを比較する際の重要ポイント総まとめ
IS NOT NULLとIS NULL <> ''の挙動差
「NULLでない値を持つレコード」を抽出しようとする際に、IS NOT NULL と <> '' を混同してしまうケースがありますが、特にOracleにおいては、この2つの挙動は全く異なります。結論から言うと、NULLでないレコードの判定には必ずIS NOT NULLを使用しなければなりません。
それぞれの挙動を詳しく見ていきましょう。

IS NOT NULL の挙動
これは、NULLでないことを判定するためにSQL標準で定められた、正当な演算子です。カラムの値がNULLでなければTRUEを、NULLであればFALSEを返します。
-- nameカラムに何らかの値が入っているレコードのみを正しく抽出する
SELECT * FROM users WHERE name IS NOT NULL;<> '' の挙動
一方、<> '' は「空文字と等しくない」という意味の比較です。しかし、前述の通り、Oracleでは空文字('')はNULLとして扱われます。そのため、この条件式はOracleの内部で以下のように解釈されます。
WHERE name <> NULL
そして、NULLとの比較演算の結果は常にUNKNOWNになるため、この条件式がTRUEになることはありません。結果として、このSQLはnameカラムに値が入っているかどうかに関わらず、一件もレコードを返しません。
この挙動の違いを表にまとめると、以下のようになります。
| カラムの値 | IS NOT NULL の評価結果 | <> '' の評価結果 |
|---|---|---|
| 'taro' | TRUE | UNKNOWN |
| NULL | FALSE | UNKNOWN |
見ての通り、<> '' はOracleにおいてNULL判定の目的では全く機能しません。これはOracle特有の仕様に起因する罠であり、他のデータベースの感覚でSQLを書くとハマりやすいポイントなので、十分に注意してください。
NULLを別の値に置き換える代表的な方法
NULLをそのまま比較するのではなく、一時的に別の具体的な値に置き換えてから処理したいケースは頻繁にあります。例えば、NULLを0として計算したり、'未設定'という文字列として表示したりする場合です。Oracleでは、そのために便利な関数がいくつか用意されています。

NVL関数
NVL(expr1, expr2) は、最もシンプルでよく使われる関数です。
- expr1がNULLでなければ、expr1を返します。
- expr1がNULLであれば、expr2を返します。
-- salaryがNULLの従業員は給与を0として表示する
SELECT name, NVL(salary, 0) FROM employees;NVL2関数
NVL2(expr1, expr2, expr3) は、NVLをさらに拡張した関数で、3つの引数を取ります。
- expr1がNULLでなければ、expr2を返します。
- expr1がNULLであれば、expr3を返します。
-- commissionがNULLでない(歩合がある)場合は'歩合あり'、NULLの場合は'歩合なし'と表示
SELECT name, NVL2(commission, '歩合あり', '歩合なし') FROM employees;COALESCE関数
COALESCE(expr1, expr2, ..., exprN) は、複数の引数を取り、その中で最初に見つかったNULLでない値を返します。全ての引数がNULLの場合はNULLを返します。これはANSI SQL標準の関数であり、他のデータベースとの互換性が高いのが特徴です。
-- 自宅電話番号がなければ携帯電話番号を、それもなければ'連絡先不明'を表示
SELECT name, COALESCE(home_phone, mobile_phone, '連絡先不明') FROM contacts;関数の使い分け
- NVL:
シンプルにNULLを一つの代替値に置き換えたい場合に最適。 - NVL2:
NULLか否かで全く異なる2つの値を返したい場合に便利。 - COALESCE:
複数のカラムを優先順位付けして、最初に見つかった有効な値を使いたい場合に強力。
パフォーマンスに関する注意点
これらの関数をWHERE句の中で使用すると、対象のカラムにインデックスが設定されていても、それが利用されずにパフォーマンスが低下する可能性があります。大量のデータを扱う場合は、安易な使用は避け、実行計画を確認するなどの注意が必要です。
NUMBER型でNULLを許容する場合の注意点

数値データを扱うNUMBER型カラムでNULLを許容する設計は、特に集計処理において注意深い考慮が必要です。NULLは0とは異なるため、集計関数の結果が直感と異なる場合があります。
集計関数におけるNULLの扱い
Oracleの主要な集計関数(SUM, AVG, COUNT, MAX, MIN)は、基本的に計算の対象からNULL値を無視します。
- SUM(col):
colがNULLの行は無視して合計を計算します。 - MIN(col) / MAX(col):
colがNULLの行は無視して最小値/最大値を求めます。 - COUNT(col):
colがNULLでない行の数を返します。 - COUNT(*):
NULLを含め、全ての行の数を返します。 - AVG(col):
colがNULLの行は分母・分子の両方から除外して平均値を計算します(SUM(col) / COUNT(col)と等価)。
特にCOUNTとAVGの挙動は重要です。例えば、テストの点数カラム(score)に、受験済み(点数あり)と未受験(NULL)の学生が混在しているとします。
| student_name | score |
|---|---|
| Aさん | 80 |
| Bさん | 60 |
| Cさん | NULL (未受験) |
このデータに対して各集計関数を実行すると、結果は以下のようになります。
COUNT(*)→ 3 (全生徒数)COUNT(score)→ 2 (受験者数)SUM(score)→ 140 (合計点)AVG(score)→ 70 (140 / 2、つまり受験者の平均点)
AVG(score)の結果が、全生徒数である3で割った値 (140 / 3 ≒ 46.7) にならない点に注意が必要です。もし未受験者(NULL)を0点として扱って全体の平均点を出したい場合は、AVG(NVL(score, 0)) のように明示的にNULLを0に変換する必要があります。
このように、NULLを許容するかどうか、そしてNULLをどのように扱うかは、データの意味そのものを変えてしまいます。テーブルを設計する段階で、そのカラムがNULLを許容すべきか、許容しない場合はデフォルト値を何にするかを慎重に検討することが、後の複雑な問題を避けることにつながります。
OracleでNULLを比較する際の重要ポイント総まとめ
この記事では、OracleにおけるNULLの比較と扱い方について、基礎から応用まで詳しく解説しました。最後に、重要なポイントをリスト形式で振り返ります。
- SQLの比較はTRUE、FALSE、UNKNOWNの3値論理で評価される
- NULLを含む比較演算子(=, <>, <, >)の結果は常にUNKNOWNになる
- WHERE句は評価がTRUEの行のみを返すため、NULLとの比較では行が抽出されない
- NULLであるかどうかの判定には専用のIS NULLまたはIS NOT NULLを使用する
- NULL = NULLの結果もUNKNOWNであり、TRUEにはならない
- Oracleは例外的にDECODE関数内ではNULL同士を等しいとみなす
- NULLは「値の不在」であり、数値の0や真偽値のFALSEとは全く異なる概念
- NULLを含む算術演算の結果は必ずNULLになる
- Oracleの大きな特徴として、空文字('')はNULLとして扱われる
- 空文字のレコードを検索する場合もIS NULLを使用する必要がある
- 否定条件(<> や NOT)を使う際は、NULLのレコードが漏れないようOR IS NULLの追加を検討する
- NULLでない判定に<> ''を使うと、Oracleでは1行も返らないため使ってはならない
- NULLを特定の値に置き換えるにはNVL、NVL2、COALESCEといった関数が有効
- COALESCEは複数の候補から最初の非NULL値を返す標準SQL関数で互換性が高い
- 集計関数(SUM, AVG, COUNTなど)は基本的に計算対象からNULLを無視する