PostgreSQLでのエラー『 table referenced in 』の原因と対処
- 作成日 2025.09.10
- その他
他テーブルの外部キー(FOREIGN KEY)が参照している表をDROPしようとすると、依存関係の保全のために拒否される。正しい手順は「依存の見える化 → 参照元のテーブル/制約を先に処理 → 必要ならCASCADEで一括」と段階的に進める。再現用コード、依存の洗い出しSQL、DROP順序の自動生成、CASCADEの安全運用、パーティションやスキーマ跨りの注意までまとめる。
- 1. エラーの意味と発生条件(RESTRICTが既定)
- 2. 最短再現(NG→OK)
- 3. 依存関係の可視化(参照元FKを一覧)
- 4. 解決策1:参照元テーブルを先にDROP(順序を正す)
- 5. 解決策2:参照元の外部キーのみを先に解除
- 6. 解決策3:DROP TABLE … CASCADE の使い所と注意
- 7. 効かない回避策(落とし穴)
- 8. スキーマ跨り/多数テーブルの依存を一括抽出
- 9. パーティション/継承テーブルの注意点
- 10. 代表的な運用フロー(安全版チェックリスト)
- 11. FK再作成用スクリプトの保存と復元
- 12. 依存FKのDROPを自動実行(DOブロック)
- 13. NG→OK 早見表(よくあるパターン)
- 14. 補足:削除ではなく置換/再設計の選択肢
エラーの意味と発生条件(RESTRICTが既定)
・DROP TABLEは既定でRESTRICT。参照されている表は、参照元FOREIGN KEYが残っている限りDROP不可
・参照は同一スキーマに限らない。他スキーマ・他データベース(dblink/FDWは除く)からのFKは同一DB内で成立
・FKはカタログ依存(pg_constraint/pg_depend)として管理。トリガ無効化やDEFERRED指定では依存チェックを回避できない
最短再現(NG→OK)
-- 例: customers を orders が参照
CREATE TABLE customers (id bigint PRIMARY KEY, name text NOT NULL);
CREATE TABLE orders (
id bigint PRIMARY KEY,
customer_id bigint NOT NULL REFERENCES customers(id),
amount numeric NOT NULL
);
-- NG: 参照されているcustomersをDROP
DROP TABLE customers;
-- ERROR: cannot drop table customers because other objects depend on it
-- DETAIL: constraint orders_customer_id_fkey on table orders depends on table customers
-- OK1: 参照元テーブルを先にDROP
DROP TABLE orders;
DROP TABLE customers;
-- OK2: 参照元のFKだけ先に外してからDROP
ALTER TABLE orders DROP CONSTRAINT orders_customer_id_fkey;
DROP TABLE customers;依存関係の可視化(参照元FKを一覧)
-- 指定テーブルを参照しているFKを洗い出す(テーブル名はschema付で)
WITH target AS (
SELECT 'public.customers'::regclass AS rel
)
SELECT
con.conname AS constraint_name,
con.conrelid::regclass AS referencing_table,
con.confrelid::regclass AS referenced_table,
pg_get_constraintdef(con.oid) AS constraint_def
FROM pg_constraint con
JOIN target t ON con.confrelid = t.rel
WHERE con.contype = 'f'
ORDER BY con.conrelid::regclass::text;解決策1:参照元テーブルを先にDROP(順序を正す)
・「参照元→参照先」の順にDROP
・他にも連鎖がある場合は、上記の一覧で連鎖を辿るか、CASCADEを検討
解決策2:参照元の外部キーのみを先に解除
-- 参照元FKのDROP文を自動生成
WITH target AS (SELECT 'public.customers'::regclass AS rel)
SELECT 'ALTER TABLE '||con.conrelid::regclass||' DROP CONSTRAINT '||quote_ident(con.conname)||';' AS ddl
FROM pg_constraint con JOIN target t ON con.confrelid=t.rel
WHERE con.contype='f';
-- 実行例(生成したDDLを流す)
ALTER TABLE public.orders DROP CONSTRAINT orders_customer_id_fkey;
DROP TABLE public.customers;・後で元に戻す必要がある場合、pg_get_constraintdefで定義を保存しておく
解決策3:DROP TABLE … CASCADE の使い所と注意
-- 依存する制約やオブジェクトも連鎖的に削除
DROP TABLE public.customers CASCADE;
-- NOTICEで落ちる対象が表示される → 直後にROLLBACKすれば「何が消えるかのプレビュー」に使える
BEGIN;
DROP TABLE public.customers CASCADE;
-- NOTICEを確認
ROLLBACK; -- 本番前の安全確認として有用・CASCADEは便利だが影響範囲が広い。必ずトランザクション内で確認→必要ならROLLBACK
効かない回避策(落とし穴)
・ALTER TABLE … DISABLE TRIGGERやSET CONSTRAINTS DEFERREDは実行時チェックの制御。カタログ上の依存関係は残るためDROPの阻害要因は解消しない
・session_replication_role = replica でも同様。DROP時の依存チェックは回避不可
スキーマ跨り/多数テーブルの依存を一括抽出
-- DB全体で「誰が誰を参照しているか」を一覧化
SELECT
con.conrelid::regclass AS referencing_table,
con.confrelid::regclass AS referenced_table,
con.conname,
pg_get_constraintdef(con.oid) AS def
FROM pg_constraint con
WHERE con.contype='f'
ORDER BY referencing_table::text, referenced_table::text;・スキーマを跨って依存がある場合、DROPの順序計画を作る材料にする
パーティション/継承テーブルの注意点
・パーティション親を参照するFKは親側に定義(バージョンにより挙動差あり)。親DROPにはそのFKの解除が必要
・親をDROPするには、子パーティションのDETACH→個別DROP、または親DROP CASCADEで連鎖削除
-- 親のDROP手順例(安全側)
ALTER TABLE sales DETACH PARTITION sales_2025q1;
DROP TABLE sales_2025q1;
-- 参照FKを解除後に親DROP
ALTER TABLE some_table DROP CONSTRAINT some_fk;
DROP TABLE sales;代表的な運用フロー(安全版チェックリスト)
1) 対象表をregclassで特定(schema付)
2) 依存FKをSQLで一覧化し、影響範囲をチームで確認
3) 参照元テーブルを先にDROPするか、FKのみDROPするかを決定
4) 必要ならBEGIN内でDROP … CASCADEを試し、NOTICEで確認→ROLLBACK
5) 本番適用:メンテ時間帯に実施し、アプリ接続を停止/限定
6) 必要に応じてFKの再作成スクリプトを適用
FK再作成用スクリプトの保存と復元
-- 1) 参照先=対象表のFK定義を保存
WITH target AS (SELECT 'public.customers'::regclass AS rel)
SELECT con.conrelid::regclass AS referencing_table,
con.conname,
'ALTER TABLE '||con.conrelid::regclass||' ADD CONSTRAINT '||
quote_ident(con.conname)||' '||pg_get_constraintdef(con.oid)||';' AS add_ddl
INTO TEMP TABLE fk_backup
FROM pg_constraint con JOIN target t ON con.confrelid=t.rel
WHERE con.contype='f';
-- 2) FKをDROPして表をDROP
SELECT 'ALTER TABLE '||con.conrelid::regclass||' DROP CONSTRAINT '||quote_ident(con.conname)||';'
FROM pg_constraint con JOIN target t ON con.confrelid=t.rel
WHERE con.contype='f';
DROP TABLE public.customers;
-- 3) 必要なら後で復元
SELECT add_ddl FROM fk_backup; -- 出力を実行依存FKのDROPを自動実行(DOブロック)
DO $$
DECLARE r record;
BEGIN
FOR r IN
SELECT conrelid::regclass AS tbl, conname
FROM pg_constraint
WHERE contype='f' AND confrelid = 'public.customers'::regclass
LOOP
EXECUTE format('ALTER TABLE %s DROP CONSTRAINT %I;', r.tbl, r.conname);
END LOOP;
END$$;
-- その後 DROP TABLE public.customers;NG→OK 早見表(よくあるパターン)
-- P1: 直接DROP
-- NG: DROP TABLE customers;
-- OK: ALTER TABLE orders DROP CONSTRAINT orders_customer_id_fkey; DROP TABLE customers;
-- P2: 依存が多すぎる
-- NG: 手作業で探す
-- OK: pg_constraint/pg_get_constraintdef で一覧化→自動生成DDL
-- P3: とりあえずCASCADE
-- NG: いきなり本番でCASCADE
-- OK: BEGIN内でCASCADE→NOTICE確認→ROLLBACK→影響承認→本番適用
-- P4: トリガ無効化で回避できるはず
-- NG: ALTER TABLE ... DISABLE TRIGGER ALL; DROP TABLE ...
-- OK: 依存はカタログ管理。FK解除 or 参照元DROP or CASCADEのいずれか補足:削除ではなく置換/再設計の選択肢
・データ移行が目的なら、新表作成→データコピー→FK先を切替→旧表DROPの順が安全
・ON DELETE CASCADEへ設計変更しておくと、行削除時の運用は簡易になる(表DROPの可否とは別軸)
-
前の記事
PostgreSQL『ERROR: cannot execute SELECT FOR UPDATE』の原因と対処 2025.09.10
-
次の記事
PostgreSQL「syntax error in tsquery」の原因と対処 2025.09.11
コメントを書く