Oracle の開発環境では、テストデータのリセット、テーブルの再作成、シーケンスの初期化など環境を初期状態に戻す作業が頻繁に発生します。毎回手動で実行するのは手間がかかりミスの原因にもなります。
本記事では、開発環境の初期化をスクリプト化する方法を、SQL ベース(DROP + 再作成)、Data Pump ベース(ダンプからの復元)、FLASHBACK DATABASE(特定時点への巻き戻し)の 3 パターンで紹介し、本番環境での誤実行を防ぐ安全策まで解説します。
この記事でわかること
・全テーブル DROP + 再作成のスクリプト
・シーケンスのリセット方法
・テストデータの投入スクリプト
・Data Pump ダンプからの一括復元による初期化
・FLASHBACK DATABASE による初期状態への巻き戻し
・Shell 統合スクリプト(バックアップ → 初期化 → テストデータ投入)
・本番環境での誤実行を防ぐ安全策
・全テーブル DROP + 再作成のスクリプト
・シーケンスのリセット方法
・テストデータの投入スクリプト
・Data Pump ダンプからの一括復元による初期化
・FLASHBACK DATABASE による初期状態への巻き戻し
・Shell 統合スクリプト(バックアップ → 初期化 → テストデータ投入)
・本番環境での誤実行を防ぐ安全策
初期化の 3 つのアプローチ
| アプローチ | 方法 | メリット | デメリット |
|---|---|---|---|
| SQL ベース | DROP + CREATE + INSERT の SQL スクリプトを実行 | テーブル構造やテストデータを細かく制御可能 | テーブル数が多いと管理が大変 |
| Data Pump ベース | 初期状態のダンプを impdp で復元 | テーブル数に関係なく一発で復元 | ダンプファイルの管理が必要 |
| FLASHBACK DATABASE | リストアポイントに巻き戻し | 最速(秒単位) | ARCHIVELOG + FLASHBACK 設定が必要 |
アプローチの選び方
・テーブル数が少ない(数十以下)→ SQL ベース(最もシンプル)
・テーブル数が多い / スキーマ全体をリセット → Data Pump ベース
・秒単位で高速に戻したい → FLASHBACK DATABASE
・テーブル数が少ない(数十以下)→ SQL ベース(最もシンプル)
・テーブル数が多い / スキーマ全体をリセット → Data Pump ベース
・秒単位で高速に戻したい → FLASHBACK DATABASE
SQL ベース: DROP + 再作成スクリプト
全テーブルの DROP
SQL(スキーマ内の全テーブルを DROP)
-- PL/SQL で全テーブルを自動 DROP
BEGIN
-- 外部キー制約を先に無効化(DROP 順序の問題を回避)
FOR rec IN (
SELECT constraint_name, table_name
FROM user_constraints
WHERE constraint_type = 'R' -- 外部キーのみ
) LOOP
EXECUTE IMMEDIATE 'ALTER TABLE ' || rec.table_name
|| ' DROP CONSTRAINT ' || rec.constraint_name;
END LOOP;
-- 全テーブルを DROP
FOR rec IN (
SELECT table_name FROM user_tables
) LOOP
EXECUTE IMMEDIATE 'DROP TABLE ' || rec.table_name || ' CASCADE CONSTRAINTS PURGE';
END LOOP;
END;
/
テーブルの再作成
SQL(create_tables.sql の例)
-- テーブル作成スクリプト(DDL)
CREATE TABLE departments (
dept_id NUMBER PRIMARY KEY,
dept_name VARCHAR2(100) NOT NULL
);
CREATE TABLE employees (
emp_id NUMBER PRIMARY KEY,
emp_name VARCHAR2(100) NOT NULL,
dept_id NUMBER REFERENCES departments(dept_id),
salary NUMBER,
hire_date DATE DEFAULT SYSDATE
);
CREATE INDEX idx_emp_dept ON employees(dept_id);
シーケンスのリセット
SQL(シーケンスを DROP + 再作成でリセット)
-- PL/SQL で全シーケンスを DROP
BEGIN
FOR rec IN (
SELECT sequence_name FROM user_sequences
) LOOP
EXECUTE IMMEDIATE 'DROP SEQUENCE ' || rec.sequence_name;
END LOOP;
END;
/
-- シーケンスを再作成
CREATE SEQUENCE seq_dept_id START WITH 1 INCREMENT BY 1;
CREATE SEQUENCE seq_emp_id START WITH 1 INCREMENT BY 1;
SQL(シーケンスを DROP せずに値だけリセット)
-- シーケンスを 1 にリセットする方法(DROP 不要)
DECLARE
v_val NUMBER;
BEGIN
-- 現在値を取得
SELECT seq_emp_id.NEXTVAL INTO v_val FROM DUAL;
-- INCREMENT を -(現在値-1) に変更して NEXTVAL で 1 に戻す
EXECUTE IMMEDIATE 'ALTER SEQUENCE seq_emp_id INCREMENT BY -' || (v_val - 1);
SELECT seq_emp_id.NEXTVAL INTO v_val FROM DUAL;
-- INCREMENT を元に戻す
EXECUTE IMMEDIATE 'ALTER SEQUENCE seq_emp_id INCREMENT BY 1';
END;
/
テストデータの投入
SQL(insert_test_data.sql)
-- テストデータ投入 INSERT INTO departments VALUES (seq_dept_id.NEXTVAL, '営業部'); INSERT INTO departments VALUES (seq_dept_id.NEXTVAL, '開発部'); INSERT INTO departments VALUES (seq_dept_id.NEXTVAL, '総務部'); INSERT INTO employees VALUES (seq_emp_id.NEXTVAL, '田中太郎', 1, 5000, DATE '2020-04-01'); INSERT INTO employees VALUES (seq_emp_id.NEXTVAL, '鈴木花子', 2, 6000, DATE '2021-04-01'); INSERT INTO employees VALUES (seq_emp_id.NEXTVAL, '佐藤一郎', 1, 4500, DATE '2022-04-01'); COMMIT;
統合 SQL スクリプト
SQL(reset_dev.sql: 全処理を統合)
-- reset_dev.sql: 開発環境の一括初期化 SET SERVEROUTPUT ON PROMPT ===== 開発環境の初期化を開始 ===== -- (1) 全テーブル DROP @drop_all_tables.sql -- (2) テーブル作成 @create_tables.sql -- (3) シーケンス作成 @create_sequences.sql -- (4) テストデータ投入 @insert_test_data.sql -- (5) 統計情報の収集 EXEC DBMS_STATS.GATHER_SCHEMA_STATS(USER); PROMPT ===== 初期化完了 =====
Data Pump ベース: ダンプからの一括復元
初期状態のダンプファイルを 1 つ保管しておけば、何度でも同じ状態に復元できます。テーブル数が多い環境ではこちらが効率的です。
初期状態のダンプを作成(初回のみ)
Shell(初期状態をダンプ)
# テスト環境を初期状態にした後にダンプを取得
expdp dev_user/password \
directory=DP_DIR \
dumpfile=dev_initial_state.dmp \
logfile=dev_initial_exp.log \
schemas=DEV_USER \
compression=ALL
# このダンプを「初期状態マスター」として保管
初期化時: ダンプから復元
Shell(ダンプからの復元)
# 全オブジェクトを削除してからダンプで復元
impdp dev_user/password \
directory=DP_DIR \
dumpfile=dev_initial_state.dmp \
logfile=dev_reset.log \
schemas=DEV_USER \
table_exists_action=REPLACE
Data Pump ベースのメリット
・テーブルが数百あっても 1 コマンドで初期化
・制約 / インデックス / シーケンス / PL/SQL もまとめて復元
・ダンプファイルをバージョン管理すれば「どの初期状態に戻すか」も選択可能
・テーブルが数百あっても 1 コマンドで初期化
・制約 / インデックス / シーケンス / PL/SQL もまとめて復元
・ダンプファイルをバージョン管理すれば「どの初期状態に戻すか」も選択可能
FLASHBACK DATABASE: 特定時点への巻き戻し(最速)
テスト前にリストアポイントを作成し、テスト後にそのポイントに巻き戻す方法です。秒単位で初期状態に戻せます。
SQL(FLASHBACK DATABASE による初期化)
-- (1) テスト開始前: リストアポイントを作成 CREATE RESTORE POINT dev_initial GUARANTEE FLASHBACK DATABASE; -- (2) テスト実施... -- (3) テスト完了: リストアポイントに巻き戻し -- SYSDBA で接続 SHUTDOWN IMMEDIATE; STARTUP MOUNT; FLASHBACK DATABASE TO RESTORE POINT dev_initial; ALTER DATABASE OPEN RESETLOGS; -- (4) 不要になったらリストアポイントを削除 DROP RESTORE POINT dev_initial;
FLASHBACK DATABASE の前提条件
・ARCHIVELOG モードが有効
・FLASHBACK DATABASE が有効(
・十分な FRA(高速リカバリ領域)の空き容量
・DB 全体が巻き戻るため、他のスキーマのデータも戻る(開発専用 DB 向け)
・ARCHIVELOG モードが有効
・FLASHBACK DATABASE が有効(
ALTER DATABASE FLASHBACK ON)・十分な FRA(高速リカバリ領域)の空き容量
・DB 全体が巻き戻るため、他のスキーマのデータも戻る(開発専用 DB 向け)
Shell 統合スクリプト
Shell(reset_dev_env.sh)
#!/bin/bash
# reset_dev_env.sh: 開発環境の一括初期化
set -e
export ORACLE_HOME=/oracle/product/19c/dbhome_1
export ORACLE_SID=DEVDB
SCRIPT_DIR=$(cd $(dirname $0) && pwd)
LOG_DIR=$SCRIPT_DIR/logs
mkdir -p $LOG_DIR
echo "[$(date)] 開発環境の初期化を開始" | tee -a $LOG_DIR/reset.log
# (1) 安全チェック: 本番 DB でないことを確認
DB_NAME=$(sqlplus -s / as sysdba <<EOF
SET HEADING OFF FEEDBACK OFF
SELECT name FROM v\$database;
EOF
)
if [ "$DB_NAME" != "DEVDB" ]; then
echo "ERROR: 本番 DB ($DB_NAME) では実行できません" >&2
exit 1
fi
# (2) Data Pump で初期状態を復元
impdp dev_user/password \
directory=DP_DIR \
dumpfile=dev_initial_state.dmp \
schemas=DEV_USER \
table_exists_action=REPLACE \
logfile=reset_import.log 2>&1 | tee -a $LOG_DIR/reset.log
# (3) 統計情報の収集
sqlplus dev_user/password <<EOF
EXEC DBMS_STATS.GATHER_SCHEMA_STATS(USER);
EOF
echo "[$(date)] 初期化完了" | tee -a $LOG_DIR/reset.log
本番環境での誤実行を防ぐ安全策
| 安全策 | 実装方法 |
|---|---|
| DB 名チェック | スクリプト内で V$DATABASE.NAME を確認。本番 DB 名なら即終了 |
| ユーザー名チェック | スクリプト内で USER を確認。本番スキーマなら即終了 |
| 環境変数チェック | 環境変数 ENV=dev の場合のみ実行を許可 |
| 確認プロンプト | ACCEPT で「本当に初期化しますか?(yes/no)」を表示 |
| ファイル名に DEV を含める | reset_DEV_only.sh のように名前で区別 |
| 本番サーバーにスクリプトを置かない | 開発サーバーのみにスクリプトを配置 |
SQL(スクリプト内の安全チェック例)
-- reset_dev.sql の先頭に安全チェックを追加
WHENEVER SQLERROR EXIT SQL.SQLCODE
DECLARE
v_db_name VARCHAR2(100);
BEGIN
SELECT name INTO v_db_name FROM v$database;
IF v_db_name NOT IN ('DEVDB', 'TESTDB') THEN
RAISE_APPLICATION_ERROR(-20999,
'本番DB(' || v_db_name || ')では実行できません');
END IF;
IF USER NOT IN ('DEV_USER', 'TEST_USER') THEN
RAISE_APPLICATION_ERROR(-20998,
'本番ユーザー(' || USER || ')では実行できません');
END IF;
END;
/
PROMPT 安全チェック OK: 初期化を続行します
-- ここから DROP / CREATE / INSERT 処理
本番 DB で初期化スクリプトを実行すると取り返しがつかない
DROP TABLE CASCADE は即座に全データが消え、COMMIT 前の ROLLBACK もできません(DDL は自動 COMMIT)。安全チェックをスクリプトの先頭に必ず組み込み、本番 DB での実行を物理的に防止してください。
DROP TABLE CASCADE は即座に全データが消え、COMMIT 前の ROLLBACK もできません(DDL は自動 COMMIT)。安全チェックをスクリプトの先頭に必ず組み込み、本番 DB での実行を物理的に防止してください。
TRUNCATE ベース: テーブル構造を維持してデータだけリセット
SQL(全テーブルを TRUNCATE)
-- テーブル構造(制約 / インデックス)を維持してデータだけ削除
BEGIN
-- 外部キー制約を一時無効化
FOR rec IN (
SELECT constraint_name, table_name FROM user_constraints
WHERE constraint_type = 'R'
) LOOP
EXECUTE IMMEDIATE 'ALTER TABLE ' || rec.table_name
|| ' DISABLE CONSTRAINT ' || rec.constraint_name;
END LOOP;
-- 全テーブルを TRUNCATE
FOR rec IN (SELECT table_name FROM user_tables) LOOP
EXECUTE IMMEDIATE 'TRUNCATE TABLE ' || rec.table_name;
END LOOP;
-- 外部キー制約を再有効化
FOR rec IN (
SELECT constraint_name, table_name FROM user_constraints
WHERE constraint_type = 'R'
) LOOP
EXECUTE IMMEDIATE 'ALTER TABLE ' || rec.table_name
|| ' ENABLE CONSTRAINT ' || rec.constraint_name;
END LOOP;
END;
/
-- その後テストデータを投入
@insert_test_data.sql
TRUNCATE vs DROP + CREATE の使い分け
・TRUNCATE: テーブル構造を変更しない場合。高速。インデックスも維持される
・DROP + CREATE: テーブル構造が変更された場合(列の追加 / 型変更など)
・TRUNCATE: テーブル構造を変更しない場合。高速。インデックスも維持される
・DROP + CREATE: テーブル構造が変更された場合(列の追加 / 型変更など)
テーブル以外のオブジェクトのリセット
SQL(ビュー / プロシージャ等の再作成)
-- ビューの再作成 @create_views.sql -- プロシージャ / ファンクション / パッケージの再作成 @create_packages.sql -- トリガーの再作成 @create_triggers.sql -- INVALID オブジェクトの再コンパイル EXEC DBMS_UTILITY.COMPILE_SCHEMA(USER);
SQL(全ビュー / 全プロシージャを動的に DROP)
-- 全ビューを DROP
BEGIN
FOR rec IN (SELECT view_name FROM user_views) LOOP
EXECUTE IMMEDIATE 'DROP VIEW ' || rec.view_name;
END LOOP;
END;
/
-- 全プロシージャを DROP
BEGIN
FOR rec IN (
SELECT object_name, object_type FROM user_objects
WHERE object_type IN ('PROCEDURE','FUNCTION','PACKAGE')
) LOOP
EXECUTE IMMEDIATE 'DROP ' || rec.object_type || ' ' || rec.object_name;
END LOOP;
END;
/
よくある質問
QDROP TABLE で外部キー制約エラーが出ます
A外部キー制約がある場合、参照されるテーブルを先に DROP しようとするとエラーになります。
DROP TABLE ... CASCADE CONSTRAINTS PURGE を使うか、先に外部キー制約を DROP してからテーブルを削除してください。本記事の PL/SQL スクリプトでは外部キーを先に DROP する方式を採用しています。Qシーケンスの値を 1 にリセットするには?
A最もシンプルなのは DROP + 再作成です。DROP せずにリセットする方法もあります(INCREMENT BY を負値に変更 → NEXTVAL → INCREMENT BY を元に戻す)。本記事で両方のパターンを紹介しています。
Q本番と開発の DB 名が同じ場合はどうしますか?
ADB 名が同じ場合は USER(スキーマ名)やホスト名で区別してください。
SYS_CONTEXT('USERENV', 'SERVER_HOST') でホスト名を取得し、開発サーバーでのみ実行を許可する安全チェックを追加します。QTRUNCATE と DELETE はどちらが速いですか?
ATRUNCATE の方が圧倒的に速いです。TRUNCATE は HWM をリセットして即座にデータを消去します。DELETE は 1 行ずつ削除するため遅く、UNDO ログも大量に消費します。開発環境の初期化には TRUNCATE を推奨します。
QFLASHBACK DATABASE は開発環境に向いていますか?
Aはい。リストアポイントに巻き戻すだけなので最速です。ただし ARCHIVELOG モード + FLASHBACK ON が前提です。また DB 全体が巻き戻るため、複数スキーマで共有している DB では他のスキーマにも影響します。開発専用 DB であれば最適な方法です。
QData Pump ベースと SQL ベースはどちらが管理しやすいですか?
Aテーブルが少なく DDL の変更が頻繁 → SQL ベース(DDL をバージョン管理しやすい)。テーブルが多くスキーマ全体をリセット → Data Pump ベース(1 コマンドで完結)。DDL の変更があった場合は Data Pump の初期状態ダンプも再作成する必要がある点に注意してください。
まとめ
開発環境の初期化方法をまとめます。
| やりたいこと | 方法 |
|---|---|
| 全テーブルを DROP + 再作成 | PL/SQL で user_tables / user_constraints をループ DELETE → @create_tables.sql |
| テーブル構造を維持してデータだけリセット | 全 TRUNCATE(外部キー無効化 → TRUNCATE → 再有効化) |
| シーケンスをリセット | DROP + CREATE SEQUENCE(または INCREMENT BY 調整) |
| スキーマ全体を初期状態に復元 | impdp … table_exists_action=REPLACE(初期状態ダンプを保管) |
| 秒単位で初期状態に巻き戻し | FLASHBACK DATABASE TO RESTORE POINT |
| 統合スクリプト化 | Shell スクリプトに安全チェック + Data Pump + 統計収集を統合 |
| 本番での誤実行を防止 | V$DATABASE.NAME / USER のチェックをスクリプト先頭に追加 |
テーブルの作成方法は「テーブルを作成する方法」、Data Pump の使い方は「Data Pump の使い方完全ガイド」、シーケンスは「シーケンスを作成する方法」も併せて参照してください。

