【Oracle】ALTER TABLE完全ガイド|列追加・変更・削除・制約・MOVE・SET UNUSED・本番での注意点まで解説

【Oracle】ALTER TABLE完全ガイド|列追加・変更・削除・制約・MOVE・SET UNUSED・本番での注意点まで解説 Oracle

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 でできる操作一覧

操作 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)

SQL(1列追加)
ALTER TABLE emp
    ADD phone VARCHAR2(20);
SQL(複数列を同時に追加)
ALTER TABLE emp
    ADD (
        phone      VARCHAR2(20),
        address    VARCHAR2(200),
        updated_at DATE DEFAULT SYSDATE NOT NULL
    );
Oracle 11g 以降:NOT NULL + DEFAULT 付き列追加は瞬時に完了する
Oracle 11g より前は、NOT NULL DEFAULT 値 の列を大テーブルに追加すると、全行を更新するため非常に時間がかかりました。
11g 以降では、この操作はメタデータの変更のみになり、億レコードのテーブルでも瞬時に完了します。既存行への値の書き込みは、その行が次回 UPDATE されたときに初めて行われます。
(ただし Oracle 11.2.0.4 以前には一部バグがあるため、パッチバージョンを確認してください)
DEFAULT なしの NOT NULL 列は既存データがあると追加できない
テーブルに既存行があるとき、NOT NULL だけを指定した列(DEFAULT なし)を追加しようとすると ORA-01758: 必須(NOT NULL)列を追加するには、テーブルが空である必要があります エラーになります。
対処法:① DEFAULT 値を指定する ② NULL 許容で追加してから UPDATE し NOT NULL を後から付ける

列のデータ型・サイズ・デフォルト値の変更(MODIFY)

SQL(データ型のサイズを拡張)
-- VARCHAR2(50) → VARCHAR2(200) に拡張
ALTER TABLE emp
    MODIFY emp_name VARCHAR2(200);
SQL(NUMBER の精度を変更)
ALTER TABLE emp
    MODIFY salary NUMBER(12, 2);
SQL(DEFAULT 値を変更)
ALTER TABLE emp
    MODIFY status DEFAULT 'INACTIVE';
SQL(NOT NULL 制約を追加・削除)
-- NOT NULL を付ける(既存行に NULL がないことが前提)
ALTER TABLE emp
    MODIFY phone NOT NULL;

-- NOT NULL を外す(NULL 許容に戻す)
ALTER TABLE emp
    MODIFY phone NULL;
SQL(複数列を同時に変更)
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)

SQL(列名を変更)
ALTER TABLE emp
    RENAME COLUMN old_column TO new_column;
SQL(実例:salary → monthly_salary)
ALTER TABLE emp
    RENAME COLUMN salary TO monthly_salary;
列名変更後はビュー・トリガー・プロシージャの確認が必要
Oracle は列名変更に追随してビューや PL/SQL の依存オブジェクトを自動更新しません。列名を参照しているビュー・トリガー・ストアドプロシージャが存在する場合、変更後にそれらのオブジェクトが無効(INVALID)になります。
USER_DEPENDENCIES で依存オブジェクトを事前に確認し、変更後は ALTER … COMPILE で再コンパイルしてください。

列の削除(DROP COLUMN)

SQL(列を削除)
ALTER TABLE emp
    DROP COLUMN phone;
SQL(複数列を同時に削除)
ALTER TABLE emp
    DROP (phone, address, memo);
大テーブルの列削除は時間がかかる
DROP COLUMN は全行からそのカラムのデータを実際に削除するため、大テーブルでは長時間かかります。本番稼働中に実行するとテーブルがロックされ業務が停止します。
解決策:まず SET UNUSED で論理削除し、メンテナンス時間帯に実際の削除を実行する。

SET UNUSED:大テーブルの列を即座に論理削除する

SET UNUSED は列を即座に「参照不可」にする操作です。ディクショナリへのメタデータ変更のみなので大テーブルでも瞬時に完了します。その後、メンテナンス時間帯に DROP UNUSED COLUMNS で実際の削除を行います。

SQL(SET UNUSED の手順)
-- 【ステップ1】業務時間中:列を即座に論理削除(参照不可にする)
ALTER TABLE emp SET UNUSED COLUMN phone;

-- 複数列を一括で SET UNUSED にする
ALTER TABLE emp SET UNUSED (phone, address);

