4.

SQL カラム追加・削除 (ALTER TABLE) 完全ガイド

編集
この記事の要点
  • カラム追加: ALTER TABLE t ADD col TYPE [DEFAULT ...] [NOT NULL]
  • カラム削除: ALTER TABLE t DROP COLUMN col
  • MySQL は AFTER other_col / FIRSTカラム位置指定可、PostgreSQL は末尾追加のみ
  • 巨大テーブルはテーブルロックを取るため要注意 → Online DDL / pt-online-schema-change / gh-ost
  • ローリングデプロイでは「追加 → アプリ更新 → 削除」の順で互換性を保つ

カラム追加 (ADD COLUMN)

-- 基本: 末尾にカラム追加
ALTER TABLE users ADD COLUMN email VARCHAR(255);

-- NOT NULL + DEFAULT 必須 (既存行のため)
ALTER TABLE users ADD COLUMN status VARCHAR(20) NOT NULL DEFAULT 'active';

-- NULL 可なら DEFAULT 省略可
ALTER TABLE users ADD COLUMN phone VARCHAR(20) NULL;

-- 複数カラムを一度に
ALTER TABLE users
    ADD COLUMN first_name VARCHAR(100),
    ADD COLUMN last_name  VARCHAR(100);

-- 制約付き
ALTER TABLE users ADD COLUMN age INT CHECK (age >= 0);
ALTER TABLE users ADD COLUMN email VARCHAR(255) UNIQUE;
ALTER TABLE orders ADD COLUMN user_id BIGINT,
    ADD CONSTRAINT fk_user FOREIGN KEY (user_id) REFERENCES users(id);

カラム位置の指定 (MySQL のみ)

MySQL / MariaDB は AFTER / FIRST でカラム位置を指定できます。PostgreSQL / Oracle / SQL Server はテーブル末尾にしか追加できません:

-- MySQL: 既存カラムの後ろに
ALTER TABLE users ADD COLUMN nickname VARCHAR(50) AFTER name;

-- MySQL: 先頭に
ALTER TABLE users ADD COLUMN uuid CHAR(36) FIRST;

-- PostgreSQL: 位置指定不可 (常に末尾)
ALTER TABLE users ADD COLUMN nickname VARCHAR(50);  -- 末尾のみ

-- PostgreSQL で順序を変えたい場合は: 新テーブル作成 → INSERT SELECT
-- → 旧テーブル DROP → 新テーブル RENAME
-- (実用上はカラム順序は気にせずビュー側で並べる)

カラム削除 (DROP COLUMN)

-- 1 カラム削除
ALTER TABLE users DROP COLUMN phone;

-- 複数削除
ALTER TABLE users
    DROP COLUMN phone,
    DROP COLUMN fax;

-- PostgreSQL は CASCADE で依存ビュー等も削除
ALTER TABLE users DROP COLUMN phone CASCADE;

-- IF EXISTS で安全に (PG/MySQL 8 で対応)
ALTER TABLE users DROP COLUMN IF EXISTS phone;

カラム型・名前変更

-- MySQL: MODIFY (型のみ変更)
ALTER TABLE users MODIFY COLUMN name VARCHAR(200) NOT NULL;

-- MySQL: CHANGE (名前+型変更)
ALTER TABLE users CHANGE COLUMN name full_name VARCHAR(200) NOT NULL;

-- PostgreSQL: 個別構文
ALTER TABLE users ALTER COLUMN name TYPE VARCHAR(200);
ALTER TABLE users ALTER COLUMN name SET NOT NULL;
ALTER TABLE users ALTER COLUMN name DROP NOT NULL;
ALTER TABLE users ALTER COLUMN name SET DEFAULT 'unknown';
ALTER TABLE users RENAME COLUMN name TO full_name;

-- SQL Server
ALTER TABLE users ALTER COLUMN name VARCHAR(200) NOT NULL;
EXEC sp_rename 'users.name', 'full_name', 'COLUMN';

巨大テーブルでの注意

数千万行以上のテーブルで ALTER TABLE を実行すると、テーブルロックで数時間〜数日サービス停止することがあります。

