代表的な面接トピック

データエンジニアリング面接:PostgreSQLのテンポラル制約はどのように有効期間の重複を防ぐのか?

データ難しい
Offer.cc 編集チーム公開日 更新日

質問

価格やリースのテーブルでは、同一のビジネスキーに対して有効期間が重複してはなりません。PostgreSQL 18のWITHOUT OVERLAPSとPERIODを使用して、この制約をどのようにモデル化、移行、検証しますか?

プロンプトと範囲

価格、リース、スケジュール、権限のレコードは、ビジネスキーと有効期間を組み合わせます。面接官は、並行書き込みを含め、単一の製品またはテナントに対する期間の重複をデータベースが拒否することを求めています。この質問では、単に衝突を検索するクエリではなく、テンポラルモデリング、制約セマンティクス、安全なマイグレーション計画が試されます。

面接官が評価するポイント

  • WITHOUT OVERLAPS テンポラル主キーまたは一意キーと、通常のB-tree一意キーの違いを区別できているか。
  • 範囲の端点、空の範囲、NULLの挙動、離散的な日付、連続的なタイムスタンプを定義しているか。
  • PERIOD 外部キーが、一致するビジネスキーだけでなく時間のカバレッジも検証することを理解しているか。
  • 履歴データのクリーンアップ、ロックの影響、ロールバック、並行検証を計画できるか。
  • データベース制約、アプリケーションメッセージ、監査メトリクスの責任が分離されているか。

セマンティクスと境界

PostgreSQLは範囲カラムに対してテンポラル制約を定義します。WITHOUT OVERLAPS は主キーおよび一意制約で使用でき、通常のキー部分が同一の場合、関連付けられた範囲は重複してはなりません。範囲カラムは暗黙的に非NULLであり、空の範囲やマルチレンジは有効なテンポラルキーを構成しません。このデータベース不変条件は、アプリケーションによる「チェックしてから挿入」のシーケンスで代用することはできません。

PERIOD はテンポラル外部キーに使用されます。子レコードのビジネスキーと期間は、1つ以上の親レコードによってカバーされる必要があります。同一のビジネスキーを持つ親レコードが存在することを証明するだけでは不十分です。親レコードの削除やカバーされている期間の短縮は、外部キーのアクションとトランザクションの順序付けに従う必要があります。

モデリングの手順

半開区間または閉区間を選択し、すべての書き込みパスで同一の規約を使用します。日付の有効性には一般に [start, end) を使用し、タイムスタンプの有効性にはタイムゾーンと精度を指定する必要があります。必要に応じて daterangetsrange、または tstzrange を選択し、通常のビジネスキーカラムを範囲カラムの前に配置します。

親レコードにテンポラル一意制約を作成し、カバーされるレコードに PERIOD 外部キーを作成します。アプリケーションは分かりやすいメッセージを提供できますが、コミットが成功するかどうかを判断するのはデータベースです。履歴データを移行する前に、レポートまたは排他スタイルのチェックを使用して重複を特定し、各衝突をマージ、分割、または廃止するかを定義します。

SQL例

この例では、1つの plan_id に対して価格の重複を防止し、ルールが価格プランによって完全にカバーされることを要求します:

sql
CREATE TABLE price_plan (
  plan_id bigint,
  valid_during daterange NOT NULL,
  amount numeric(12, 2) NOT NULL,
  PRIMARY KEY (plan_id, valid_during WITHOUT OVERLAPS)
);

CREATE TABLE plan_rule (
  plan_id bigint,
  valid_during daterange NOT NULL,
  rule_code text NOT NULL,
  CONSTRAINT plan_rule_plan_period_fk
    FOREIGN KEY (plan_id, PERIOD valid_during)
    REFERENCES price_plan (plan_id, PERIOD valid_during)
);

本番適用の前にシャドウテーブルで構文と挙動を検証し、クライアントドライバがデータベースの衝突を安定した方法で表面化することを確認します。この例はコアとなる不変条件を示しています。通貨、金額の精度、監査カラムは引き続き製品要件に従います。

並行書き込みとマイグレーション

制約を追加する前に、衝突している範囲をカウントしてビジネスキー順に並べ替えます。ビジネス上の決定なしに「重複のように見える」履歴レコードを削除してはなりません。大規模なテーブルの場合は、インデックス構築時間、ロック待機、レプリケーション遅延を見積もります。バッチクリーンアップ、トラフィックの少ない時間枠、および観察可能な進捗マーカーを使用します。

重複する期間に対する2つの並行インサートは、コミット時にデータベースによって調整される必要があります。アプリケーションは一意性の衝突を、リトライ可能または説明可能なビジネスエラーに変換する必要があります。事前の事前チェックが成功したからといって、その後のインサートが保証されるわけではありません。シャドウ検証、デュアルライトの照合、リカバリ訓練が合格するまで、古いカラムと書き込みパスをロールバック用に維持してください。

よくある間違い

  • 一意の (plan_id, start_at) インデックスのみを作成し、期間の重複を許容してしまうこと。
  • 端点ルールを定義せず、2026-02-01 で終了する日付と次の期間の開始を混同してしまうこと。
  • PERIOD 外部キーを、ビジネスキーのみをチェックする通常の外部キーとして扱ってしまうこと。
  • 履歴、ロック、またはレプリケーション遅延を確認せずに、大規模な本番テーブルに直接制約を追加すること。
  • アプリケーションのリトライによって制約の衝突が隠蔽され、価格の重複や不完全なカバレッジが発生すること。

フォローアップ質問

既存の重複はどのように処理しますか?

ビジネスキーごとにグループ化され、範囲で並べ替えられた衝突レポートを生成します。ビジネスオーナーにマージ、分割、または廃止のセマンティクスを選択してもらいます。行を修正し、シャドウテーブルで書き込みをリプレイし、レポートが空であることを証明してから、制約を追加します。

なぜ排他制約やトリガーを使用しないのですか?

排他制約でも区間の相互排他を表現できますが、WITHOUT OVERLAPS はテンポラル主キーまたは一意セマンティクスを直接記述し、PERIOD 外部キーと合成できます。トリガーは並行性、再帰、レプリケーションパスを見落とす可能性があります。コアの不変条件は制約に保持したまま、テーブル間の追加の副作用にのみトリガーを使用してください。

マイグレーションでビジネス時間が保持されたことをどのように証明しますか?

事前と事後で、区間数、境界サンプル、拒否された衝突率、およびクエリプランを比較します。リードレプリカおよびリカバリ環境でカバレッジチェックを実行します。ロールアウト中は古いロジックと照合し、有効な書き込みが拒否された場合は、履歴を削除せずに制約の切り替えをロールバックします。

公開情報ソース

関連する質問