はじめに
「今月のKPI、過去最高ですね!素晴らしい!」
会議で上司から褒められたあの瞬間。プレゼン資料に映し出された右肩上がりのグラフ。チームメンバーからの拍手。
でも、その翌日。
「ちょっといい?このSQL見せて」
先輩エンジニアに声をかけられた瞬間、私の人生で最も恥ずかしい経験が始まりました。
KPIが20%も上振れしていた。 過去最高どころか、平均以下。昨日褒められたのは、すべて NULLを理解していなかった 自分のミスによる幻想でした。
この記事では、NULLを甘く見て痛い目に遭った実体験をお伝えします。
事故の始まり:月次報告の準備
「今月のアクティブ率を集計してくれ」
月末の金曜日。上司から依頼されました。
「来週の経営会議で使うから、今月のユーザーアクティブ率を出してくれる?」
アクティブ率の定義:月内にログインしたユーザー数 / 全登録ユーザー数
すぐにSQLを書きました。
SELECT
COUNT(DISTINCT u.user_id) as total_users,
COUNT(DISTINCT l.user_id) as active_users,
ROUND(COUNT(DISTINCT l.user_id) * 100.0 / COUNT(DISTINCT u.user_id), 2) as active_rate
FROM
users u
LEFT JOIN
login_logs l
ON u.user_id = l.user_id
AND l.login_date >= '2024-11-01'
AND l.login_date < '2024-12-01';
結果:アクティブ率76.5%
「先月は65%くらいだったから、かなり改善してる!」
グラフを作成して上司に報告。週末を迎えました。
会議での報告「過去最高です!」
月曜の朝、経営会議。
「今月のユーザーアクティブ率ですが、76.5%で過去最高を記録しました」
自信満々でプレゼン。上司が満足そうに頷き、「素晴らしい!」とチームメンバーからも拍手。
その夜は、気分良く帰宅しました。
翌日の悪夢
「ちょっといい?このSQL見せて」
火曜日の午後。先輩エンジニアが近づいてきました。
「昨日の報告、見たよ。ちょっとそのSQL見せてもらっていい?」
何も疑わず、画面を見せました。先輩が私のSQLを見て、10秒ほどの沈黙。
「...あ」
「これ、LEFT JOINしてるよね。ログインしてないユーザーの場合、l.user_id は?」
「え...NULL...です...ね...」
血の気が引きました。
COUNT()の使い方が間違っていた
先輩が説明してくれました。
「COUNT(DISTINCT l.user_id) は、NULLを除外してカウントする。だから、ログインしたユーザーは正しい。でも、users テーブルの user_id にNULLが入ってたら?」
「え、それはないはず...あ、でも最近、退会ユーザーの仕様変更で...」
思い出しました。先月、一部のレコードで user_id を NULL に更新する処理を入れていました。
「正しい全登録ユーザー数は、COUNT(*) で取得すべきだったんだ」
-- 正しいSQL
SELECT
COUNT(*) as total_users, -- これが正しい
COUNT(DISTINCT l.user_id) as active_users,
...
先輩が修正したSQLを実行。
アクティブ率:58.3%
報告した数字より 18.2ポイント も低い。過去最高どころか、過去3ヶ月で最低でした。
「上司に報告、どうする?」
「...上司に、報告しないと...」
立ち上がる足が震えました。昨日あれだけ褒められたのに。
「あの...昨日の数字なんですが...」
訂正報告の会議
「昨日の数字、間違っていました」
翌日、再び経営会議。今度は謝罪のためです。
「昨日報告した数字ですが、集計ミスがありました。正しいアクティブ率は 58.3% です。昨日の報告値より約18ポイント低くなります」
スクリーンの修正後のグラフ。右肩上がりだったトレンドが、横ばいに。
会議室に重い沈黙が流れました。
「SQLのCOUNT関数の使い方を誤っていました。NULLの扱いを正しく理解していなかったことが原因です。申し訳ございませんでした」
深く頭を下げました。
会議後、上司から個別に呼ばれました。
「ミスは誰にでもある。でも、KPIはチーム全体の評価に直結するから、今後は必ずレビューを入れること。それと...SQLの基礎、ちゃんと勉強した方がいいよ。なんとなくで書いてたでしょ?」
図星でした。何も言い返せませんでした。
なぜこんなことに?
デスクに戻って考えました。
- COUNT(col) と COUNT(*) の違いを知らなかった
- NULLがどう扱われるか理解していなかった
- 「なんとなく動くSQL」を書いていた
一番の問題は、SQLの基礎知識が欠けていた ことでした。
真因:基礎知識の欠如
後日、先輩が教えてくれました。
JOINでNULLが増える
「LEFT JOINは、左側のテーブルの全レコードを保持する。右側にマッチするデータがない場合、右側のカラムはすべてNULLになる」
言われてみれば当たり前。でも、意識したことがありませんでした。特に集計関数と組み合わせた場合、NULLの扱いを理解していないと今回のような事故が起きます。
COALESCEで"0扱い"にしていた
「君のSQL、よくCOALESCEでNULLを0に変換してるよね」
過去のコードを見返すと、確かにそうでした。
「購入履歴なし(NULL) と 購入金額0円 は、意味が違うよね。COALESCEで一律0にすると、その違いが消えちゃう」
平均購入額を計算するとき、この違いは大きな影響を与えます。意識せずにCOALESCEを使っていた 自分は、その違いを理解していませんでした。
NULLの三値論理
先輩から渡された本で初めて知りました。NULLの三値論理。
SQLの世界では、TRUE と FALSE だけでなく、UNKNOWN という第三の状態が存在する。NULLとの比較結果は UNKNOWN になり、WHERE句では除外される。
知らなかったでは済まされない。これは SQLの基本中の基本 でした。
基礎を学び直す決意
「もう二度とこんな恥はかきたくない」
もう二度と、こんな恥はかきたくない。
ネットで調べてコピペする仕事の仕方は終わりにする。体系的に学ぶため、先輩に勧められた OSS-DB技術者認定試験 に挑戦することにしました。
OSS-DB技術者認定試験への挑戦
OSS-DBは PostgreSQL の技術力と知識を認定する資格です。
Silver の出題範囲を見ると、今回のミスの原因がそのまま載っていました。
- SQL全般:SELECT、集計関数、NULL の扱い
- データベース設計:正規化、制約
- 運用管理:バックアップ
- パフォーマンス:インデックス、EXPLAIN
「これだ」と思い、勉強を開始。NULLの章では「COUNT(col) と COUNT(*) の違い」「NULLを含む計算」など、知らなかったことだらけでした。
2ヶ月後、OSS-DB Silver に合格しました。
学び直した結果
試験勉強を通じて得られたもの:
- 自信を持ってSQLを書ける - 「なんとなく」ではなく「理解して」書けるように
- 同じミスをしない - NULL、集計、JOINの挙動を理解
- 信頼の回復 - 半年後の報告では、レビュー済みで正確な数値を提出。「前より信頼できる」と言葉をもらいました。
まとめ
NULLを甘く見た代償
- KPIが20%上振れして会議で褒められる
- 翌日に間違いが発覚して謝罪
- チームからの信頼を失う
基礎知識の大切さ
- 断片的な知識では不十分
- 体系的に学ぶことの価値
- SQLの基本(NULL、COUNT、COALESCE)の理解
SQLの基礎に不安がある方は、OSS-DB試験での学習をおすすめします。試験合格が目的ではなく、学び直しのガイドとして活用するだけでも、実務で大きな違いを生むはずです。
詳細は公式サイトをご確認ください:
※試験内容や受験料は変更される可能性がありますので、最新情報は公式サイトでご確認ください。

