【Oracle】テーブルをコピーする方法完全ガイド|CTAS・INSERT SELECT・構造のみ・制約の再作成・DB Link・高速化まで解説

【Oracle】テーブルをコピーする方法完全ガイド|CTAS・INSERT SELECT・構造のみ・制約の再作成・DB Link・高速化まで解説 Oracle

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 でテーブル構造とデータが同時にコピーされます。

SQL(CTAS 基本)
-- employees テーブルを employees_copy としてコピー
CREATE TABLE employees_copy AS
SELECT * FROM employees;

-- コピー後の確認
SELECT COUNT(*) FROM employees_copy;

特定の列だけコピーする

SQL(列を指定してコピー)
-- 必要な列だけコピー
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 句で絞り込み)

SQL(条件を指定してコピー)
-- 部門 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';

構造のみコピー(データなし)

テーブルのカラム定義だけをコピーし、データは入れない方法です。テスト用のテンプレートや一時テーブルの作成に使います。

SQL(構造のみコピー)
-- 方法①: 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=0WHERE ROWNUM < 1 はどちらも 0 件を返します。Oracle は実行計画で「行が返らない」ことを認識するため、テーブル全体のスキャンは発生しません(高速)。

INSERT INTO … SELECT で既存テーブルにコピー

INSERT INTO ... SELECT は、既に存在するテーブルにデータをコピーする方法です。CTAS と異なり、コピー先のテーブルは事前に作成されている必要があります。

SQL(INSERT INTO … SELECT 基本)
-- 既存テーブルにデータをコピー
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 は DDL(自動 COMMIT)
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 で再付与

制約・インデックスを再作成する方法

SQL(コピー先に制約・インデックスを追加)
-- ① 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 を取得する

SQL(元テーブルの制約・インデックス 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 を検討
Data Pump(expdp / impdp)を使えば、制約・インデックス・トリガー・権限を含めてテーブルの完全なコピーが可能です。詳細は「Data Pumpの使い方まとめ」を参照してください。

コピー先の表領域を指定する

SQL(表領域を指定して CTAS)
-- デフォルトとは別の表領域にコピー
CREATE TABLE employees_copy
TABLESPACE users_data
AS SELECT * FROM employees;

-- 表領域を分けたい場面:
-- ・テスト用テーブルを別の表領域に隔離
-- ・SSD 上の表領域に高速テーブルをコピー
-- ・バックアップ用テーブルを大容量表領域に作成

DB Link 経由で別データベースのテーブルをコピー

DB Link(データベースリンク)を使えば、リモートの Oracle データベースからテーブルをコピーできます。

SQL(DB Link でリモートテーブルをコピー)
-- 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 経由の大量データコピーは遅い
DB Link は SQL*Net 経由でデータを転送するため、大量データ(数百万行以上)のコピーには時間がかかります。大量データの場合は Data Pump(expdp / impdp)でダンプファイルを転送する方が高速です。

大量データの高速コピー(NOLOGGING + APPEND + PARALLEL)

数百万〜数千万行のテーブルをコピーする場合、デフォルト設定では REDO ログの書き込みがボトルネックになります。以下のオプションを組み合わせることで大幅に高速化できます。

SQL(CTAS の高速化)
-- 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;
SQL(INSERT の高速化: APPEND ヒント + NOLOGGING)
-- コピー先テーブルを 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 後は必ずバックアップを取る
NOLOGGING で書き込んだデータは REDO ログに記録されないため、コピー後にデータベースがクラッシュすると復旧できません。NOLOGGING 操作後はテーブルを LOGGING に戻し、RMAN でフルバックアップを取ってください。

別スキーマのテーブルをコピーする

SQL(別スキーマからコピー)
-- 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 権限が必要

実務パターン集

パターン①:テスト用コピーの作成

SQL(テスト用のテーブルコピー)
-- テスト用にテーブルをコピー(接尾辞 _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;

パターン②:日次バックアップテーブルの作成

SQL(日付付きバックアップテーブル)
-- 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 テーブルが作成される

パターン③:本番テーブルの構造変更時のバックアップ

SQL(ALTER TABLE 前のバックアップ)
-- ① 現在のテーブルをバックアップ
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;

パターン④:大量データの分割コピー

SQL(バッチ処理で分割コピー)
-- 大量データを 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;
/

よくある質問

QCTAS で PRIMARY KEY もコピーされますか?
Aいいえ。CTAS でコピーされるのはカラム定義と NOT NULL 制約のみです。PRIMARY KEY、UNIQUE、FOREIGN KEY、CHECK 制約、インデックス、トリガーはコピーされません。コピー後に ALTER TABLE ... ADD CONSTRAINT で手動追加するか、DBMS_METADATA.GET_DDL で元テーブルの DDL を取得して適用してください。
Q構造だけコピーしてデータを入れない方法は?
ACREATE TABLE new_table AS SELECT * FROM old_table WHERE 1=0; でカラム定義のみコピーされ、データは 0 件になります。WHERE ROWNUM < 1 でも同じ結果です。
QCTAS は ROLLBACK できますか?
ACTAS は DDL(CREATE TABLE)なので自動 COMMIT されます。ROLLBACK はできません。失敗した場合は DROP TABLE ... PURGE で削除します。ROLLBACK が必要な場合は、先にテーブルを作成してから INSERT INTO … SELECT を使ってください。
Q別のデータベースからテーブルをコピーするには?
ADB Link を使います。CREATE TABLE local_copy AS SELECT * FROM table_name@db_link_name; でリモートデータベースのテーブルをコピーできます。大量データの場合は DB Link よりも Data Pump(expdp/impdp) の方が高速です。
QNOLOGGING にするとデータが消えますか?
ANOLOGGING 自体でデータが消えることはありません。ただし NOLOGGING で書き込んだデータは REDO ログに記録されないため、メディアリカバリ(RMAN リストア)時に復旧できません。NOLOGGING 操作後は必ずテーブルを LOGGING に戻し、フルバックアップを取ってください。
Qコピー元のテーブルにロックがかかりますか?
ACTAS / INSERT INTO … SELECT は元テーブルに対して共有ロック(読み取りロック)を取得します。元テーブルへの SELECT は可能ですが、DML(INSERT/UPDATE/DELETE)は待たされる場合があります。本番稼働中にコピーする場合は、負荷が低い時間帯に実行することを推奨します。

まとめ

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の使い方まとめ」も併せて参照してください。