SQL ALTER TABLEの使い方|列の追加・削除・変更・リネーム

記事の目的

SQL の ALTER TABLE 文を使って、既存テーブルの構造(列・データ型・制約・テーブル名)を変更できるようになります。

列の追加( ADD )、削除( DROP )、型変更、列名・テーブル名のリネーム、制約の追加・削除までを、PostgreSQL・MySQL・SQL Server の製品差に注意しながら整理します。ALTER TABLE は製品差がとくに大きい構文のため、製品ごとの書き方を比較表でまとめます。

重要な注意(本記事の操作はすべて DDL です)

ALTER TABLE はテーブルの定義そのものを変える操作です。実行すると元に戻せない変更が含まれ、本番環境では他のクエリのロック待ち・データ消失・想定外のダウンタイムを引き起こすことがあります。

本記事のコードを本番環境で動かす前に、必ずバックアップを取得し、テスト環境で同じ操作を試してから実行してください。とくに DROP COLUMN ・列の型変更・ NOT NULL 制約の追加は、データ消失や長時間ロックのリスクが高い操作です。

記事のサンプルテーブルの前提

本記事では、説明用に次のような users テーブルがある前提で進めます。

  • id :自動採番(主キー)
  • name :氏名(文字列)
  • email :メールアドレス(文字列)

実際の 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 での書き方は後述の比較表で示します。

  • 主要な RDBMS(PostgreSQL / MySQL / SQL Server)で共通の書き方
-- 列を追加(age 列を整数型で追加)
ALTER TABLE users ADD COLUMN age INTEGER;

-- 列を削除
ALTER TABLE users DROP COLUMN age;
  • 列の型変更とリネームは製品差が大きいので、まずは PostgreSQL での書き方を示します。
-- PostgreSQL: age 列の型を BIGINT に変える
ALTER TABLE users ALTER COLUMN age TYPE BIGINT;

-- PostgreSQL: name 列を full_name にリネーム
ALTER TABLE users RENAME COLUMN name TO full_name;

ここから先は、操作の種類ごとに構文と製品差を整理します。

列を追加する(ADD COLUMN)

新しい列をテーブルに追加します。最も使う頻度が高い ALTER TABLE です。

ALTER TABLE テーブル名 ADD COLUMN 列名 データ型 [制約];
  • 列名・データ型は必須。
  • 既定値( DEFAULT )・ NOT NULL などの制約をその場で付けることもできます。
  • 既存行があるテーブルに NOT NULL 列を追加する場合は、既存行に入れる値が必要になります。小さなテーブルでは DEFAULT 付きで追加できますが、大きなテーブルでは「NULL 許容で列を追加 → UPDATE で値を埋める → NOT NULL 制約を追加」のように段階的に変更したほうが、ロック影響と失敗時のリカバリの面で安全な場合があります。

製品差(COLUMN キーワードの扱い)

製品構文
PostgreSQLALTER TABLE users ADD COLUMN age INTEGER;
MySQLALTER TABLE users ADD COLUMN age INT;COLUMN は省略可)
SQL ServerALTER 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 式の内容(定数か関数か)によって異なります。大きなテーブルで実行する前に、対象環境の挙動をテスト環境で確認してください。

列を削除する(DROP COLUMN)

列の削除はデータの完全削除です。一度削除した列は元に戻せません。

ALTER TABLE users DROP COLUMN memo;

製品差

製品構文
PostgreSQLALTER TABLE users DROP COLUMN memo [CASCADE];
MySQLALTER TABLE users DROP COLUMN memo;COLUMN は省略可)
SQL ServerALTER TABLE users DROP COLUMN memo;

削除できないケースとよくあるエラー

  • 列に外部キー制約が貼られていると、先に制約を DROP する必要があります。PostgreSQL は CASCADE を付けると連鎖的に削除しますが、影響範囲が広がるので慎重に。
  • 列にインデックスがある場合、製品によってはインデックスごと削除されます。
  • 列がビューから参照されていると、製品によっては失敗します。

「本当に消してよいか」を確認してから実行する習慣をおすすめします。

列の型を変更する(製品差が大きい)

列の型を変えたい場合の書き方は、3製品で異なります。

製品構文
PostgreSQLALTER TABLE users ALTER COLUMN age TYPE BIGINT [USING 変換式];
MySQLALTER TABLE users MODIFY COLUMN age BIGINT;
SQL ServerALTER 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;

