Oracle でテーブルをコピーしたい場面は頻繁にあります。テスト用にデータを複製する、本番テーブルのバックアップを取る、マイグレーション先に構造ごとコピーする――目的に応じて最適な方法は異なります。
本記事では、最も基本的な CTAS(CREATE TABLE AS SELECT)から、INSERT INTO … SELECT、構造のみコピー(データなし)、制約・インデックスが自動コピーされない問題とその対処法、DB Link を使った別データベース間コピー、そして NOLOGGING + APPEND + PARALLEL による高速化まで体系的に解説します。
・CTAS(CREATE TABLE AS SELECT)でテーブルごとコピーする方法
・構造のみコピー(データなし)する方法
・特定の行だけコピーする方法(WHERE 条件付き)
・INSERT INTO … SELECT で既存テーブルにデータをコピーする方法
・CTAS でコピーされないもの(制約・インデックス・トリガー等)と対処法
・DB Link 経由で別データベースのテーブルをコピーする方法
・NOLOGGING / APPEND / PARALLEL で大量データを高速コピーする方法
・テスト用コピー・バックアップ・マイグレーションの実務パターン
CTAS(CREATE TABLE AS SELECT)で丸ごとコピー
CREATE TABLE ... AS SELECT ...(CTAS)は、SELECT の結果をもとに新しいテーブルを作成しながらデータもコピーする方法です。1 つの SQL でテーブル構造とデータが同時にコピーされます。
-- employees テーブルを employees_copy としてコピー CREATE TABLE employees_copy AS SELECT * FROM employees; -- コピー後の確認 SELECT COUNT(*) FROM employees_copy;
特定の列だけコピーする
-- 必要な列だけコピー CREATE TABLE emp_summary AS SELECT employee_id, last_name, salary, department_id FROM employees; -- 列に別名を付ければコピー先のカラム名を変更できる CREATE TABLE emp_renamed AS SELECT employee_id AS id, last_name AS name, salary AS pay FROM employees;
条件付きコピー(WHERE 句で絞り込み)
-- 部門 20 のデータのみコピー CREATE TABLE dept20_employees AS SELECT * FROM employees WHERE department_id = 20; -- 2025年以降の注文のみコピー CREATE TABLE orders_2025 AS SELECT * FROM orders WHERE order_date >= DATE '2025-01-01';
構造のみコピー(データなし)
テーブルのカラム定義だけをコピーし、データは入れない方法です。テスト用のテンプレートや一時テーブルの作成に使います。
-- 方法①: WHERE 1=0(常に偽の条件でデータを 0 件にする) CREATE TABLE employees_empty AS SELECT * FROM employees WHERE 1 = 0; -- 方法②: WHERE ROWNUM < 1(同じ効果) CREATE TABLE employees_template AS SELECT * FROM employees WHERE ROWNUM < 1; -- どちらも同じ結果: カラム定義のみコピー、データは 0 件 SELECT COUNT(*) FROM employees_empty; -- 0
WHERE 1=0 と WHERE ROWNUM < 1 はどちらも 0 件を返します。Oracle は実行計画で「行が返らない」ことを認識するため、テーブル全体のスキャンは発生しません(高速)。INSERT INTO … SELECT で既存テーブルにコピー
INSERT INTO ... SELECT は、既に存在するテーブルにデータをコピーする方法です。CTAS と異なり、コピー先のテーブルは事前に作成されている必要があります。
-- 既存テーブルにデータをコピー INSERT INTO employees_backup SELECT * FROM employees; -- 特定の列だけコピー(列名を合わせる) INSERT INTO emp_summary (id, name, salary) SELECT employee_id, last_name, salary FROM employees; -- 条件付きコピー INSERT INTO active_employees SELECT * FROM employees WHERE status = 'ACTIVE'; COMMIT;
| 項目 | CTAS | INSERT INTO … SELECT |
|---|---|---|
| テーブルの事前作成 | 不要(自動作成) | 必要 |
| データコピー | テーブル作成と同時 | 既存テーブルに追加 |
| トランザクション | DDL(自動 COMMIT) | DML(明示 COMMIT が必要) |
| ロールバック | 不可(DDL) | 可能(COMMIT 前なら ROLLBACK できる) |
| 制約のコピー | NOT NULL のみ | コピー先の制約がそのまま適用 |
| 用途 | 新規テーブルの作成 + コピー | 既存テーブルへのデータ追加・マイグレーション |
CTAS は CREATE TABLE 文なので、実行した瞬間に自動的に COMMIT されます。失敗した場合は ROLLBACK ではなく DROP TABLE で戻す必要があります。一方、INSERT INTO … SELECT は DML なので COMMIT 前なら ROLLBACK できます。安全性を重視する場合は INSERT 方式を使ってください。
CTAS でコピーされないもの
CTAS でテーブルをコピーすると、カラム定義と NOT NULL 制約だけがコピーされます。それ以外のオブジェクトは手動で再作成する必要があります。
| オブジェクト | CTAS でコピーされるか | 対処法 |
|---|---|---|
| カラム定義(名前・データ型) | ○ コピーされる | ― |
| NOT NULL 制約 | ○ コピーされる | ― |
| PRIMARY KEY | × コピーされない | ALTER TABLE … ADD CONSTRAINT で追加 |
| UNIQUE 制約 | × コピーされない | ALTER TABLE … ADD CONSTRAINT で追加 |
| FOREIGN KEY | × コピーされない | ALTER TABLE … ADD CONSTRAINT で追加 |
| CHECK 制約 | × コピーされない | ALTER TABLE … ADD CONSTRAINT で追加 |
| DEFAULT 値 | × コピーされない | ALTER TABLE … MODIFY で設定 |
| インデックス | × コピーされない | CREATE INDEX で作成 |
| トリガー | × コピーされない | CREATE TRIGGER で再作成 |
| コメント(COMMENT ON) | × コピーされない | COMMENT ON で再設定 |
| 権限(GRANT) | × コピーされない | GRANT で再付与 |
制約・インデックスを再作成する方法
-- ① CTAS でデータをコピー
CREATE TABLE employees_copy AS SELECT * FROM employees;
-- ② PRIMARY KEY を追加
ALTER TABLE employees_copy ADD CONSTRAINT pk_emp_copy PRIMARY KEY (employee_id);
-- ③ インデックスを追加
CREATE INDEX idx_emp_copy_dept ON employees_copy (department_id);
-- ④ FOREIGN KEY を追加
ALTER TABLE employees_copy ADD CONSTRAINT fk_emp_copy_dept
FOREIGN KEY (department_id) REFERENCES departments (dept_id);
DBMS_METADATA で元テーブルの DDL を取得する
-- 元テーブルの制約 DDL を取得
SELECT DBMS_METADATA.GET_DDL('CONSTRAINT', constraint_name, owner) AS ddl
FROM all_constraints
WHERE table_name = 'EMPLOYEES'
AND owner = 'HR'
AND constraint_type IN ('P', 'U', 'R', 'C');
-- 元テーブルのインデックス DDL を取得
SELECT DBMS_METADATA.GET_DDL('INDEX', index_name, owner) AS ddl
FROM all_indexes
WHERE table_name = 'EMPLOYEES'
AND owner = 'HR';
-- 取得した DDL のテーブル名を置換して実行すれば制約・インデックスを再作成できる
Data Pump(expdp / impdp)を使えば、制約・インデックス・トリガー・権限を含めてテーブルの完全なコピーが可能です。詳細は「Data Pumpの使い方まとめ」を参照してください。
コピー先の表領域を指定する
-- デフォルトとは別の表領域にコピー CREATE TABLE employees_copy TABLESPACE users_data AS SELECT * FROM employees; -- 表領域を分けたい場面: -- ・テスト用テーブルを別の表領域に隔離 -- ・SSD 上の表領域に高速テーブルをコピー -- ・バックアップ用テーブルを大容量表領域に作成
DB Link 経由で別データベースのテーブルをコピー
DB Link(データベースリンク)を使えば、リモートの Oracle データベースからテーブルをコピーできます。
-- DB Link の作成(事前準備) CREATE DATABASE LINK remote_db CONNECT TO remote_user IDENTIFIED BY password USING 'remote_tns_name'; -- リモートテーブルの構造 + データをコピー(CTAS) CREATE TABLE local_orders AS SELECT * FROM orders@remote_db; -- リモートから条件付きでコピー CREATE TABLE recent_orders AS SELECT * FROM orders@remote_db WHERE order_date >= DATE '2025-01-01'; -- 既存テーブルにリモートデータを追加 INSERT INTO local_orders SELECT * FROM orders@remote_db WHERE order_id NOT IN (SELECT order_id FROM local_orders); COMMIT;
DB Link は SQL*Net 経由でデータを転送するため、大量データ(数百万行以上)のコピーには時間がかかります。大量データの場合は Data Pump(expdp / impdp)でダンプファイルを転送する方が高速です。
大量データの高速コピー(NOLOGGING + APPEND + PARALLEL)
数百万〜数千万行のテーブルをコピーする場合、デフォルト設定では REDO ログの書き込みがボトルネックになります。以下のオプションを組み合わせることで大幅に高速化できます。
-- NOLOGGING + PARALLEL で高速 CTAS CREATE TABLE employees_copy NOLOGGING PARALLEL 4 AS SELECT * FROM employees; -- コピー後に LOGGING に戻す(RMAN バックアップのため) ALTER TABLE employees_copy LOGGING; ALTER TABLE employees_copy NOPARALLEL;
-- コピー先テーブルを NOLOGGING に設定 ALTER TABLE employees_backup NOLOGGING; -- APPEND ヒント(ダイレクトパスインサート)で高速コピー INSERT /*+ APPEND PARALLEL(4) */ INTO employees_backup SELECT * FROM employees; COMMIT; -- NOLOGGING を元に戻す ALTER TABLE employees_backup LOGGING;
| オプション | 効果 | 注意点 |
|---|---|---|
| NOLOGGING | REDO ログを最小化(書き込みを大幅削減) | クラッシュ時にデータが復旧不可。コピー後に LOGGING に戻し、フルバックアップを取ること |
| APPEND ヒント | ダイレクトパスインサート(HWM 以降に追記) | 既存データの後ろに追記。同一トランザクション内で同テーブルの SELECT 不可 |
| PARALLEL N | N プロセスで並列実行 | CPU・I/O リソースを消費。他のセッションへの影響を考慮 |
NOLOGGING で書き込んだデータは REDO ログに記録されないため、コピー後にデータベースがクラッシュすると復旧できません。NOLOGGING 操作後はテーブルを LOGGING に戻し、RMAN でフルバックアップを取ってください。
別スキーマのテーブルをコピーする
-- HR スキーマの employees を現在のスキーマにコピー CREATE TABLE employees_copy AS SELECT * FROM hr.employees; -- 権限: hr.employees への SELECT 権限が必要 -- 別スキーマにコピーを作成する場合 CREATE TABLE test_schema.employees_copy AS SELECT * FROM hr.employees; -- 権限: test_schema での CREATE TABLE 権限 + hr.employees への SELECT 権限が必要
実務パターン集
パターン①:テスト用コピーの作成
-- テスト用にテーブルをコピー(接尾辞 _test を付与) CREATE TABLE orders_test AS SELECT * FROM orders; -- テスト用なので制約は最小限(PK のみ追加) ALTER TABLE orders_test ADD CONSTRAINT pk_orders_test PRIMARY KEY (order_id); -- テスト後に削除 DROP TABLE orders_test PURGE;
パターン②:日次バックアップテーブルの作成
-- PL/SQL で日付付きバックアップテーブルを動的作成
DECLARE
v_table_name VARCHAR2(100);
BEGIN
v_table_name := 'ORDERS_BK_' || TO_CHAR(SYSDATE, 'YYYYMMDD');
EXECUTE IMMEDIATE
'CREATE TABLE ' || v_table_name || ' AS SELECT * FROM orders';
DBMS_OUTPUT.PUT_LINE('Created: ' || v_table_name);
END;
/
-- 結果: ORDERS_BK_20260328 テーブルが作成される
パターン③:本番テーブルの構造変更時のバックアップ
-- ① 現在のテーブルをバックアップ CREATE TABLE employees_bk_20260328 AS SELECT * FROM employees; -- ② ALTER TABLE で構造変更 ALTER TABLE employees ADD (middle_name VARCHAR2(50)); -- ③ 問題が発生した場合はバックアップから復元 -- TRUNCATE TABLE employees; -- INSERT INTO employees SELECT emp.*, NULL FROM employees_bk_20260328 emp; -- COMMIT;
パターン④:大量データの分割コピー
-- 大量データを 10 万件ずつ分割してコピー(UNDO 溢れ防止)
BEGIN
LOOP
INSERT /*+ APPEND */ INTO orders_archive
SELECT * FROM orders
WHERE order_date < DATE '2024-01-01'
AND ROWNUM <= 100000;
EXIT WHEN SQL%ROWCOUNT = 0;
COMMIT;
END LOOP;
END;
/
よくある質問
ALTER TABLE ... ADD CONSTRAINT で手動追加するか、DBMS_METADATA.GET_DDL で元テーブルの DDL を取得して適用してください。CREATE TABLE new_table AS SELECT * FROM old_table WHERE 1=0; でカラム定義のみコピーされ、データは 0 件になります。WHERE ROWNUM < 1 でも同じ結果です。DROP TABLE ... PURGE で削除します。ROLLBACK が必要な場合は、先にテーブルを作成してから INSERT INTO … SELECT を使ってください。CREATE TABLE local_copy AS SELECT * FROM table_name@db_link_name; でリモートデータベースのテーブルをコピーできます。大量データの場合は DB Link よりも Data Pump(expdp/impdp) の方が高速です。まとめ
Oracle でテーブルをコピーする方法の要点をまとめます。
| やりたいこと | 推奨方式 |
|---|---|
| テーブル構造 + データを丸ごとコピー | CREATE TABLE new AS SELECT * FROM old |
| 構造のみコピー(データなし) | CREATE TABLE new AS SELECT * FROM old WHERE 1=0 |
| 条件に合うデータだけコピー | CREATE TABLE new AS SELECT * FROM old WHERE 条件 |
| 既存テーブルにデータを追加コピー | INSERT INTO target SELECT * FROM source |
| 制約・インデックスも含めて完全コピー | Data Pump(expdp/impdp) |
| 別データベースからコピー | CREATE TABLE local AS SELECT * FROM remote@dblink |
| 大量データを高速コピー | NOLOGGING + PARALLEL + CTAS(またはAPPEND ヒント) |
| ROLLBACK 可能な安全なコピー | INSERT INTO … SELECT(DML) |
CREATE TABLE の構文詳細は「テーブルを作成する方法完全ガイド」、ALTER TABLE については「ALTER TABLE完全ガイド」、Data Pump による完全なテーブルコピーは「Data Pumpの使い方まとめ」も併せて参照してください。
