既存テーブルにカラムを1本足すだけでしょ?」——そう思って本番DBで実行し、長時間ロックや想定外のエラーに慌てた経験はありませんか。
本記事では、ALTER TABLE ... ADD COLUMN
の基本構文から、FIRST
/ AFTER
による位置指定、NOT NULL
・デフォルト値の扱い、複数カラムの同時追加、オンラインDDLパラメータ(ALGORITHM
/ LOCK
)の使い方、そして大規模テーブルでも安全に進めるための実務的な手順やチェックリストまで、網羅的に解説します。
テーブル定義の変更(DDL)は、サイズの大きいテーブルでは長時間のテーブルロックや再構築を伴い、アプリケーションに影響を与える可能性があります。特に以下を意識しましょう。
NOT NULL
+ DEFAULT
を伴うと、全行の再書き込みが発生するケースがあるALGORITHM
により、オンラインで変更できるかどうかが変わる最も基本的な構文は次のとおりです。
ALTER TABLE テーブル名
ADD COLUMN 新カラム名 データ型 [NOT NULL | NULL] [DEFAULT デフォルト値];
例:
ALTER TABLE users
ADD COLUMN age INT NULL;
NULL
/ NOT NULL
の指定、DEFAULT
の有無で挙動やコストが変わる点を覚えておきましょう。
MySQLでは、カラムをテーブルの先頭や任意のカラムの後ろに差し込めます。
-- 先頭に追加
ALTER TABLE users
ADD COLUMN created_by VARCHAR(50) NULL FIRST;
-- 任意のカラムの後ろに追加
ALTER TABLE users
ADD COLUMN updated_by VARCHAR(50) NULL AFTER created_by;
注意:アプリ側が「SELECT * の列順」に依存していると破壊的変更になります。アプリで列名指定を徹底しましょう。
既存データに値がない状態で NOT NULL
を付けるとエラーになります。よく用いられる手順は以下。
ALTER TABLE users ADD COLUMN status TINYINT NULL;
UPDATE users SET status = 0 WHERE status IS NULL;
ALTER TABLE users MODIFY COLUMN status TINYINT NOT NULL DEFAULT 0;
この三段階に分けることで、テーブル再構築のコストやロック時間を抑えられるケースがあります(バージョン・データ量次第)。
DDLは1回にまとめた方がテーブル再構築の回数を減らせて効率的です。
ALTER TABLE orders
ADD COLUMN shipped_at DATETIME NULL,
ADD COLUMN shipped_by VARCHAR(100) NULL;
ただし、1回のDDLが長引くと影響範囲も広がるため、サイズ・時間・リスクのバランスは要検討です。
MySQL 5.6以降(InnoDB)では、オンラインDDLを部分的にサポートしています。MySQL 8.0系ではさらに改善されています。以下のように指定できます。
ALTER TABLE users
ALGORITHM=INPLACE,
LOCK=NONE
ADD COLUMN last_logged_in DATETIME NULL;
ALGORITHM=INPLACE
:テーブルコピーを避け、より低コストで変更できる可能性LOCK=NONE
:読み書き可能な状態を維持しながらDDLを実行(常に可能とは限らない)ポイント:指定したパラメータが非対応の場合、エラーで止まるので「静かにテーブルコピーに落ちる」より安全です。
pt-online-schema-change
(Percona Toolkit)などのツール利用の検討 MySQLでは計算結果を格納/非格納する生成列が使えます。
-- VIRTUAL(計算のみ、ストレージを消費しない)
ALTER TABLE sales
ADD COLUMN total_price DECIMAL(10,2)
AS (unit_price * quantity) VIRTUAL;
-- STORED(値を保存、インデックスも貼れる)
ALTER TABLE sales
ADD COLUMN total_price DECIMAL(10,2)
AS (unit_price * quantity) STORED;
STORED はテーブル再構築が必要になる場合があるため、オンラインDDL可否を確認しましょう。
{}
にしたい場合は DEFAULT (JSON_OBJECT())
と書けないバージョンもあるため、まずNULL許可で追加し、アプリ側で埋める運用も選択肢です。DESC テーブル名;
などで確認。NOT NULL
かつ DEFAULT
なしで既存行に値がない。三段階手順で回避。DDLは基本的にトランザクションで簡単にロールバックできません。戻したくなった際に即座に実行できるよう、逆方向のDDL を用意しておきましょう。
sqlコピーする編集するALTER TABLE users
DROP COLUMN status;
ただし、アプリ側が既に新カラムを参照していると逆に障害になります。デプロイ順序とフェーズドリリース(新旧スキーマ両対応期間を設ける)が鍵です。
NOT NULL
+ DEFAULT
は重いため、NULL許可 → 値埋め → NOT NULL化 の三段階戦略を検討ALGORITHM=INPLACE
/ LOCK=NONE
を明示し、非対応ならエラーで止める