TS-Tech
未分類

NULL と CASE で欠損と分岐を扱う

2
  • SQL

SQL で一番つまずきやすい値のひとつが NULL(不明・未設定)です。練習用の members を作り、比較、COUNTCOALESCECASE をまとめて確認します。


目次

  1. NULL 入りの表
  2. 比較と IS NULL
  3. COALESCE と NULLIF
  4. CASE
  5. 集計での NULL
  6. よくあるつまずき

NULL 入りの表

cd $DB_SQL/PostgreSQL_setupdocker compose exec db psql -U practice -d practice_db
DROP TABLE IF EXISTS members;CREATE TABLE members (  id SERIAL PRIMARY KEY,  name TEXT NOT NULL,  nickname TEXT,  points INT);INSERT INTO members (name, nickname, points) VALUES  ('山田', 'ヤマ', 10),  ('佐藤', NULL, 0),  ('鈴木', 'スズ', NULL),  ('高橋', NULL, NULL);

比較と IS NULL

これはニックネームが無い行を返しません(NULL = NULL は真にならないため)。よくあるのは = NULL で絞ろうとして空になり、原因に気づくまで時間がかかることです。

SELECT * FROM members WHERE nickname = NULL;  -- 結果は常に空

正しい書き方:

SELECT * FROM members WHERE nickname IS NULL;SELECT * FROM members WHERE nickname IS NOT NULL;

nickname IS NULL なら佐藤・高橋の 2 行、IS NOT NULL なら山田・鈴木の 2 行です。

points = 0points IS NULL は別物です。0 は「点がゼロ」、NULL は「まだ無い」です。

SELECT name, points FROM members WHERE points = 0;SELECT name, points FROM members WHERE points IS NULL;

COALESCE と NULLIF

COALESCE は左から見て最初の非 NULL を返します。表示用のフォールバックに便利です。

SELECT  name,  COALESCE(nickname, name) AS display_name,  COALESCE(points, 0) AS points_or_zeroFROM members;

NULLIF(a, b)a = b のとき NULL、それ以外は a です。ゼロ除算回避などにも使います。

SELECT NULLIF(points, 0) FROM members;

CASE

SELECT  name,  points,  CASE    WHEN points IS NULL THEN '未設定'    WHEN points = 0 THEN 'ゼロ'    WHEN points >= 10 THEN '高め'    ELSE '普通'  END AS points_labelFROM members;

単純 CASE(一致比較):

SELECT  name,  CASE COALESCE(nickname, '')    WHEN '' THEN 'ニックネームなし'    ELSE nickname  END AS nick_labelFROM members;

集計での NULL

SELECT  COUNT(*) AS all_rows,  COUNT(nickname) AS has_nickname,  COUNT(points) AS has_points,  SUM(points) AS sum_points,  AVG(points) AS avg_pointsFROM members;
  • COUNT(*) … 行数(NULL 行も含む)
  • COUNT(列) … その列が非 NULL の件数
  • SUM / AVG … NULL は無視(ゼロとは数えない)

AVG(COALESCE(points, 0)) にすると「未設定を 0 とみなす平均」になり、意味が変わります。どちらが欲しいかを先に決めてください。


よくあるつまずき

症状 対処
= NULL で絞れない IS NULL を使う
NOT IN (サブクエリ) がおかしい サブクエリ側の NULL
平均が思ったより大きい/小さい NULL を無視しているか、0 埋めしているか確認
文字列連結で全体が NULL COALESCE で空文字に落とす

掃除:

DROP TABLE IF EXISTS members;

IS NULLCOUNT(列) の振る舞いが分かれば、欠損まわりの大半は説明できます。

シェア