記事の目的
SQL の ALTER TABLE 文を使って、既存テーブルの構造(列・データ型・制約・テーブル名)を変更できるようになります。
列の追加( ADD )、削除( DROP )、型変更、列名・テーブル名のリネーム、制約の追加・削除までを、PostgreSQL・MySQL・SQL Server の製品差に注意しながら整理します。ALTER TABLE は製品差がとくに大きい構文のため、製品ごとの書き方を比較表でまとめます。
重要な注意(本記事の操作はすべて DDL です)
ALTER TABLE はテーブルの定義そのものを変える操作です。実行すると元に戻せない変更が含まれ、本番環境では他のクエリのロック待ち・データ消失・想定外のダウンタイムを引き起こすことがあります。
本記事のコードを本番環境で動かす前に、必ずバックアップを取得し、テスト環境で同じ操作を試してから実行してください。とくに DROP COLUMN ・列の型変更・ NOT NULL 制約の追加は、データ消失や長時間ロックのリスクが高い操作です。
記事のサンプルテーブルの前提
本記事では、説明用に次のような users テーブルがある前提で進めます。
実際の CREATE TABLE 文は別記で扱います。本記事では、この users テーブルに対して列を足したり、型を変えたり、リネームしたりしていきます。
最もよく使う4パターンを並べます。詳しい説明・製品差は後述します。
-- 列を追加(age 列を整数型で追加)
ALTER TABLE users ADD COLUMN age INTEGER;
-- 列を削除
ALTER TABLE users DROP COLUMN age;
-- PostgreSQL: age 列の型を BIGINT に変える
ALTER TABLE users ALTER COLUMN age TYPE BIGINT;
-- PostgreSQL: name 列を full_name にリネーム
ALTER TABLE users RENAME COLUMN name TO full_name;MySQL / SQL Server での書き方は後述の比較表で示します。
-- 列を追加(age 列を整数型で追加)
ALTER TABLE users ADD COLUMN age INTEGER;
-- 列を削除
ALTER TABLE users DROP COLUMN age;-- PostgreSQL: age 列の型を BIGINT に変える
ALTER TABLE users ALTER COLUMN age TYPE BIGINT;
-- PostgreSQL: name 列を full_name にリネーム
ALTER TABLE users RENAME COLUMN name TO full_name;ここから先は、操作の種類ごとに構文と製品差を整理します。
新しい列をテーブルに追加します。最も使う頻度が高い ALTER TABLE です。
ALTER TABLE テーブル名 ADD COLUMN 列名 データ型 [制約];DEFAULT )・ NOT NULL などの制約をその場で付けることもできます。NOT NULL 列を追加する場合は、既存行に入れる値が必要になります。小さなテーブルでは DEFAULT 付きで追加できますが、大きなテーブルでは「NULL 許容で列を追加 → UPDATE で値を埋める → NOT NULL 制約を追加」のように段階的に変更したほうが、ロック影響と失敗時のリカバリの面で安全な場合があります。| 製品 | 構文 |
|---|---|
| PostgreSQL | ALTER TABLE users ADD COLUMN age INTEGER; |
| MySQL | ALTER TABLE users ADD COLUMN age INT; ( COLUMN は省略可) |
| SQL Server | ALTER TABLE users ADD age INT; ( COLUMN キーワードを付けない) |
3製品共通で動かしたい場合は、上記の表のように製品ごとに書き分けます。
-- PostgreSQL / MySQL(DEFAULT 付きで列を追加。既存行にも DEFAULT 値が入る)
ALTER TABLE users
ADD COLUMN created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP;-- SQL Server(COLUMN は省略)
ALTER TABLE users
ADD created_at DATETIME2 DEFAULT SYSUTCDATETIME();DEFAULT 付きで列を追加すると、既存行を参照したときにも既定値が入ったように扱われます。実際にテーブルを物理的に書き換えるか・どの程度のロックが発生するかは、製品・バージョン・ DEFAULT 式の内容(定数か関数か)によって異なります。大きなテーブルで実行する前に、対象環境の挙動をテスト環境で確認してください。
列の削除はデータの完全削除です。一度削除した列は元に戻せません。
ALTER TABLE users DROP COLUMN memo;| 製品 | 構文 |
|---|---|
| PostgreSQL | ALTER TABLE users DROP COLUMN memo [CASCADE]; |
| MySQL | ALTER TABLE users DROP COLUMN memo; ( COLUMN は省略可) |
| SQL Server | ALTER TABLE users DROP COLUMN memo; |
DROP する必要があります。PostgreSQL は CASCADE を付けると連鎖的に削除しますが、影響範囲が広がるので慎重に。「本当に消してよいか」を確認してから実行する習慣をおすすめします。
列の型を変えたい場合の書き方は、3製品で異なります。
| 製品 | 構文 |
|---|---|
| PostgreSQL | ALTER TABLE users ALTER COLUMN age TYPE BIGINT [USING 変換式]; |
| MySQL | ALTER TABLE users MODIFY COLUMN age BIGINT; |
| SQL Server | ALTER TABLE users ALTER COLUMN age BIGINT; |
-- PostgreSQL: 整数列を文字列に変える例(USING で明示的に変換)
ALTER TABLE users
ALTER COLUMN age TYPE TEXT USING age::TEXT;-- MySQL: 列定義を上書きする(NULL 許容や DEFAULT も同時に指定)
ALTER TABLE users
MODIFY COLUMN age BIGINT NOT NULL DEFAULT 0;BIGINT → INT への変更は、大きな値が入っていれば失敗します。USING 句で「どう変換するか」を明示できます(暗黙変換できない場合は必須)。ALGORITHM=INPLACE 、PostgreSQL では特定の型同士の変換が高速化される条件など、各製品のドキュメントで挙動を確認してください。列名を変える操作は、3製品で書き方がまったく違います。
| 製品 | 構文 |
|---|---|
| PostgreSQL | ALTER TABLE users RENAME COLUMN name TO full_name; |
| MySQL(8.0 以降) | ALTER TABLE users RENAME COLUMN name TO full_name; |
| SQL Server | EXEC sp_rename 'users.name', 'full_name', 'COLUMN'; ( ALTER TABLE ではなくストアドプロシージャ) |
MySQL 8.0 以降では RENAME COLUMN が使えます。古い MySQL 5.x 系 では CHANGE COLUMN を使い、ALTER TABLE users CHANGE COLUMN name full_name VARCHAR(255) NOT NULL; のように型と制約を再指定する必要があります(現行の定義を SHOW CREATE TABLE 等で確認してから実行)。
SQL Server だけは ALTER TABLE ではなく sp_rename ストアドプロシージャを使います。sp_rename は列名・テーブル名そのものは変更しますが、その名前を参照しているビュー・ストアドプロシージャ・トリガー・アプリケーション側 SQL までは自動で直してくれません。リネーム前に依存関係を確認し、必要なら関連するオブジェクトも同時に修正します。
制約の追加・削除は製品差が大きく、特に外部キーや DEFAULT 制約は個別の注意点があります。本記事では代表的な構文の入口だけを示し、詳しい挙動や運用上のコツは各制約の個別記事で扱います。
| 製品 | 追加 | 削除 |
|---|---|---|
| PostgreSQL | ALTER COLUMN age SET NOT NULL | ALTER COLUMN age DROP NOT NULL |
| MySQL | MODIFY COLUMN age INT NOT NULL (型ごと再指定) | MODIFY COLUMN age INT NULL |
| SQL Server | ALTER COLUMN age INT NOT NULL | ALTER COLUMN age INT NULL |
CONSTRAINT 名を付けて追加すると、あとで削除しやすくなります。
-- UNIQUE 制約を追加(3 製品共通)
ALTER TABLE users ADD CONSTRAINT uq_users_email UNIQUE (email);
-- 外部キー制約を追加(3 製品共通)
ALTER TABLE orders
ADD CONSTRAINT fk_orders_user
FOREIGN KEY (user_id) REFERENCES users(id);
-- 制約を削除(PostgreSQL / SQL Server)
ALTER TABLE users DROP CONSTRAINT uq_users_email;
-- MySQL の制約削除(種類で構文が違う)
ALTER TABLE users DROP INDEX uq_users_email; -- UNIQUE / INDEX
ALTER TABLE orders DROP FOREIGN KEY fk_orders_user; -- 外部キー| 製品 | 既定値を設定 | 既定値を削除 |
|---|---|---|
| PostgreSQL | ALTER COLUMN age SET DEFAULT 0 | ALTER COLUMN age DROP DEFAULT |
| MySQL | ALTER COLUMN age SET DEFAULT 0 | ALTER COLUMN age DROP DEFAULT |
| SQL Server | 既存の DEFAULT 制約を DROP CONSTRAINT してから ADD CONSTRAINT … DEFAULT … を追加 | 同上 |
SQL Server は DEFAULT も「制約」として管理されているため、変更時は古い制約を削除して新しく追加し直すのが定石です。DEFAULT 制約に名前を明示せずに作成した場合は自動生成名が付くため、削除前に sys.default_constraints などのシステムカタログ、または SSMS で実際の制約名を確認してから DROP CONSTRAINT を実行します。
| 製品 | 構文 |
|---|---|
| PostgreSQL | ALTER TABLE users RENAME TO app_users; |
| MySQL | ALTER TABLE users RENAME TO app_users; または RENAME TABLE users TO app_users; |
| SQL Server | EXEC sp_rename 'users', 'app_users'; |
MySQL では複数テーブルを一気に入れ替えたい場合、 RENAME TABLE のほうが原子的に動くため安全と言われます。
-- MySQL: アプリ無停止での切り替えに使われるイディオム
RENAME TABLE users TO users_old, users_new TO users;以下は本番環境で実際に問題になりやすいポイントです。実行前に必ず確認してください。
DROP COLUMN は完全な削除です。元に戻したい場合は、削除前に取得していたバックアップから復元するしかありません。CASCADE を使うと、関連する外部キー・ビュー・依存オブジェクトまで削除されます。
影響範囲は、PostgreSQL では psql の \d メタコマンド(PostgreSQL 専用)、3製品共通では INFORMATION_SCHEMA /各製品のシステムカタログで確認してから実行します。
大きなテーブルへの ALTER TABLE は、テーブル全体に長時間のロックを取ることがあります。挙動は製品とバージョンで大きく異なります。
ALGORITHM=INPLACE / LOCK=NONE をヒントとして指定できる操作があります。ALTER は軽い操作(メタデータ変更のみ)ですが、一部の型変更や NOT NULL の追加などはテーブル書き換えが発生し、長時間ロックの原因になります。WITH (ONLINE = ON) )など、Enterprise エディションでのオプションがあります。本番運用では、メンテナンスウィンドウ・オンラインでの段階的変更・ pt-online-schema-change (MySQL)/ pg-osc などのツール利用を検討してください。
既存行に NULL が入っている列に NOT NULL 制約を追加すると、エラーになります。先に UPDATE で値を埋めるか、 DEFAULT を同時に指定して既存行を埋めます。
-- PostgreSQL の例:先に既存行を埋めてから NOT NULL を追加
UPDATE users SET age = 0 WHERE age IS NULL;
ALTER TABLE users ALTER COLUMN age SET NOT NULL;ALTER TABLE がトランザクション可能で、 ROLLBACK できます。複数のスキーマ変更を 1 つのトランザクションでまとめると安全です。ROLLBACK できません。失敗しても元に戻らないため、テスト環境での事前確認が必須です。MySQL には型変更系の構文が 2 つあります。混同しないように区別します。
MODIFY COLUMN :列の定義(型・制約)を変える。列名は変えない。CHANGE COLUMN :列の名前と定義を同時に変える。リネームしないときも、必ず新しい列名(古い名前と同じでも可)を指定する必要があります。ALTER TABLE はテーブル構造を変える DDL ですが、 列追加( ADD )・列削除( DROP )・型変更・リネーム・制約の追加/削除の5パターンを軸に押さえ、3製品の構文差を比較表で確認すれば、現場の構造変更はほぼ書けるようになります。本番環境では、バックアップ・テスト環境での事前確認・ロック影響の見積もりを必ず行ってください。