操作MySQL 5.7 以前MySQL 8 Online DDL
NULL 可カラム追加テーブルコピー (長時間)INSTANT (即時)
NOT NULL + DEFAULT カラム追加テーブルコピーINSTANT (条件付き)
カラム削除テーブルコピーINPLACE (行ロック無し)
カラム型変更テーブルコピーINPLACE or COPY

MySQL 8 Online DDL

-- INSTANT アルゴリズム指定 (一瞬で終わる)
ALTER TABLE users
    ADD COLUMN nickname VARCHAR(100),
    ALGORITHM=INSTANT;

-- INPLACE + LOCK=NONE で行ロック無し
ALTER TABLE users
    ADD INDEX idx_email (email),
    ALGORITHM=INPLACE,
    LOCK=NONE;

-- 失敗時のフォールバック制御
ALTER TABLE users
    DROP COLUMN deprecated_field,
    ALGORITHM=INPLACE,
    LOCK=NONE;
-- → サポート外操作だとエラー、明示することで意図しないテーブルコピーを防止

pt-online-schema-change / gh-ost (オンライン ALTER)

MySQL 5.7 以前や、Online DDL 非対応の操作には外部ツールを使います。新テーブルを作って差分を同期しながらコピー → 切替の仕組み:

# Percona Toolkit の pt-online-schema-change
pt-online-schema-change \
  --alter "ADD COLUMN email VARCHAR(255)" \
  D=mydb,t=users \
  --execute

# GitHub の gh-ost (バイナリログベース、より安全)
gh-ost \
  --database=mydb --table=users \
  --alter="ADD COLUMN email VARCHAR(255)" \
  --execute

# どちらも:
# 1. _users_new という新テーブル作成
# 2. 差分をトリガー or binlog で同期
# 3. 完了後 RENAME で切替
# 4. 旧テーブルを削除

ローリングデプロイの順序

本番運用中のサービスでカラムを変更する場合、アプリと DB の互換性を保つ必要があります。鉄則は「追加は先、削除は後」:

カラム追加

  1. DB 側: 新カラム追加 (NULL 可 or DEFAULT 付き) → 旧アプリはこのカラムを知らないが書き込みは成功
  2. アプリデプロイ: 新カラムを読み書きするコード
  3. 必要ならバックフィル (既存行に値を埋める UPDATE)
  4. NOT NULL 化が必要なら、ここで ALTER ... NOT NULL

カラム削除

  1. アプリデプロイ: そのカラムを読み書きしないコードに変更
  2. 本番で問題ないことを確認 (数日〜1週間)
  3. DB 側: カラム削除
-- ❌ NG: アプリより先に DB のカラムを消す
ALTER TABLE users DROP COLUMN email;
-- → 旧バージョンのアプリが SELECT email でエラー連発

-- ✅ OK: アプリを先に「email を読まない」に更新してから DB DROP
-- (Blue/Green デプロイ・カナリアでも同様)

Laravel マイグレーションでの例

// php artisan make:migration add_email_to_users_table
public function up(): void
{
    Schema::table('users', function (Blueprint $table) {
        $table->string('email', 255)->nullable()->after('name');
        $table->index('email');
    });
}

public function down(): void
{
    Schema::table('users', function (Blueprint $table) {
        $table->dropIndex(['email']);
        $table->dropColumn('email');
    });
}

// 複数カラム削除
Schema::table('users', function (Blueprint $table) {
    $table->dropColumn(['phone', 'fax']);
});

// 型変更 (要 doctrine/dbal)
Schema::table('users', function (Blueprint $table) {
    $table->string('name', 200)->nullable(false)->change();
});

FAQ

Q: NOT NULL カラムを既存テーブルに追加できない
A: DEFAULT 値を必ず指定してください。ALTER TABLE t ADD col INT NOT NULL DEFAULT 0。既存行を埋める値が無いと失敗します。

Q: ALTER TABLE が終わらない
A: SHOW PROCESSLIST (MySQL) で進捗確認。Online DDL なら SELECT * FROM information_schema.innodb_alter_table_progress。長すぎる場合は kill して pt-online-schema-change に切替。

Q: カラム削除すると DB ファイルサイズが減らない
A: 多くの DB は「論理削除」のみ。物理的に小さくするには OPTIMIZE TABLE (MySQL) / VACUUM FULL (PostgreSQL) が必要。

編集
Post Share
子ページ

子ページはありません

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

最近更新/作成されたページ