クイック解決
PostgreSQL が外来キーの作成をブロックしているのは、参照先のカラムが一意(ユニーク)であることが保証されていないためです。リレーションシップを機能させるには、親テーブルのカラム(order_id など)に PRIMARY KEY または UNIQUE 制約が設定されている必要があります。
不足している制約を追加するには、次のコマンドを実行します。
ALTER TABLE orders ADD CONSTRAINT orders_order_id_unique UNIQUE (order_id);
これを適用すれば、FOREIGN KEY の作成はすぐに成功します。
このエラーが発生する理由
例えば、アプリケーションで 1 日 5,000 件のトランザクションを管理しているとします。orders テーブルと order_items テーブルがあり、これらを一意でないカラムでリンクしようとすると、PostgreSQL は処理を継続できなくなります。
-- この親テーブルの設定が問題の原因となります
CREATE TABLE orders (
id SERIAL,
tracking_code TEXT -- ここに一意性制約がありません!
);
-- この子テーブルの作成は失敗します
CREATE TABLE order_items (
id SERIAL,
sku_name TEXT,
order_ref TEXT REFERENCES orders(tracking_code)
);
データベースエンジンは matching given keys エラーを出して停止します。これは、外来キーがポインタとして機能するためです。もし tracking_code に重複した値が許可されていると、PostgreSQL はそのアイテムが具体的にどの注文に属しているのかを特定できなくなります。
仕組み:データ整合性のルール
PostgreSQL においてデータの整合性は譲れない要素です。たとえ現在テーブル内の値が 100% 重複していなくても、データベースはスキーマレベルでの保証を必要とします。単なる推測ではなく、明確な制約が必要なのです。
この問題の一般的な原因には以下が含まれます:
- **通常カラム:** 一意性インデックス(Unique index)がない標準的なカラムを参照している。
- **複合キーの不一致:** 2 つのカラム(例: `tenant_id` と `user_id`)をリンクしようとしているが、親テーブルではそれらが個別にしか一意として定義されていない。
- **データ型の摩擦:** `BIGINT` を `INT` にリンクしようとすると、紛らわしいエラーが発生したり、パフォーマンスが低下したりすることがあります。
リレーションシップを修正する 3 つの方法
1. 一意性制約を追加する
カラムを TEXT 型や VARCHAR 型のまま維持しつつ、有効な参照ターゲットにする必要がある場合に使用します。これは、INV-2024-001 のような人間が読める ID を使用する場合によく見られます。
ALTER TABLE orders ADD CONSTRAINT unique_tracking_code UNIQUE (tracking_code);
2. 主キー(Primary Key)を参照する(推奨)
ほとんどの本番環境では、id カラムを参照する方が安全で高速です。データが 10 万行を超えると、整数ベースの結合は文字列ベースの結合よりも大幅にパフォーマンスが向上します。
-- 子テーブルが ID を参照するように変更します
ALTER TABLE order_items
ADD CONSTRAINT fk_order_id
FOREIGN KEY (order_id)
REFERENCES orders(id);
3. 複合キーの問題を解決する
リレーションシップが複数のカラムに依存している場合、PostgreSQL は単一の複数カラム制約を必要とします。単に 2 つの独立した一意なカラムを指定するだけでは不十分です。
修正方法:
-- 両方のカラムをカバーする1つの制約を作成します
ALTER TABLE orders ADD CONSTRAINT store_order_composite_unique UNIQUE (store_id, order_id);
スキーマの確認方法
マイグレーションを再試行する前に、psql インターフェースを使用してテーブルの現在の状態を確認してください。\d orders と入力するとテーブル定義が表示されます。
インデックス:
"orders_pkey" PRIMARY KEY, btree (id)
"unique_tracking_code" UNIQUE CONSTRAINT, btree (tracking_code)
ターゲットカラムの横に UNIQUE CONSTRAINT というラベルが表示されていない場合、外来キーの作成は引き続き失敗します。インデックスが表示されれば、準備完了です。
最終チェックリスト
- 親カラムに `UNIQUE` または `PRIMARY KEY` 制約はありますか?
- データ型は完全に一致していますか(例: `UUID` と `UUID`)?
- 複合キーを使用している場合、外来キーのカラム順序は一意性制約の順序と一致していますか?

