MeWrite Docs

Cannot add or update a child row: a foreign key constraint fails

MySQLで外部キー制約違反が発生した場合のエラー

概要

Cannot add or update a child row: a foreign key constraint fails は、外部キー制約に違反するデータを挿入・更新しようとした場合に発生します。参照先のレコードが存在しない場合に発生します。

エラーメッセージ

ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`database`.`orders`, CONSTRAINT `orders_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`))
ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails

原因

1. 参照先のレコードが存在しない

1
2
3
-- usersテーブルにid=999は存在しない
INSERT INTO orders (user_id, amount) VALUES (999, 1000);
-- Error: Cannot add or update a child row

2. 参照されているレコードを削除

1
2
3
-- このユーザーを参照しているordersがある
DELETE FROM users WHERE id = 1;
-- Error: Cannot delete or update a parent row

3. データ型の不一致

1
2
3
-- usersのidがINTなのに、ordersのuser_idがBIGINT
ALTER TABLE orders ADD FOREIGN KEY (user_id) REFERENCES users(id);
-- Error: Cannot add foreign key constraint

解決策

1. 参照先のデータを先に挿入

1
2
3
4
5
-- 先にユーザーを作成
INSERT INTO users (id, name) VALUES (1, 'John');

-- その後で注文を作成
INSERT INTO orders (user_id, amount) VALUES (1, 1000);

2. ON DELETE CASCADEを設定

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
-- テーブル作成時
CREATE TABLE orders (
    id INT PRIMARY KEY AUTO_INCREMENT,
    user_id INT,
    amount DECIMAL(10, 2),
    FOREIGN KEY (user_id) REFERENCES users(id)
        ON DELETE CASCADE
        ON UPDATE CASCADE
);

-- 既存テーブルに追加
ALTER TABLE orders
DROP FOREIGN KEY orders_user_id_foreign;

ALTER TABLE orders
ADD CONSTRAINT orders_user_id_foreign
FOREIGN KEY (user_id) REFERENCES users(id)
    ON DELETE CASCADE
    ON UPDATE CASCADE;

3. ON DELETE SET NULLを設定

1
2
3
4
5
6
-- 親が削除されたらNULLにする
ALTER TABLE orders
ADD CONSTRAINT orders_user_id_foreign
FOREIGN KEY (user_id) REFERENCES users(id)
    ON DELETE SET NULL
    ON UPDATE CASCADE;

4. 一時的に外部キーチェックを無効化

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
-- 外部キーチェックを無効化(データ移行時など)
SET FOREIGN_KEY_CHECKS = 0;

-- データ操作
INSERT INTO orders (user_id, amount) VALUES (999, 1000);

-- 外部キーチェックを再有効化
SET FOREIGN_KEY_CHECKS = 1;

-- 警告: 整合性が崩れる可能性があるため、本番では慎重に使用

5. 削除順序を守る

1
2
3
4
5
6
7
8
9
-- 子テーブルから先に削除
DELETE FROM orders WHERE user_id = 1;
DELETE FROM users WHERE id = 1;

-- トランザクションで一括
START TRANSACTION;
DELETE FROM orders WHERE user_id = 1;
DELETE FROM users WHERE id = 1;
COMMIT;

外部キー制約の確認

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
-- テーブルの外部キーを確認
SHOW CREATE TABLE orders;

-- 制約の詳細を確認
SELECT * FROM information_schema.TABLE_CONSTRAINTS
WHERE TABLE_NAME = 'orders' AND CONSTRAINT_TYPE = 'FOREIGN KEY';

-- 参照関係を確認
SELECT * FROM information_schema.KEY_COLUMN_USAGE
WHERE TABLE_NAME = 'orders' AND REFERENCED_TABLE_NAME IS NOT NULL;

ON DELETE/UPDATE オプション

オプション動作
RESTRICT削除/更新を禁止(デフォルト)
CASCADE子レコードも削除/更新
SET NULL子の外部キーをNULLに設定
SET DEFAULT子の外部キーをデフォルト値に設定
NO ACTIONRESTRICTと同じ

フレームワークでの対処

Laravel

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// マイグレーション
Schema::create('orders', function (Blueprint $table) {
    $table->id();
    $table->foreignId('user_id')
        ->constrained()
        ->onDelete('cascade');
    $table->decimal('amount', 10, 2);
});

// 外部キーを削除
Schema::table('orders', function (Blueprint $table) {
    $table->dropForeign(['user_id']);
});

Django

1
2
3
4
5
6
7
8
class Order(models.Model):
    user = models.ForeignKey(
        User,
        on_delete=models.CASCADE,  # 親削除時に子も削除
        # on_delete=models.SET_NULL, null=True,  # NULLに設定
        # on_delete=models.PROTECT,  # 削除を禁止
    )
    amount = models.DecimalField(max_digits=10, decimal_places=2)

Sequelize

1
2
3
4
5
Order.belongsTo(User, {
    foreignKey: 'user_id',
    onDelete: 'CASCADE',
    onUpdate: 'CASCADE'
});

デバッグ

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
-- 孤立したレコードを検出
SELECT o.* FROM orders o
LEFT JOIN users u ON o.user_id = u.id
WHERE u.id IS NULL;

-- 不整合データを修正
DELETE FROM orders
WHERE user_id NOT IN (SELECT id FROM users);

-- または NULL に設定
UPDATE orders
SET user_id = NULL
WHERE user_id NOT IN (SELECT id FROM users);

関連エラー

関連エラー

MySQL の他のエラー

最終更新: 2025-12-23