【実務・中級編】 NOT NULL制約 – PostgreSQL

「またNULLで悩んでるの?」

現場でデータベースを触っていると、たまにこんな光景に出くわします。アプリケーション層で必死にバリデーションをかけているのに、DBのスキーマ定義を見ると、肝心のカラムが `NULL` を許容している状態。

正直に言おう。「とりあえずNULL許可」は、データベース設計における最大の負債の一つだ。

今日は、PostgreSQLにおける `NOT NULL` 制約について、教科書には載っていない「現場の視点」で話をしようと思う。単なるデータ品質の話じゃない。これは、君の書くクエリのパフォーマンスを左右する、エンジニアの腕の見せ所なんだ。

—

なぜ「NULL」が厄介なのか

データベースにおいて `NULL` は「値が存在しない」という意味だけど、これが曲者だ。

例えば、ユーザーのメールアドレスを保存するカラムを考えてみてほしい。`NOT NULL` を付けていないと、何が起こるか?

  • バグの温床: アプリ側のロジックで「メールアドレスは必ず入っているはず」という前提でコードを書くと、`NULL` が紛れ込んだ瞬間に `NullPointerException` や予期せぬエラーが飛ぶ。
  • 集計の狂い: `COUNT(email)` を実行すると、`NULL` はカウントされない。意図しない集計結果が出て、深夜にアラートメールで叩き起こされる羽目になる。

DBの制約は、君のアプリケーションを守る最後の砦だ。「あとでアプリ側で制御するから」なんて甘い考えは捨てて、データベースのスキーマで叩き潰しておこう。

—

パフォーマンスの隠れた立役者:クエリオプティマイザ

ここからが本題だ。ベテランのエンジニアがなぜこれほどまでに `NOT NULL` にこだわるのか。それは、PostgreSQLのクエリプランナ(最適化エンジン)を味方につけるためだ。

PostgreSQLはクエリを実行する際、「このカラムにはNULLが入る可能性があるか?」を常に気にしている。もしカラムが `NULL` を許容していれば、プランナは「NULLかどうかのチェック」を処理の中に組み込まざるを得ない。

逆に、`NOT NULL` 制約が付いていれば、プランナは確信を持ってこう判断する。
「このカラムには絶対に値が入っている。だから、NULLの存在を考慮した余計な分岐処理はカットしよう」

これが積み重なると、特に大規模なテーブルでの実行計画に如実に差が出る。インデックスのサイズ、スキャン効率、そして結合(JOIN)時のコスト。`NOT NULL` は、データベースへの「この列は安全です」という最強のメッセージなんだ。

—

実務での賢い使い方

では、具体的にどう定義すべきか。現場でよくあるパターンを紹介するよ。

1. 基本は「NOT NULL」で始める

デフォルトを `NOT NULL` にし、もしどうしても値が入らないケースがあるなら、それは「別のテーブルに切り出す」か「0や空文字などのデフォルト値で埋める」ことを検討する。

— よい例:明確な制約とデフォルト値
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
user_id INT NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT ‘pending’,
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP
);

2. 既存のテーブルに制約を追加する

あとから「あ、これNOT NULLにすべきだった」と気づくことはよくある。その場合、いきなりALTER TABLEを打つとテーブル全体をロックしてしまい、サービスが止まる可能性がある。

そんな時は `NOT VALID` を使うのがプロのテクニックだ。

— 制約を追加するが、既存データへのチェックは後回しにする
ALTER TABLE users ADD CONSTRAINT check_email_not_null CHECK (email IS NOT NULL) NOT VALID;

— 準備ができたら、バックグラウンドで検証を走らせる
ALTER TABLE users VALIDATE CONSTRAINT check_email_not_null;

これなら、ロック時間を最小限に抑えて安全に制約を適用できる。

—

最後に:完璧主義ではなく、信頼のために

もちろん、ビジネスロジック上「どうしてもNULLが必要」なケースはある。そこまで無理に排除する必要はない。

ただ、意識してほしいのは、「NULLを許容する」ということは、そのカラムに関するすべてのクエリで「NULLだった場合どうするか」を考慮し続けるという重いコストを支払うことだ、ということ。

`NOT NULL` は、君が将来の自分や、一緒に働くチームメンバーに対して残す「安心の記録」だ。「ここは絶対にデータが入っているから、安心してロジックを組んでくれ」というメッセージ。

良い設計は、コードをシンプルにする。
次回のテーブル設計では、ぜひ `NOT NULL` の重みを感じながら `CREATE TABLE` してみてくれ。きっと、クエリのレスポンスも、君のエンジニアとしての自信も、少しだけ向上するはずだから。

それじゃ、また現場で会おう。

コメント

タイトルとURLをコピーしました