できない.dev

MySQL で「Cannot delete or update a parent row」が出て行を削除できない

Cannot delete or update a parent row は、子テーブルの行を先に処理するか ON DELETE を設定すれば直る。
削除する行が他テーブルの外部キーから参照されているときに出る。

公開: 更新:

症状できない · exit 1
mysql -u root -v demo < bad.sql
--------------
DELETE FROM parent WHERE id = 10
--------------

ERROR 1451 (23000) at line 1: Cannot delete or update a parent row: a foreign key constraint fails (`demo`.`child`, CONSTRAINT `fk_child_parent` FOREIGN KEY (`parent_id`) REFERENCES `parent` (`id`))
解決後できた · exit 0
mysql -u root -t -v demo < solve_cascade.sql
…
| child_rows |
+------------+
|          0 |
+------------+

2026-10-09 に mysql:8.4(MySQL 8.4.11) で実際に打って取った出力。検証環境の詳細

要約

Cannot delete or update a parent row: a foreign key constraint fails(エラー 1451)は、削除・更新しようとした行が他のテーブルの外部キーから参照されているときに出る。

外部キーの参照動作を指定しない場合の既定は RESTRICT で、参照されている親の行の削除は拒否される。
子テーブル側を先に処理するか、外部キー定義に ON DELETE の動作を設定するのが解決策である。

実行例

子テーブル child が参照している parent の行を削除すると、ERROR 1451 (23000) のメッセージに参照元の child テーブルと制約名 fk_child_parent が表示されて終了コード 1 になり、information_schema.KEY_COLUMN_USAGE からも同じテーブルと制約名が引けた。
子の行を先に消すと親の削除は通り、同名の制約の DROP と ADD を 1 つの ALTER TABLE にまとめると ERROR 1826 で拒否されたが、2 文に分けて ON DELETE CASCADE を付け直すと親の削除に合わせて子の行が 0 件になっている。

$ mysql -u root -v demo < bad.sql
--------------
DELETE FROM parent WHERE id = 10
--------------
 
ERROR 1451 (23000) at line 1: Cannot delete or update a parent row: a foreign key constraint fails (`demo`.`child`, CONSTRAINT `fk_child_parent` FOREIGN KEY (`parent_id`) REFERENCES `parent` (`id`))
$ echo $?
1
$ mysql -u root -t demo < find_ref.sql
+------------+-------------+-----------------+
| TABLE_NAME | COLUMN_NAME | CONSTRAINT_NAME |
+------------+-------------+-----------------+
| child      | parent_id   | fk_child_parent |
+------------+-------------+-----------------+
$ mysql -u root -v demo < solve_child_first.sql
--------------
DELETE FROM child WHERE parent_id = 10
--------------
 
--------------
DELETE FROM parent WHERE id = 10
--------------
$ echo $?
0
$ mysql -u root -v demo < solve_cascade_one_statement.sql
--------------
ALTER TABLE child
  DROP FOREIGN KEY fk_child_parent,
  ADD CONSTRAINT fk_child_parent
    FOREIGN KEY (parent_id) REFERENCES parent(id)
    ON DELETE CASCADE
--------------
 
ERROR 1826 (HY000) at line 1: Duplicate foreign key constraint name 'fk_child_parent'
$ echo $?
1
$ mysql -u root -t -v demo < solve_cascade.sql
--------------
ALTER TABLE child DROP FOREIGN KEY fk_child_parent
--------------
 
--------------
ALTER TABLE child
  ADD CONSTRAINT fk_child_parent
    FOREIGN KEY (parent_id) REFERENCES parent(id)
    ON DELETE CASCADE
--------------
 
--------------
DELETE FROM parent WHERE id = 20
--------------
 
--------------
SELECT COUNT(*) AS child_rows FROM child
--------------
 
+------------+
| child_rows |
+------------+
|          0 |
+------------+
$ echo $?
0

— 2026-10-09 時点の出力

検証環境MySQL 8.4.11 / 2026-10-09 検証
検証日
実行環境
mysql:8.4 Oracle Linux Server 9.8
バージョン
  • MySQL 8.4.11

この記事の「実行例」は、上記の環境で実際にコマンドを実行して得られた出力をそのまま掲載しています。 再現手順はリポジトリの検証スクリプトとして管理し、定期的に再実行して出力を更新しています。

解決策

1. 参照元のテーブルを特定する

SELECT TABLE_NAME, COLUMN_NAME, CONSTRAINT_NAME
FROM information_schema.KEY_COLUMN_USAGE
WHERE REFERENCED_TABLE_NAME = 'parent';

エラーメッセージにも制約名と参照元テーブルが表示されるので、まずどの外部キーが原因かを確定する。

2. 子テーブル側を先に処理する

DELETE FROM child WHERE parent_id = 10;
DELETE FROM parent WHERE id = 10;

参照している行が無くなれば、親の行は普通に削除できる。

3. 外部キーに ON DELETE の動作を設定する

ALTER TABLE child DROP FOREIGN KEY fk_child_parent;
ALTER TABLE child
  ADD CONSTRAINT fk_child_parent
    FOREIGN KEY (parent_id) REFERENCES parent(id)
    ON DELETE CASCADE;

同じ制約名で付け直すときは、DROP と ADD を別々の ALTER TABLE に分ける。
1 つの文にまとめると、まだ残っている元の制約と名前が重複するとみなされ、MySQL 8.4 と 8.0 のどちらでも ERROR 1826 (HY000): Duplicate foreign key constraint name 'fk_child_parent' で失敗する。
1 文で済ませたい場合は、追加する側に別の制約名を付ける。

CASCADE は親の削除に合わせて子の行も削除し、SET NULL は子の外部キー列を NULL にする(列が NULL 許容である必要がある)。
どの動作が適切かはデータの性質で決まる(FOREIGN KEY 制約の公式ドキュメント(新しいタブで開く))。

4. 一時的にチェックを外す(移行作業のみ)

SET FOREIGN_KEY_CHECKS = 0;
-- テーブル再作成やデータ移行など
SET FOREIGN_KEY_CHECKS = 1;

参照整合性が壊れたデータを作れてしまうため、日常の削除操作には使わない。
ダンプの取り込みやテーブル定義の作り直しなど、整合性を自分で保証できる場面に限る。

よくある原因

  1. 子の行が残っている: 削除したい親の行の主キーを、子テーブルの外部キー列がまだ参照している。
  2. 参照動作が未指定: ON DELETE を指定していない外部キーは RESTRICT 扱いになり、親側の削除・更新が拒否される。
  3. 参照元が不明: テーブルが多いと、どの外部キーが削除を妨げているのか分からなくなる。

この手順で直った?