9.

SQL 外部キー (FOREIGN KEY) の追加・削除・制約名の確認|MySQL / PostgreSQL

編集
この記事の要点
  • SQL で外部キーの追加・削除と制約名の確認方法
  • 追加: ALTER TABLE t ADD FOREIGN KEY (col) REFERENCES other(col)
  • 制約名確認: information_schema.TABLE_CONSTRAINTS または SHOW CREATE TABLE
  • 削除: ALTER TABLE t DROP FOREIGN KEY 制約名(MySQL)/ DROP CONSTRAINT(PostgreSQL)
  • カスケード: ON DELETE CASCADE / ON UPDATE CASCADE でリレーション維持

外部キーの追加

-- 基本構文
ALTER TABLE orders
ADD FOREIGN KEY (user_id) REFERENCES users(id);

-- 制約名を明示(推奨:あとで削除が楽)
ALTER TABLE orders
ADD CONSTRAINT fk_orders_user_id
FOREIGN KEY (user_id) REFERENCES users(id);

-- カスケード動作付き
ALTER TABLE orders
ADD CONSTRAINT fk_orders_user_id
FOREIGN KEY (user_id) REFERENCES users(id)
    ON DELETE CASCADE
    ON UPDATE CASCADE;

-- 削除時は NULL に
ALTER TABLE orders
ADD CONSTRAINT fk_orders_user_id
FOREIGN KEY (user_id) REFERENCES users(id)
    ON DELETE SET NULL;

-- テーブル作成時に同時定義
CREATE TABLE orders (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    user_id BIGINT,
    amount DECIMAL(10, 2),
    CONSTRAINT fk_orders_user_id
        FOREIGN KEY (user_id) REFERENCES users(id)
);

外部キー制約名の確認

MySQL

-- SHOW CREATE TABLE
SHOW CREATE TABLE orders;
-- → 出力中の CONSTRAINT `fk_xxx` FOREIGN KEY ... を確認

-- information_schema 経由
SELECT
    CONSTRAINT_NAME,
    TABLE_NAME,
    COLUMN_NAME,
    REFERENCED_TABLE_NAME,
    REFERENCED_COLUMN_NAME
FROM information_schema.KEY_COLUMN_USAGE
WHERE TABLE_SCHEMA = 'mydb'
  AND TABLE_NAME = 'orders'
  AND REFERENCED_TABLE_NAME IS NOT NULL;

-- カスケード設定込みで確認
SELECT
    CONSTRAINT_NAME,
    UPDATE_RULE,
    DELETE_RULE
FROM information_schema.REFERENTIAL_CONSTRAINTS
WHERE CONSTRAINT_SCHEMA = 'mydb'
  AND TABLE_NAME = 'orders';

PostgreSQL

-- psql の \d
\d orders

-- system catalog
SELECT
    conname,
    pg_get_constraintdef(c.oid)
FROM pg_constraint c
JOIN pg_class t ON c.conrelid = t.oid
WHERE t.relname = 'orders'
  AND c.contype = 'f';

-- information_schema
SELECT
    tc.constraint_name,
    kcu.column_name,
    ccu.table_name AS foreign_table,
    ccu.column_name AS foreign_column
FROM information_schema.table_constraints tc
JOIN information_schema.key_column_usage kcu
    ON tc.constraint_name = kcu.constraint_name
JOIN information_schema.constraint_column_usage ccu
    ON ccu.constraint_name = tc.constraint_name
WHERE tc.table_name = 'orders'
  AND tc.constraint_type = 'FOREIGN KEY';

外部キーの削除

MySQL

-- 制約名を指定して削除
ALTER TABLE orders DROP FOREIGN KEY fk_orders_user_id;

-- 暗黙的に作られたインデックスは別途削除(必要に応じて)
ALTER TABLE orders DROP INDEX fk_orders_user_id;

PostgreSQL / 標準 SQL

ALTER TABLE orders DROP CONSTRAINT fk_orders_user_id;

ON DELETE / ON UPDATE のオプション

オプション動作
NO ACTION (デフォルト)参照先が変わったらエラーになる(安全)
RESTRICTNO ACTION とほぼ同じ。即時チェック
CASCADE参照先と同じ動作を伝播(親削除なら子も削除)
SET NULL参照元を NULL に書き換える(NULL 許可カラムのみ)
SET DEFAULT参照元をデフォルト値に(PG のみ)

外部キー一時無効化(インポート時等)

-- MySQL: セッション単位
SET FOREIGN_KEY_CHECKS = 0;
-- 大量 INSERT / TRUNCATE 等
SET FOREIGN_KEY_CHECKS = 1;  -- 戻す

-- PostgreSQL: トランザクション内で
BEGIN;
SET CONSTRAINTS ALL DEFERRED;
-- ... 処理
COMMIT;

-- ★ 注意: 制約を切ると整合性が失われる。データ整合確認後に有効化

Laravel マイグレーションでの書き方

// マイグレーション: 外部キー追加
public function up() {
    Schema::table('orders', function (Blueprint $table) {
        // 短縮形(Laravel 7+)
        $table->foreignId('user_id')->constrained()->onDelete('cascade');

        // 明示的に書く場合
        $table->unsignedBigInteger('product_id');
        $table->foreign('product_id')
              ->references('id')->on('products')
              ->onDelete('restrict')
              ->onUpdate('cascade');
    });
}

// マイグレーション: 外部キー削除
public function down() {
    Schema::table('orders', function (Blueprint $table) {
        // 命名規則: テーブル名_カラム名_foreign
        $table->dropForeign(['user_id']);
        // または制約名指定
        $table->dropForeign('orders_user_id_foreign');
    });
}

外部キー追加でエラーになる典型例

エラー原因
Cannot add foreign key constraint参照先テーブル / カラムの不一致(型・charset・collation)
Foreign key constraint fails既存データに参照先に存在しない値がある → 先に整合性確保
Duplicate foreign key constraint name制約名が重複 → 名前を変える
Cannot delete or update a parent row参照されている行を削除しようとしている → CASCADE か事前に子行削除

パフォーマンスへの影響

  • 暗黙的にインデックスが作られる(MySQL): 外部キーカラムには自動で BTREE INDEX
  • INSERT / UPDATE 時に参照チェック: 親テーブルの存在確認で若干のオーバーヘッド
  • 大量 INSERT は遅くなる: バッチインポート時は一時的に無効化
  • カスケード削除は重い: 数十万行の連鎖削除は時間がかかる
  • 本番運用は注意深く: アプリ側ロジックで整合性を保つ方が柔軟な場合も
編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. テーブル作成
  2. カラムの定義変更
  3. カラム名の変更
  4. カラム追加 / 削除
  5. インデックスの作成
  6. インデックスの削除
  7. AUTO_INCREMENTの仕様について
  8. AUTO_INCREMENTの追加と確認方法
  9. 外部キーの追加/削除と制約名の確認方法