-- 【ステップ2】メンテナンス時間帯:実際に削除(この操作は時間がかかる)
ALTER TABLE emp DROP UNUSED COLUMNS;
SQL(SET UNUSED 状態の列を確認)
SELECT table_name, count(*) AS unused_col_count
FROM USER_UNUSED_COL_TABS
ORDER BY table_name;
SET UNUSED と DROP COLUMN の使い分け
業務時間中・大テーブルSET UNUSED → 即時完了、後でまとめて DROP UNUSED COLUMNS
メンテナンス時間帯・小テーブルDROP COLUMN で直接削除

列の非表示化(INVISIBLE / VISIBLE):Oracle 12c 以降

Oracle 12c 以降では、列を INVISIBLE(非表示)に設定できます。SELECT *INSERT INTO テーブル名 VALUES (...) の対象外になりますが、列名を明示すれば参照・更新できます。

SQL(列を非表示にする)
-- 列を非表示に設定
ALTER TABLE emp MODIFY internal_code INVISIBLE;

-- 非表示を解除する
ALTER TABLE emp MODIFY internal_code VISIBLE;
SQL(非表示列の確認)
SELECT column_name, hidden_column
FROM USER_TAB_COLS
WHERE table_name = 'EMP'
ORDER BY internal_column_id;
INVISIBLE 列の典型的な使い方
・既存アプリに影響を与えずに新列を追加(SELECT * に出ない)
・将来的に廃止予定の列を段階的に非表示にする
・セキュリティ上の理由で通常の SELECT に出したくない列

制約の追加(ADD CONSTRAINT)

SQL(PRIMARY KEY 制約を追加)
ALTER TABLE emp
    ADD CONSTRAINT pk_emp PRIMARY KEY (emp_id);
SQL(FOREIGN KEY 制約を追加)
ALTER TABLE emp
    ADD CONSTRAINT fk_emp_dept
        FOREIGN KEY (dept_id) REFERENCES dept (dept_id);
SQL(UNIQUE 制約を追加)
ALTER TABLE emp
    ADD CONSTRAINT uq_emp_email UNIQUE (email);
SQL(CHECK 制約を追加)
ALTER TABLE emp
    ADD CONSTRAINT ck_emp_salary CHECK (salary >= 0);
SQL(NOT NULL を後から追加)
-- NOT NULL は ADD CONSTRAINT ではなく MODIFY で追加する
ALTER TABLE emp
    MODIFY emp_name NOT NULL;
NOT NULL は MODIFY で追加する
NOT NULL 制約は他の制約と異なり、ADD CONSTRAINT ではなく MODIFY 列名 NOT NULL で追加します。(Oracle の内部では CHECK (列名 IS NOT NULL) として実装されています)

制約の削除・有効化・無効化

SQL(制約を削除)
ALTER TABLE emp
    DROP CONSTRAINT fk_emp_dept;

-- PRIMARY KEY を削除(依存する FK ごと削除)
ALTER TABLE dept
    DROP CONSTRAINT pk_dept CASCADE;
SQL(制約を一時的に無効化する)
-- データ修正や一括 INSERT 前に無効化
ALTER TABLE emp DISABLE CONSTRAINT fk_emp_dept;

-- 作業後に有効化
ALTER TABLE emp ENABLE CONSTRAINT fk_emp_dept;
SQL(制約を無効化して既存データを検証しない NOVALIDATE)
-- 既存データを検証せず有効化(新規 DML のみ制約が効く)
ALTER TABLE emp ENABLE NOVALIDATE CONSTRAINT fk_emp_dept;
制約の有効化・無効化の使い分け
DISABLE CONSTRAINT:大量データの一括 INSERT や移行時に FK/CHECK を一時的に外す。終了後は必ず ENABLE すること
ENABLE NOVALIDATE:既存の不整合データがある状態で制約を有効化したい場合に使う(新規 DML のみ制約チェック)
SQL(テーブルの全制約を確認)
SELECT constraint_name, constraint_type, status, validated
FROM USER_CONSTRAINTS
WHERE table_name = 'EMP'
ORDER BY constraint_type, constraint_name;

テーブル名・列名の変更(RENAME)

SQL(テーブル名を変更)
-- 方法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;
SQL(列名を変更)
ALTER TABLE emp
    RENAME COLUMN salary TO monthly_salary;
