【入門編】 遅延可能制約 – PostgreSQL

こんにちは!データベースの世界へようこそ。

普段、PostgreSQLを触っていて「データの整合性は守りたいけれど、どうしても一度にルールを適用できない……」なんて悩んだことはありませんか?

例えば、お互いに「あなたが先に注文してよ!」「いやいや、あなたこそ先に伝票を書いて!」と譲り合ってしまうような、いわゆる「鶏と卵」問題。データベースの世界でも、複雑なデータ構造を扱っていると、こうした「順番のジレンマ」にぶつかることがよくあるんです。

今日はそんな困った場面をスマートに解決してくれる、PostgreSQLの隠れた(でも超優秀な)機能、「遅延可能制約(Deferrable Constraints)」についてお話しします。

—

そもそも「制約」って何?

まず、少しだけ基本のおさらいをしましょう。データベースにおける「制約」とは、言ってみれば「データの守り神」です。

  • 「この列は空っぽにしちゃダメだよ(NOT NULL)」
  • 「他のテーブルに存在するIDしか登録しちゃダメだよ(外部キー制約)」

これらは、データが汚れるのを防ぐための大切なルールですよね。通常、PostgreSQLは、コマンドを打ったその瞬間に「ルール違反がないか?」を厳しくチェックします。

でも、この「即座にチェックする」という性質が、時には私たちを苦しめることになるんです。

—

なぜ「待ってほしい」ことがあるのか?

例えば、ある「注文」と「配送」を管理するシステムを想像してみてください。

1. 「注文テーブル」には「配送ID」が必要
2. 「配送テーブル」には「注文ID」が必要

この2つを同時に登録しようとすると、データベースはこう言います。
「配送IDがまだないのに注文を登録しようとしてる? ダメダメ!」
「注文IDがまだないのに配送を登録しようとしてる? それもダメ!」

…これじゃあ、どちらも登録できませんよね。これが「循環参照」という悩みどころです。現実世界なら「とりあえず両方の伝票をデスクに置いて、最後にまとめてチェックして!」と言えるのに、データベースは生真面目すぎて、その場その場で怒られてしまうんです。

—

そこで登場!「遅延可能制約」という魔法

ここで登場するのが「遅延可能制約(DEFERRABLE)」です。

これは、データベースに対して「ごめん、今はまだルール違反に見えるかもしれないけど、このトランザクション(一連の作業)が終わるまで待っててね!」とお願いする機能なんです。

イメージとしては、「試験中に、問題の途中で採点しないで! 全部書き終わるまで待ってて!」と言えるようなものですね。

どうやって使うの?

設定は驚くほどシンプルです。テーブルを作るときに、少しだけおまじないを加えるだけ。

ALTER TABLE 配送テーブル
ADD CONSTRAINT 注文ID_FK
FOREIGN KEY (注文ID) REFERENCES 注文テーブル(id)
DEFERRABLE INITIALLY DEFERRED;

この `INITIALLY DEFERRED` という言葉が重要です。「最初はチェックを先延ばしにするよ」という宣言ですね。これさえしておけば、お互いに相手のデータが必要な複雑な登録処理も、トランザクションの中で平穏無事に完了させることができます。

—

注意点:便利だけど「諸刃の剣」

「じゃあ、全部これにしておけばいいじゃないか!」と思うかもしれませんが、それは少し危険です。

制約チェックを後回しにするということは、「間違いが確定するのが遅れる」ということでもあります。プログラムのバグで間違ったデータを流し込んでしまったとき、即座にエラーが出ればすぐ修正できますが、後回しにすると「最後にまとめてエラー」になってしまい、原因を特定するのが難しくなることもあります。

「本当に複雑な循環参照があるときだけ使う」。これが、この魔法を使いこなすプロの姿勢です。

—

最後に

データベースの設計は、パズルのようなものです。時には今回のような「遅延可能制約」という特殊ピースを使って、パズルを完成させないといけない場面も出てくるでしょう。

もし皆さんがデータモデリングで「どうしても順番が合わない!」と頭を抱えたら、ぜひこの「後回しにする勇気」を思い出してみてください。きっと、PostgreSQLが優しくあなたの作業をサポートしてくれるはずですよ。

それでは、また次回のブログでお会いしましょう!ハッピーなデータベースライフを!

コメント

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