型変更時の注意点

  • 値が新しい型の範囲に収まらない場合、エラーまたは切り詰めが発生します。たとえば BIGINTINT への変更は、大きな値が入っていれば失敗します。
  • PostgreSQL は USING 句で「どう変換するか」を明示できます(暗黙変換できない場合は必須)。
  • 大きなテーブルへの型変更は、内部でテーブル書き換えが起きてロック時間が長くなることがあります。MySQL では ALGORITHM=INPLACE 、PostgreSQL では特定の型同士の変換が高速化される条件など、各製品のドキュメントで挙動を確認してください。

列をリネームする(製品差が大きい)

列名を変える操作は、3製品で書き方がまったく違います。

製品構文
PostgreSQLALTER TABLE users RENAME COLUMN name TO full_name;
MySQL(8.0 以降)ALTER TABLE users RENAME COLUMN name TO full_name;
SQL ServerEXEC 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 制約は個別の注意点があります。本記事では代表的な構文の入口だけを示し、詳しい挙動や運用上のコツは各制約の個別記事で扱います。

NOT NULL 制約

製品追加削除
PostgreSQLALTER COLUMN age SET NOT NULLALTER COLUMN age DROP NOT NULL
MySQLMODIFY COLUMN age INT NOT NULL (型ごと再指定)MODIFY COLUMN age INT NULL
SQL ServerALTER COLUMN age INT NOT NULLALTER COLUMN age INT NULL

UNIQUE 制約・外部キー制約・チェック制約

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; -- 外部キー

DEFAULT 値の変更

製品既定値を設定既定値を削除
PostgreSQLALTER COLUMN age SET DEFAULT 0ALTER COLUMN age DROP DEFAULT
MySQLALTER COLUMN age SET DEFAULT 0ALTER COLUMN age DROP DEFAULT
SQL Server既存の DEFAULT 制約を DROP CONSTRAINT してから ADD CONSTRAINT … DEFAULT … を追加同上

SQL Server は DEFAULT も「制約」として管理されているため、変更時は古い制約を削除して新しく追加し直すのが定石です。DEFAULT 制約に名前を明示せずに作成した場合は自動生成名が付くため、削除前に sys.default_constraints などのシステムカタログ、または SSMS で実際の制約名を確認してから DROP CONSTRAINT を実行します。

テーブルをリネームする

製品構文
PostgreSQLALTER TABLE users RENAME TO app_users;
MySQLALTER TABLE users RENAME TO app_users; または RENAME TABLE users TO app_users;
SQL ServerEXEC 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 は、テーブル全体に長時間のロックを取ることがあります。挙動は製品とバージョンで大きく異なります。

  • MySQL 5.6 以降: ALGORITHM=INPLACELOCK=NONE をヒントとして指定できる操作があります。
  • PostgreSQL:多くの ALTER は軽い操作(メタデータ変更のみ)ですが、一部の型変更や NOT NULL の追加などはテーブル書き換えが発生し、長時間ロックの原因になります。
  • SQL Server:オンラインでの再構築( WITH (ONLINE = ON) )など、Enterprise エディションでのオプションがあります。

本番運用では、メンテナンスウィンドウ・オンラインでの段階的変更・ pt-online-schema-change (MySQL)/ pg-osc などのツール利用を検討してください。

NOT NULL を後から追加するときの既存データ

既存行に 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;

トランザクション内での実行可否

  • PostgreSQL:ほとんどの ALTER TABLE がトランザクション可能で、 ROLLBACK できます。複数のスキーマ変更を 1 つのトランザクションでまとめると安全です。
  • MySQL:DDL 文は基本的に暗黙コミットが走り、 ROLLBACK できません。失敗しても元に戻らないため、テスト環境での事前確認が必須です。
  • SQL Server:トランザクション可能ですが、長時間ロックの問題は別途あります。

MySQL の MODIFY と CHANGE の違い

MySQL には型変更系の構文が 2 つあります。混同しないように区別します。

  • MODIFY COLUMN :列の定義(型・制約)を変える。列名は変えない。
  • CHANGE COLUMN :列の名前と定義を同時に変える。リネームしないときも、必ず新しい列名(古い名前と同じでも可)を指定する必要があります。

まとめ

ALTER TABLE はテーブル構造を変える DDL ですが、 列追加( ADD )・列削除( DROP )・型変更・リネーム・制約の追加/削除の5パターンを軸に押さえ、3製品の構文差を比較表で確認すれば、現場の構造変更はほぼ書けるようになります。本番環境では、バックアップ・テスト環境での事前確認・ロック影響の見積もりを必ず行ってください。

目次