RENAME 後は依存オブジェクトの再コンパイルが必要
テーブル名・列名を変更すると、そのオブジェクトを参照しているビュー・トリガー・プロシージャが INVALID になります。変更後は以下で INVALID オブジェクトを確認し、再コンパイルしてください。
SELECT object_name, object_type FROM USER_OBJECTS WHERE STATUS = 'INVALID';

MOVE:テーブルを別の表領域に移動する

テーブルを別の表領域に移動するには ALTER TABLE … MOVE を使います。断片化の解消や I/O 分散のために使うことがあります。

SQL(テーブルを別の表領域に移動)
-- テーブルを data2 表領域に移動
ALTER TABLE emp MOVE TABLESPACE data2;
MOVE 後は必ずインデックスを再作成・再構築すること
ALTER TABLE … MOVE を実行すると、テーブルの行の物理アドレス(ROWID)が変わります。インデックスは古い ROWID を参照したままになるため、すべてのインデックスが UNUSABLE 状態になります。
MOVE 後は以下を実行してインデックスを再構築しないと、インデックスを使うクエリがエラーになります。

インデックスの再構築については【Oracle】インデックスの作成、再構築、削除を参照してください。

SQL(MOVE 後:UNUSABLE インデックスを確認して再構築)
-- 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
SQL(権限確認)
SELECT privilege FROM SESSION_PRIVS WHERE PRIVILEGE LIKE '%TABLE%';
権限の確認・付与方法の詳細は【Oracle】ユーザ権限を確認する方法完全ガイドを参照してください。

本番稼働中の 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 後はインデックス再構築が必要

よく使うパターン集

パターン1:既存テーブルに管理用の列を3つ追加
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
    );
パターン2:データ移行後に制約を追加(NOVALIDATE で既存データはスキップ)
-- 移行データに不整合があっても新規DMLには制約を効かせたい場合
ALTER TABLE emp
    ADD CONSTRAINT fk_emp_dept
        FOREIGN KEY (dept_id) REFERENCES dept (dept_id)
        ENABLE NOVALIDATE;
パターン3:列の型変更(互換性がない場合)
-- 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;

よくある質問

QALTER TABLE で列を追加したら ORA-01758 エラーになりました
AORA-01758: 必須(NOT NULL)列を追加するには、テーブルが空である必要があります は、既存行があるテーブルに DEFAULT なしで NOT NULL 列を追加しようとしたときのエラーです。
対処法は①DEFAULT 値 を指定する(Oracle 11g 以降は瞬時完了)、②NULL 許容で列を追加してから UPDATE で値を入れた後 MODIFY … NOT NULL を実行する、の2つです。
QORA-01442: 列はすでに NOT NULL として定義されているため変更できません
Aすでに NOT NULL の列に MODIFY … NOT NULL を実行したときのエラーです。現状を確認して、変更が不要であれば何もしなくて問題ありません。MODIFY … NULL で NOT NULL を解除してから再度 MODIFY … NOT NULL を実行する必要はありません。
QDROP COLUMN が終わらない・時間がかかりすぎる
ADROP COLUMN は全行のデータを物理的に削除するため、大テーブルでは時間がかかります。実行中の操作を中断したい場合は ALTER TABLE テーブル名 DROP COLUMN 列名 CHECKPOINT 1000; のようにCHECKPOINT を指定することで、途中で中断・再開が可能になります。
本番では SET UNUSED で即時論理削除してから、メンテ時間帯に DROP UNUSED COLUMNS を実行することを推奨します。
QMOVE 後にクエリが ORA-01502(インデックス使用不可)エラーになります
AALTER TABLE … MOVE の後はすべてのインデックスが UNUSABLE 状態になります。ALTER INDEX インデックス名 REBUILD; で再構築してください。SELECT index_name FROM USER_INDEXES WHERE table_name = 'テーブル名' AND status = 'UNUSABLE'; で UNUSABLE なインデックスを確認できます。
Q外部キー制約を付けようとしたら ORA-02298 エラーになります
AORA-02298: 親キーが見つかりません は、子テーブルのデータに親テーブルに存在しない値があるときのエラーです。
対処法は①不整合データを修正してから ADD CONSTRAINT する、②ENABLE NOVALIDATE で既存データをスキップして制約を有効化する(新規 DML のみ制約チェック)、の2つです。
Qテーブルの列の順番を変えられますか?
AOracle では列の順番を 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】インデックスの作成、再構築、削除を参照してください。