【実務・中級編】 制約とデータ整合性 – PostgreSQL

DBの「守護神」を使いこなせ:PostgreSQLの制約(Constraint)でデータ品質を極める

やあ、エンジニアの皆さん。今日も元気にクエリを叩いていますか?

システムの開発をしていると、つい「機能」の実装ばかりに目が行きがちですよね。でも、ベテランのエンジニアほど、実は「データがどう入ってくるか」よりも「不正なデータがどう入らないようにするか」に情熱を注いでいるんです。

アプリケーション側のバリデーションだけでは、正直言って穴だらけです。DBの接続先が変わったり、別の言語で書かれたバッチ処理が走ったりした瞬間に、データは簡単に汚染されてしまいます。

そこで重要になるのが、PostgreSQLが誇る「制約(Constraint)」という守護神たち。今回は、現場で泥臭く戦ってきた経験から、明日から使える制約の勘所を解説していきますね。

—

1. そもそも、なぜ制約を「DB側」でかけるのか?

たまに「制約が多すぎるとパフォーマンスが落ちるから、アプリ側で制御すればいい」という議論を見かけます。結論から言うと、整合性を担保する制約は、迷わずDBに書くべきです。

DBはシステムの「最後の砦」です。ここが堅牢であれば、アプリ側は「正しいデータが来る」という前提でビジネスロジックに集中できます。バグの温床になりがちな「データ不整合の調査」に時間を奪われないためにも、制約は必須の保険なんですよ。

—

2. 実践で多用する「基本の4大制約」

PostgreSQLの制約は、基本的にはこの4つを押さえておけば大半のケースはカバーできます。

NOT NULL:空っぽを許さない意思表示

一番シンプルですが、一番大事です。「このカラムは絶対に値が必要だ」という項目には、有無を言わさず付けましょう。

CREATE TABLE users (
id SERIAL PRIMARY KEY,
email TEXT NOT NULL, — ここが空だとシステムが破綻する
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);

UNIQUE:重複の影を許さない

メールアドレスやユーザーIDなど、「システム内で一意であるべきもの」に使います。

ALTER TABLE users ADD CONSTRAINT unique_email UNIQUE (email);

アドバイス: UNIQUE制約を貼ると、自動的にインデックスが作成されます。パフォーマンスと整合性の両立ができる、非常にコスパの良い制約です。

PRIMARY KEY:レコードの指紋

主キーは「NOT NULL」かつ「UNIQUE」な存在です。一つのテーブルに必ず一つ設定しましょう。PostgreSQLなら`SERIAL`や`BIGSERIAL`、最近なら`GENERATED ALWAYS AS IDENTITY`を使うのがトレンドですね。

FOREIGN KEY:データの繋がりを守る

外部キーは、テーブル間の「リレーション」を保証します。存在しないユーザーIDを参照した注文データなんて、ただのゴミですからね。

ALTER TABLE orders
ADD CONSTRAINT fk_user_id
FOREIGN KEY (user_id) REFERENCES users(id)
ON DELETE CASCADE; — ユーザーが消えたら注文も消す(削除の戦略は慎重に!)

—

3. CHECK制約:現場が泣いて喜ぶ「最後の防衛線」

初心者が見落としがちですが、実務で一番「おっ、わかってるね」と言われるのがこの`CHECK`制約です。カラムの値に対して「条件」を課すことができます。

例えば、年齢がマイナスだったり、価格が0未満だったりしたらおかしいですよね? そういう時に使います。

CREATE TABLE products (
id SERIAL PRIMARY KEY,
price NUMERIC CHECK (price >= 0), — 価格は絶対に0以上
discount_rate NUMERIC CHECK (discount_rate BETWEEN 0 AND 1) — 割引率は0〜1の間
);

これだけで、アプリ側でいちいち「価格は0以上か?」とチェックするコードを書かなくて済むんです。DBが勝手に弾いてくれる。最高だと思いませんか?

—

4. 現場からのちょっとしたアドバイス

最後に、これから制約と付き合っていく皆さんに、現場の先輩としてのアドバイスを二つ。

1. 制約には名前をつけよう
`ADD UNIQUE (email)` と書くと、DBが自動的に適当な名前(`users_email_key`など)を付けますが、後でエラーメッセージを見た時に分かりづらいです。`ADD CONSTRAINT users_email_uq UNIQUE (email)` のように明示的に名前をつけると、エラー調査のスピードが格段に上がります。

2. 既存テーブルへの制約追加は慎重に
すでに数百万件データが入っているテーブルに、いきなり `NOT NULL` や `CHECK` 制約を追加しようとすると、テーブル全体のスキャンが走り、ロックがかかってサービスが止まることがあります。追加する際は `NOT VALID` オプションを使って段階的に適用するなど、運用手順をしっかり確認してくださいね。

—

最後に

制約を適切に設定することは、未来の自分への、そしてチームへの最高のプレゼントです。「データが汚れない」という安心感があるからこそ、私たちは新しい機能開発に挑戦できる。

PostgreSQLは非常に懐の深いデータベースです。制約という守護神を使いこなして、誰が触っても壊れない「堅牢なシステム」を一緒に作っていきましょう!

もし「こんな制約で悩んでいるんだけど、どう書くのがベスト?」なんて疑問があれば、いつでも聞いてください。現場からは以上です!

コメント

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