Data Pump Import(impdp)でテーブルをリストアするとき、「既存データを上書きしてしまった」「依存テーブルの整合性が壊れた」というトラブルは起きがちです。リストア操作は一歩間違えると既存の本番データを破壊する危険があるため、安全策を講じてから実行することが不可欠です。
本記事では、テーブル単位のリストアで既存データを保護するための具体的なテクニックを解説します。REMAP_TABLE で別名に復元してから比較・マージする安全ワークフロー、外部キー制約がある場合の対処、リストア前後の検証手順、そしてロールバック戦略までカバーします。
この記事でわかること
・table_exists_action を「データ保護」の観点で選ぶ基準
・REMAP_TABLE で別名リストア→比較→MERGE の安全ワークフロー
・content=DATA_ONLY / METADATA_ONLY の保護観点での使い分け
・外部キー制約がある場合のリストア手順
・リストア前後の COUNT / MINUS / CHECKSUM 検証手順
・ロールバック可能なリストア戦略
・table_exists_action を「データ保護」の観点で選ぶ基準
・REMAP_TABLE で別名リストア→比較→MERGE の安全ワークフロー
・content=DATA_ONLY / METADATA_ONLY の保護観点での使い分け
・外部キー制約がある場合のリストア手順
・リストア前後の COUNT / MINUS / CHECKSUM 検証手順
・ロールバック可能なリストア戦略
リストア時のリスクと table_exists_action の選び方
| table_exists_action | 既存データへの影響 | リスク | 保護の観点 |
|---|---|---|---|
| SKIP(デフォルト) | 一切変更しない | なし(最も安全) | リストアが実行されないため復元にならない場合がある |
| APPEND | 既存データを維持して追加 | 主キー重複エラーの可能性 | 既存行は安全。ただし重複挿入に注意 |
| TRUNCATE | 既存データを全削除して投入 | 高い(データ喪失) | テーブル構造は維持されるが、既存データは消える |
| REPLACE | テーブルを DROP して再作成 | 最も高い | 構造も含めて完全に置き換わる。制約・インデックスも再作成 |
TRUNCATE / REPLACE は既存データを破壊する
TRUNCATE は全行を削除してからインポートし、REPLACE はテーブル自体を DROP します。いずれも元に戻せません(DDL による暗黙 COMMIT)。本番環境では TRUNCATE / REPLACE を使う前に必ずバックアップを取得してください。
TRUNCATE は全行を削除してからインポートし、REPLACE はテーブル自体を DROP します。いずれも元に戻せません(DDL による暗黙 COMMIT)。本番環境では TRUNCATE / REPLACE を使う前に必ずバックアップを取得してください。
既存データを保護する最も安全な方法
REMAP_TABLE で別名にリストアし、内容を確認してから本番テーブルに反映する方法です。既存テーブルに一切触れずにリストアを完了でき、問題があれば別名テーブルを DROP するだけで元に戻せます。
REMAP_TABLE で別名にリストアし、内容を確認してから本番テーブルに反映する方法です。既存テーブルに一切触れずにリストアを完了でき、問題があれば別名テーブルを DROP するだけで元に戻せます。
安全ワークフロー:別名リストア→比較→マージ
これが最も推奨される安全なリストア手順です。本番テーブルに直接触れず、段階的にデータを反映します。
ステップ(1): 別名でリストア
Shell(REMAP_TABLE で別名リストア)
# employees テーブルを employees_restore として復元
impdp hr/password \
directory=DP_DIR \
dumpfile=hr_backup_20260327.dmp \
logfile=restore_remap.log \
tables=HR.EMPLOYEES \
remap_table=HR.EMPLOYEES:HR.EMPLOYEES_RESTORE
ステップ(2): リストアデータの確認・比較
SQL(本番 vs リストアの比較)
-- 件数の比較
SELECT 'CURRENT' AS src, COUNT(*) AS cnt FROM employees
UNION ALL
SELECT 'RESTORE', COUNT(*) FROM employees_restore;
-- 本番にあってリストアにないデータ(本番で追加された行)
SELECT employee_id, last_name FROM employees
MINUS
SELECT employee_id, last_name FROM employees_restore;
-- リストアにあって本番にないデータ(復元対象)
SELECT employee_id, last_name FROM employees_restore
MINUS
SELECT employee_id, last_name FROM employees;
-- 同じ主キーで値が異なるレコードを確認
SELECT
c.employee_id,
c.salary AS current_salary,
r.salary AS restore_salary
FROM employees c
JOIN employees_restore r ON c.employee_id = r.employee_id
WHERE c.salary <> r.salary
OR NVL(c.department_id, -1) <> NVL(r.department_id, -1);
ステップ(3): MERGE で差分だけ反映
SQL(MERGE で安全に反映)
-- 本番テーブルを保護しつつ、リストアデータの差分だけ反映
MERGE INTO employees e
USING employees_restore r
ON (e.employee_id = r.employee_id)
WHEN MATCHED THEN
UPDATE SET
e.salary = r.salary,
e.department_id = r.department_id,
e.updated_at = SYSDATE
WHERE e.salary <> r.salary
OR NVL(e.department_id, -1) <> NVL(r.department_id, -1)
WHEN NOT MATCHED THEN
INSERT (employee_id, last_name, salary, department_id, created_at)
VALUES (r.employee_id, r.last_name, r.salary, r.department_id, SYSDATE);
-- COMMIT 前に件数を確認
SELECT SQL%ROWCOUNT || ' rows merged' FROM DUAL;
COMMIT;
ステップ(4): 後片付け
SQL(一時テーブルの削除)
-- 反映完了を確認してから削除 DROP TABLE employees_restore PURGE;
このワークフローのメリット
・本番テーブルに直接 impdp しないため、失敗しても既存データは無傷
・MERGE は DML なので COMMIT 前に ROLLBACK 可能
・比較ステップで差分を確認してから反映できる(目視確認可能)
・問題があれば employees_restore を DROP するだけで元に戻せる
・本番テーブルに直接 impdp しないため、失敗しても既存データは無傷
・MERGE は DML なので COMMIT 前に ROLLBACK 可能
・比較ステップで差分を確認してから反映できる(目視確認可能)
・問題があれば employees_restore を DROP するだけで元に戻せる
APPEND モードで安全に追加する
既存データを維持したまま、バックアップから不足分だけを追加したい場合はtable_exists_action=APPEND を使います。ただし主キー重複に注意が必要です。
Shell(APPEND で安全に追加)
# 既存データを維持し、ダンプのデータを追加
impdp hr/password \
directory=DP_DIR \
dumpfile=hr_backup.dmp \
logfile=append_import.log \
tables=HR.EMPLOYEES \
table_exists_action=APPEND \
content=DATA_ONLY
# 主キー重複が発生した場合は ORA-00001 で該当行がスキップされる
# ログファイルでスキップされた行数を確認
APPEND + 主キー重複の動作
主キーが重複する行は ORA-00001 エラーでスキップされます。既存行は上書きされず安全ですが、「同じ主キーで値が変わっている行を更新したい」場合は APPEND では対応できません。その場合は前述の REMAP + MERGE ワークフローを使ってください。
主キーが重複する行は ORA-00001 エラーでスキップされます。既存行は上書きされず安全ですが、「同じ主キーで値が変わっている行を更新したい」場合は APPEND では対応できません。その場合は前述の REMAP + MERGE ワークフローを使ってください。
content パラメータで保護範囲を制御する
| content 値 | 動作 | 既存テーブル構造 | 既存データ | 適するケース |
|---|---|---|---|---|
| ALL(デフォルト) | 構造 + データの両方をインポート | table_exists_action に依存 | table_exists_action に依存 | 通常のリストア |
| DATA_ONLY | データのみインポート(DDL は実行しない) | 維持される | APPEND / TRUNCATE に依存 | テーブル構造を変えずにデータだけ復元 |
| METADATA_ONLY | DDL のみ実行(データは投入しない) | 再作成される | なし(空テーブル) | テーブル構造だけコピーしたい場合 |
Shell(DATA_ONLY + TRUNCATE: 構造を維持してデータ入れ替え)
# テーブル構造(インデックス・制約)を維持し、データだけ入れ替え
impdp hr/password \
directory=DP_DIR \
dumpfile=hr_backup.dmp \
tables=HR.EMPLOYEES \
content=DATA_ONLY \
table_exists_action=TRUNCATE
content=DATA_ONLY の詳細は「impdp でデータだけをインポートする方法」を参照してください。
外部キー制約がある場合のリストア手順
親テーブルと子テーブルに外部キー制約がある場合、リストアの順序を間違えると参照整合性違反(ORA-02291)が発生します。
SQL(外部キーを考慮した安全な手順)
-- (1) 依存関係を確認
SELECT a.table_name AS child_table,
a.constraint_name,
c_pk.table_name AS parent_table
FROM user_constraints a
JOIN user_constraints c_pk ON a.r_constraint_name = c_pk.constraint_name
WHERE a.constraint_type = 'R'
AND (a.table_name = 'EMPLOYEES' OR c_pk.table_name = 'EMPLOYEES');
-- (2) 外部キー制約を一時的に無効化
ALTER TABLE order_details DISABLE CONSTRAINT fk_order_emp;
-- (3) リストアを実行
-- impdp ... tables=HR.EMPLOYEES table_exists_action=REPLACE
-- (4) 外部キー制約を再有効化
ALTER TABLE order_details ENABLE CONSTRAINT fk_order_emp;
-- 整合性違反があれば ORA-02298 で有効化に失敗する
SQL(impdp で制約を除外してインポート→後で追加)
# 制約を除外してインポート(外部キー制約でエラーにならない)
impdp hr/password \
directory=DP_DIR \
dumpfile=hr_backup.dmp \
tables=HR.EMPLOYEES \
table_exists_action=REPLACE \
exclude=CONSTRAINT
# インポート後に制約を手動で追加
# ALTER TABLE employees ADD CONSTRAINT pk_emp PRIMARY KEY (employee_id);
# ALTER TABLE employees ADD CONSTRAINT fk_emp_dept FOREIGN KEY (dept_id) REFERENCES departments(dept_id);
依存テーブルがある場合は REMAP 方式が最も安全
別名でリストアすれば既存の外部キー制約には一切影響しません。MERGE で反映すれば参照整合性を維持したままデータを更新できます。
別名でリストアすれば既存の外部キー制約には一切影響しません。MERGE で反映すれば参照整合性を維持したままデータを更新できます。
リストア前後の検証手順
| 検証項目 | SQL | 確認内容 |
|---|---|---|
| 件数比較 | SELECT COUNT(*) FROM table | リストア前後で件数が期待通りか |
| 差分確認 | SELECT * FROM current MINUS SELECT * FROM restore | 追加・削除された行 |
| データ整合性 | SELECT COUNT(*) FROM child WHERE parent_id NOT IN (SELECT id FROM parent) | 外部キー整合性 |
| NULL 件数 | SELECT COUNT(*) FROM table WHERE key_col IS NULL | NULL の増減 |
| チェックサム | SELECT SUM(ORA_HASH(col1||col2||col3)) FROM table | データ全体の整合性 |
SQL(リストア前後の自動検証スクリプト)
-- リストア前に件数とチェックサムを記録
CREATE TABLE restore_check AS
SELECT 'BEFORE' AS phase,
COUNT(*) AS row_count,
SUM(ORA_HASH(employee_id || salary || department_id)) AS checksum
FROM employees;
-- (ここでリストアを実行)
-- リストア後の値を追加
INSERT INTO restore_check
SELECT 'AFTER', COUNT(*), SUM(ORA_HASH(employee_id || salary || department_id))
FROM employees;
-- 比較
SELECT * FROM restore_check;
-- BEFORE と AFTER を比較して期待通りか確認
ロールバック戦略
| リストア方式 | ロールバック方法 | 難易度 |
|---|---|---|
| REMAP + MERGE | ROLLBACK(COMMIT 前なら即時) | 簡単(最も推奨) |
| APPEND | ROLLBACK(COMMIT 前)または追加行を DELETE | 簡単 |
| TRUNCATE | バックアップから再リストア(TRUNCATE は DDL = 自動 COMMIT) | 困難 |
| REPLACE | バックアップから再リストア(DROP は DDL = 自動 COMMIT) | 困難 |
TRUNCATE / REPLACE を使う場合は事前バックアップ必須
TRUNCATE と REPLACE は DDL 操作であり自動 COMMIT されます。実行後に ROLLBACK はできません。本番テーブルに TRUNCATE / REPLACE を適用する場合は、事前に
TRUNCATE と REPLACE は DDL 操作であり自動 COMMIT されます。実行後に ROLLBACK はできません。本番テーブルに TRUNCATE / REPLACE を適用する場合は、事前に
CREATE TABLE table_bk AS SELECT * FROM table でバックアップを取得してから実行してください。リストア作業チェックリスト
| # | 作業 | 備考 |
|---|---|---|
| 1 | 対象テーブルの現在の件数・チェックサムを記録 | restore_check テーブルに保存 |
| 2 | 対象テーブルのバックアップを作成 | CREATE TABLE table_bk AS SELECT * FROM table |
| 3 | 外部キー制約の依存関係を確認 | USER_CONSTRAINTS で R 型を検索 |
| 4 | リストアを実行(REMAP 推奨) | impdp … remap_table=OLD:NEW |
| 5 | リストアデータを確認(件数・MINUS・値比較) | 別名テーブルと本番を比較 |
| 6 | MERGE で差分を本番に反映 | COMMIT 前に SQL%ROWCOUNT を確認 |
| 7 | リストア後の件数・チェックサムを確認 | BEFORE/AFTER の比較 |
| 8 | 外部キー整合性を確認 | 孤立レコードがないことを確認 |
| 9 | COMMIT | 問題なければ確定 |
| 10 | 一時テーブル・バックアップテーブルを削除 | DROP TABLE xxx_restore PURGE |
よくある質問
Qtable_exists_action=SKIP だとデータが復元されません
ASKIP は「既存テーブルがあればスキップ」するため、テーブルが存在すると何も変わりません。既存テーブルにデータを追加したい場合は
APPEND、データを入れ替えたい場合は TRUNCATE または REPLACE を使います。最も安全なのは REMAP_TABLE で別名リストア→MERGE で反映する方法です。QREPLACE すると制約やインデックスはどうなりますか?
AREPLACE はテーブルを DROP してから再作成するため、制約・インデックス・トリガー・権限はダンプに含まれている範囲で再作成されます。ダンプ取得後に追加した制約やインデックスは失われます。テーブル構造を維持したい場合は
content=DATA_ONLY + table_exists_action=TRUNCATE を使ってください。QMERGE で反映中にエラーが出たらどうなりますか?
AMERGE は DML なので、COMMIT 前であれば ROLLBACK で全件取り消しできます。これが REMAP + MERGE 方式の最大のメリットです。TRUNCATE や REPLACE は DDL のため自動 COMMIT され、取り消しできません。
Q外部キー制約を無効化せずにリストアできますか?
AREMAP_TABLE で別名リストアすれば、既存の外部キー制約には影響しません。MERGE で反映するときに参照整合性が保たれていれば、制約を無効化する必要はありません。直接 REPLACE / TRUNCATE する場合は、外部キー制約を一時的に無効化するか、親テーブル→子テーブルの順にリストアしてください。
Qリストア後にデータが正しいことをどう確認しますか?
A(1) 件数比較: リストア前後の COUNT(*) を比較
(2) 差分確認: MINUS で追加・削除された行を確認
(3) チェックサム: ORA_HASH で全行のハッシュ値を比較
(4) 外部キー整合性: 孤立レコード(親なし子行)がないか確認
これらを自動化した検証スクリプトを用意しておくと、繰り返しのリストア作業が効率化されます。
(2) 差分確認: MINUS で追加・削除された行を確認
(3) チェックサム: ORA_HASH で全行のハッシュ値を比較
(4) 外部キー整合性: 孤立レコード(親なし子行)がないか確認
これらを自動化した検証スクリプトを用意しておくと、繰り返しのリストア作業が効率化されます。
Q大量データのテーブルを安全にリストアするコツは?
A(1) REMAP + MERGE の場合: MERGE を ROWNUM でバッチ分割(1 万件ずつ COMMIT)
(2) TRUNCATE + DATA_ONLY の場合: 事前にバックアップテーブルを作成してから実行
(3) パラレル: impdp の parallel パラメータでインポートを並列化
大量データでは CTAS でバックアップ → TRUNCATE + DATA_ONLY が最速です。
(2) TRUNCATE + DATA_ONLY の場合: 事前にバックアップテーブルを作成してから実行
(3) パラレル: impdp の parallel パラメータでインポートを並列化
大量データでは CTAS でバックアップ → TRUNCATE + DATA_ONLY が最速です。
まとめ
テーブルリストア時のデータ保護方法をまとめます。
| 安全度 | 方法 | 説明 |
|---|---|---|
| 最も安全 | REMAP_TABLE + MERGE | 別名リストア→比較→MERGE で差分反映。ROLLBACK 可能 |
| 安全 | APPEND + DATA_ONLY | 既存データを維持して不足分を追加。主キー重複はスキップ |
| 注意が必要 | TRUNCATE + DATA_ONLY | テーブル構造は維持されるが既存データは全削除。事前バックアップ必須 |
| 最も注意が必要 | REPLACE | テーブルを DROP して完全に再作成。事前バックアップ必須 |
テーブル単位のリストア方法の全体像は「指定テーブルのみリストアする方法」、Data Pump の基本は「Data Pump の使い方完全ガイド」、ORA-31684 エラーの対処は「ORA-31684 完全解説」も併せて参照してください。

