Oracleでテーブルの構造を変更するには ALTER TABLE 文を使います。列の追加・削除・型変更から制約の管理、表領域の変更まで幅広い操作をカバーします。
ただし、Oracle の ALTER TABLE には他の DB にはない挙動があります。たとえば大テーブルへの列追加が瞬時に完了する条件や、本番稼働中に列を削除する際は SET UNUSED を使うべき理由、テーブルを MOVE した後はインデックスが壊れるといった点は、知らないと本番で障害を起こすリスクがあります。
この記事では、列・制約・テーブル名のすべての変更操作を実例とともに解説し、実務で必ず押さえておくべき注意点まで体系的にまとめます。
・列の追加(ADD)・データ型変更(MODIFY)・列名変更(RENAME COLUMN)・削除(DROP COLUMN)の全構文
・SET UNUSED で列を即座に論理削除する方法(大テーブル向け)
・Oracle 12c 以降の INVISIBLE 列の活用
・11g 以降で NOT NULL + DEFAULT 付き列追加が瞬時に完了する理由
・制約の追加・削除・ENABLE / DISABLE の使い方
・MOVE による表領域変更と、その後に必要なインデックス再作成
・RENAME でのテーブル名・列名変更
・ALTER TABLE に必要な権限
- ALTER TABLE でできる操作一覧
- 列の追加(ADD)
- 列のデータ型・サイズ・デフォルト値の変更(MODIFY)
- 列名の変更(RENAME COLUMN)
- 列の削除(DROP COLUMN)
- SET UNUSED:大テーブルの列を即座に論理削除する
- 列の非表示化(INVISIBLE / VISIBLE):Oracle 12c 以降
- 制約の追加(ADD CONSTRAINT)
- 制約の削除・有効化・無効化
- テーブル名・列名の変更(RENAME)
- MOVE:テーブルを別の表領域に移動する
- ALTER TABLE に必要な権限
- 本番稼働中の ALTER TABLE:注意すべきポイント
- よく使うパターン集
- よくある質問
- まとめ
ALTER TABLE でできる操作一覧
| 操作 | SQL | 備考 |
|---|---|---|
| 列を追加する | ALTER TABLE … ADD | 既存行には NULL または DEFAULT 値が入る |
| 列のデータ型・サイズを変更する | ALTER TABLE … MODIFY | 既存データがある場合は制約あり |
| 列名を変更する | ALTER TABLE … RENAME COLUMN | Oracle 9i 以降 |
| 列を削除する | ALTER TABLE … DROP COLUMN | 大テーブルは SET UNUSED を推奨 |
| 列を論理削除する(即時) | ALTER TABLE … SET UNUSED | 実体はそのまま、参照不可にする |
| 列を非表示にする | ALTER TABLE … MODIFY … INVISIBLE | 12c 以降。SELECT * には出ない |
| 制約を追加する | ALTER TABLE … ADD CONSTRAINT | PRIMARY KEY / FK / UNIQUE / CHECK |
| 制約を削除する | ALTER TABLE … DROP CONSTRAINT | 制約名が必要 |
| 制約を無効化する | ALTER TABLE … DISABLE CONSTRAINT | データ修正時などに一時的に無効化 |
| 制約を有効化する | ALTER TABLE … ENABLE CONSTRAINT | 有効化時に既存データを検証 |
| テーブル名を変更する | ALTER TABLE … RENAME TO | |
| テーブルを別表領域に移動する | ALTER TABLE … MOVE TABLESPACE | 移動後はインデックス再作成が必要 |
列の追加(ADD)
ALTER TABLE emp
ADD phone VARCHAR2(20);
ALTER TABLE emp
ADD (
phone VARCHAR2(20),
address VARCHAR2(200),
updated_at DATE DEFAULT SYSDATE NOT NULL
);
Oracle 11g より前は、
NOT NULL DEFAULT 値 の列を大テーブルに追加すると、全行を更新するため非常に時間がかかりました。11g 以降では、この操作はメタデータの変更のみになり、億レコードのテーブルでも瞬時に完了します。既存行への値の書き込みは、その行が次回 UPDATE されたときに初めて行われます。
(ただし Oracle 11.2.0.4 以前には一部バグがあるため、パッチバージョンを確認してください)
テーブルに既存行があるとき、
NOT NULL だけを指定した列(DEFAULT なし)を追加しようとすると ORA-01758: 必須(NOT NULL)列を追加するには、テーブルが空である必要があります エラーになります。対処法:① DEFAULT 値を指定する ② NULL 許容で追加してから UPDATE し NOT NULL を後から付ける
列のデータ型・サイズ・デフォルト値の変更(MODIFY)
-- VARCHAR2(50) → VARCHAR2(200) に拡張
ALTER TABLE emp
MODIFY emp_name VARCHAR2(200);
ALTER TABLE emp
MODIFY salary NUMBER(12, 2);
ALTER TABLE emp
MODIFY status DEFAULT 'INACTIVE';
-- NOT NULL を付ける(既存行に NULL がないことが前提)
ALTER TABLE emp
MODIFY phone NOT NULL;
-- NOT NULL を外す(NULL 許容に戻す)
ALTER TABLE emp
MODIFY phone NULL;
ALTER TABLE emp
MODIFY (
emp_name VARCHAR2(200) NOT NULL,
salary NUMBER(12, 2)
);
・データ型の縮小(VARCHAR2(200)→VARCHAR2(10) など)は、既存データが新しいサイズに収まる場合のみ可能
・数値型から文字型など、互換性のない型変更は基本的に不可(列を DROP して再作成する必要がある)
・DATE → TIMESTAMP への変更は可能だが、TIMESTAMP → DATE への縮小は既存データに精度情報があると失敗することがある
・外部キー制約で参照されている列のデータ型変更はできない
列名の変更(RENAME COLUMN)
ALTER TABLE emp
RENAME COLUMN old_column TO new_column;
ALTER TABLE emp
RENAME COLUMN salary TO monthly_salary;
Oracle は列名変更に追随してビューや PL/SQL の依存オブジェクトを自動更新しません。列名を参照しているビュー・トリガー・ストアドプロシージャが存在する場合、変更後にそれらのオブジェクトが無効(INVALID)になります。
USER_DEPENDENCIES で依存オブジェクトを事前に確認し、変更後は ALTER … COMPILE で再コンパイルしてください。列の削除(DROP COLUMN)
ALTER TABLE emp
DROP COLUMN phone;
ALTER TABLE emp
DROP (phone, address, memo);
DROP COLUMN は全行からそのカラムのデータを実際に削除するため、大テーブルでは長時間かかります。本番稼働中に実行するとテーブルがロックされ業務が停止します。解決策:まず
SET UNUSED で論理削除し、メンテナンス時間帯に実際の削除を実行する。SET UNUSED:大テーブルの列を即座に論理削除する
SET UNUSED は列を即座に「参照不可」にする操作です。ディクショナリへのメタデータ変更のみなので大テーブルでも瞬時に完了します。その後、メンテナンス時間帯に DROP UNUSED COLUMNS で実際の削除を行います。
-- 【ステップ1】業務時間中:列を即座に論理削除(参照不可にする) ALTER TABLE emp SET UNUSED COLUMN phone; -- 複数列を一括で SET UNUSED にする ALTER TABLE emp SET UNUSED (phone, address); -- 【ステップ2】メンテナンス時間帯:実際に削除(この操作は時間がかかる) ALTER TABLE emp DROP UNUSED COLUMNS;
SELECT table_name, count(*) AS unused_col_count FROM USER_UNUSED_COL_TABS ORDER BY table_name;
・業務時間中・大テーブル:
SET UNUSED → 即時完了、後でまとめて DROP UNUSED COLUMNS・メンテナンス時間帯・小テーブル:
DROP COLUMN で直接削除列の非表示化(INVISIBLE / VISIBLE):Oracle 12c 以降
Oracle 12c 以降では、列を INVISIBLE(非表示)に設定できます。SELECT * や INSERT INTO テーブル名 VALUES (...) の対象外になりますが、列名を明示すれば参照・更新できます。
-- 列を非表示に設定 ALTER TABLE emp MODIFY internal_code INVISIBLE; -- 非表示を解除する ALTER TABLE emp MODIFY internal_code VISIBLE;
SELECT column_name, hidden_column FROM USER_TAB_COLS WHERE table_name = 'EMP' ORDER BY internal_column_id;
・既存アプリに影響を与えずに新列を追加(
SELECT * に出ない)・将来的に廃止予定の列を段階的に非表示にする
・セキュリティ上の理由で通常の SELECT に出したくない列
制約の追加(ADD CONSTRAINT)
ALTER TABLE emp
ADD CONSTRAINT pk_emp PRIMARY KEY (emp_id);
ALTER TABLE emp
ADD CONSTRAINT fk_emp_dept
FOREIGN KEY (dept_id) REFERENCES dept (dept_id);
ALTER TABLE emp
ADD CONSTRAINT uq_emp_email UNIQUE (email);
ALTER TABLE emp
ADD CONSTRAINT ck_emp_salary CHECK (salary >= 0);
-- NOT NULL は ADD CONSTRAINT ではなく MODIFY で追加する
ALTER TABLE emp
MODIFY emp_name NOT NULL;
NOT NULL 制約は他の制約と異なり、ADD CONSTRAINT ではなく MODIFY 列名 NOT NULL で追加します。(Oracle の内部では CHECK (列名 IS NOT NULL) として実装されています)制約の削除・有効化・無効化
ALTER TABLE emp
DROP CONSTRAINT fk_emp_dept;
-- PRIMARY KEY を削除(依存する FK ごと削除)
ALTER TABLE dept
DROP CONSTRAINT pk_dept CASCADE;
-- データ修正や一括 INSERT 前に無効化 ALTER TABLE emp DISABLE CONSTRAINT fk_emp_dept; -- 作業後に有効化 ALTER TABLE emp ENABLE CONSTRAINT fk_emp_dept;
-- 既存データを検証せず有効化(新規 DML のみ制約が効く) ALTER TABLE emp ENABLE NOVALIDATE CONSTRAINT fk_emp_dept;
・DISABLE CONSTRAINT:大量データの一括 INSERT や移行時に FK/CHECK を一時的に外す。終了後は必ず ENABLE すること
・ENABLE NOVALIDATE:既存の不整合データがある状態で制約を有効化したい場合に使う(新規 DML のみ制約チェック)
SELECT constraint_name, constraint_type, status, validated FROM USER_CONSTRAINTS WHERE table_name = 'EMP' ORDER BY constraint_type, constraint_name;
テーブル名・列名の変更(RENAME)
-- 方法1:ALTER TABLE … RENAME TO ALTER TABLE old_table_name RENAME TO new_table_name; -- 方法2:RENAME 文(Oracle 独自の短縮形) RENAME old_table_name TO new_table_name;
ALTER TABLE emp
RENAME COLUMN salary TO monthly_salary;
テーブル名・列名を変更すると、そのオブジェクトを参照しているビュー・トリガー・プロシージャが INVALID になります。変更後は以下で INVALID オブジェクトを確認し、再コンパイルしてください。
SELECT object_name, object_type FROM USER_OBJECTS WHERE STATUS = 'INVALID';MOVE:テーブルを別の表領域に移動する
テーブルを別の表領域に移動するには ALTER TABLE … MOVE を使います。断片化の解消や I/O 分散のために使うことがあります。
-- テーブルを data2 表領域に移動 ALTER TABLE emp MOVE TABLESPACE data2;
ALTER TABLE … MOVE を実行すると、テーブルの行の物理アドレス(ROWID)が変わります。インデックスは古い ROWID を参照したままになるため、すべてのインデックスが UNUSABLE 状態になります。MOVE 後は以下を実行してインデックスを再構築しないと、インデックスを使うクエリがエラーになります。
インデックスの再構築については【Oracle】インデックスの作成、再構築、削除を参照してください。
-- UNUSABLE 状態のインデックスを確認 SELECT index_name, status FROM USER_INDEXES WHERE table_name = 'EMP' AND status = 'UNUSABLE'; -- インデックスを再構築 ALTER INDEX pk_emp REBUILD; ALTER INDEX idx_emp_dept REBUILD;
ALTER TABLE に必要な権限
| 対象 | 必要な権限 |
|---|---|
| 自スキーマのテーブルを変更する | なし(テーブルのオーナーなら誰でも実行可能) |
| 他スキーマのテーブルを変更する | ALTER ANY TABLE システム権限 |
| MOVE で別表領域に移動する | 移動先表領域の QUOTA または UNLIMITED TABLESPACE |
SELECT privilege FROM SESSION_PRIVS WHERE PRIVILEGE LIKE '%TABLE%';
本番稼働中の ALTER TABLE:注意すべきポイント
| 操作 | 本番での注意点 |
|---|---|
| 列追加(NOT NULL + DEFAULT) | 11g 以降はメタデータ変更のみで瞬時完了。ロック時間は最小限 |
| 列追加(NULL 許容) | 瞬時完了。ほぼリスクなし |
| MODIFY(型変更・サイズ拡張) | 短時間のロックが発生。大テーブルはメンテ時間帯を推奨 |
| DROP COLUMN | 全行を処理するため大テーブルでは長時間ロック。SET UNUSED → DROP UNUSED COLUMNS を推奨 |
| RENAME TABLE / COLUMN | 瞬時完了。ただし依存オブジェクトが INVALID になる |
| ADD CONSTRAINT(PRIMARY KEY / FK / CHECK) | 全行の検証が走るため大テーブルは時間がかかる。不整合データがある場合は ENABLE NOVALIDATE で既存データをスキップし、後で不整合を修正する手順が安全 |
| DISABLE CONSTRAINT | 瞬時完了。ただし終了後の ENABLE を忘れずに |
| MOVE TABLESPACE | 全行コピーが走る。大テーブルは長時間。MOVE 後はインデックス再構築が必要 |
よく使うパターン集
ALTER TABLE emp
ADD (
created_by VARCHAR2(30) DEFAULT USER NOT NULL,
created_at DATE DEFAULT SYSDATE NOT NULL,
updated_at DATE DEFAULT SYSDATE NOT NULL
);
-- 移行データに不整合があっても新規DMLには制約を効かせたい場合
ALTER TABLE emp
ADD CONSTRAINT fk_emp_dept
FOREIGN KEY (dept_id) REFERENCES dept (dept_id)
ENABLE NOVALIDATE;
-- NUMBER → VARCHAR2 のように互換性のない型変更は直接 MODIFY できない -- 作業列追加 → データコピー → 元列を論理削除 → 列名変更 の手順で対応する -- [手順1] 新しい型で作業列を追加 ALTER TABLE emp ADD emp_code_new VARCHAR2(20); -- [手順2] 既存データを新列にコピー UPDATE emp SET emp_code_new = TO_CHAR(emp_code); COMMIT; -- [手順3] 元の列を論理削除(SET UNUSED:即時完了) ALTER TABLE emp SET UNUSED COLUMN emp_code; -- [手順4] 新列を元の名前に変更 ALTER TABLE emp RENAME COLUMN emp_code_new TO emp_code; -- [手順5] NOT NULL が必要なら付ける ALTER TABLE emp MODIFY emp_code NOT NULL; -- [手順6] メンテ時間帯にまとめて実際の削除を行う ALTER TABLE emp DROP UNUSED COLUMNS;
よくある質問
ORA-01758: 必須(NOT NULL)列を追加するには、テーブルが空である必要があります は、既存行があるテーブルに DEFAULT なしで NOT NULL 列を追加しようとしたときのエラーです。対処法は①
DEFAULT 値 を指定する(Oracle 11g 以降は瞬時完了)、②NULL 許容で列を追加してから UPDATE で値を入れた後 MODIFY … NOT NULL を実行する、の2つです。MODIFY … NOT NULL を実行したときのエラーです。現状を確認して、変更が不要であれば何もしなくて問題ありません。MODIFY … NULL で NOT NULL を解除してから再度 MODIFY … NOT NULL を実行する必要はありません。DROP COLUMN は全行のデータを物理的に削除するため、大テーブルでは時間がかかります。実行中の操作を中断したい場合は ALTER TABLE テーブル名 DROP COLUMN 列名 CHECKPOINT 1000; のようにCHECKPOINT を指定することで、途中で中断・再開が可能になります。本番では
SET UNUSED で即時論理削除してから、メンテ時間帯に DROP UNUSED COLUMNS を実行することを推奨します。ALTER TABLE … MOVE の後はすべてのインデックスが UNUSABLE 状態になります。ALTER INDEX インデックス名 REBUILD; で再構築してください。SELECT index_name FROM USER_INDEXES WHERE table_name = 'テーブル名' AND status = 'UNUSABLE'; で UNUSABLE なインデックスを確認できます。ORA-02298: 親キーが見つかりません は、子テーブルのデータに親テーブルに存在しない値があるときのエラーです。対処法は①不整合データを修正してから
ADD CONSTRAINT する、②ENABLE NOVALIDATE で既存データをスキップして制約を有効化する(新規 DML のみ制約チェック)、の2つです。ALTER TABLE で直接変更することはできません。列の順番を変えたい場合は、① CTAS(CREATE TABLE AS SELECT で列を好きな順番で指定して新テーブル作成)してリネームする、② INVISIBLE を活用して表示上の順番を制御する(12c以降)などの方法があります。まとめ
Oracle の ALTER TABLE で覚えておくべき重要ポイントをまとめます。
| やりたいこと | 推奨する方法 |
|---|---|
| 列を追加する(大テーブル・本番可) | ADD … DEFAULT … NOT NULL(11g以降は瞬時完了) |
| 列を削除する(大テーブル・本番時) | SET UNUSED で即時論理削除 → メンテ時に DROP UNUSED COLUMNS |
| 列の型を変更する(互換性なし) | 作業列追加 → データコピー → SET UNUSED → RENAME の4ステップ |
| 制約を一時的に外す | DISABLE CONSTRAINT → 作業 → ENABLE CONSTRAINT |
| 不整合データがある状態で制約を有効化 | ENABLE NOVALIDATE CONSTRAINT |
| 表領域を変更する | MOVE TABLESPACE → 必ずインデックス REBUILD |
テーブルの作成方法については【Oracle】テーブルを作成する方法完全ガイドを、インデックスの管理については【Oracle】インデックスの作成、再構築、削除を参照してください。

