【Oracle】テーブル名をLIKE検索してDROP TABLE文を一括生成する方法|PL/SQL動的実行も解説

【Oracle】テーブル名をLIKE検索してDROP TABLE文を一括生成する方法|PL/SQL動的実行も解説 Oracle

Oracleデータベースで「テスト用に作ったテーブルをまとめて消したい」「バックアップテーブルが大量に残っているので一括削除したい」という場面は、開発や運用の現場で頻繁に発生します。しかし、テーブルを1つずつDROP TABLEするのは非効率で、対象が数十〜数百に及ぶと現実的ではありません。

この記事では、USER_TABLESやALL_TABLESをLIKE検索してテーブルを絞り込み、DROP TABLE文を動的に生成・実行する方法を解説します。SELECT文での生成からPL/SQLでの自動実行、安全に運用するためのパターンまで、実務で使えるテクニックを網羅します。

この記事で分かること

  • USER_TABLES / ALL_TABLES をLIKE検索でフィルタする方法
  • SELECT文でDROP TABLE文を動的生成する方法(|| 演算子)
  • LISTAGG / カーソルで一括生成するテクニック
  • PL/SQLのEXECUTE IMMEDIATEで動的に実行する方法
  • dry-run・ログ記録・バックアップなど安全な運用パターン
  • 実務でよく使うシナリオとよくあるミス・注意点
スポンサーリンク
  1. USER_TABLES / ALL_TABLESをLIKE検索でフィルタする
    1. USER_TABLESで自スキーマのテーブルを検索する
    2. ALL_TABLESで他スキーマも含めて検索する
    3. UPPER() / LOWER() で大文字小文字を吸収する
    4. 複数パターンで検索する(LIKE + OR / REGEXP_LIKE)
    5. 後方一致・部分一致の検索パターン
  2. SELECT文でDROP TABLE文を動的生成する
    1. 基本: || 演算子で文字列連結する
    2. CASCADE CONSTRAINTSオプションを付ける
    3. PURGEオプションでゴミ箱を回避する
    4. 他スキーマのテーブルを含むDROP文を生成する
  3. LISTAGG / カーソルで一括生成する
    1. LISTAGGで改行区切りの一括出力
    2. Oracle 12c R2以降: ON OVERFLOW対応
    3. カーソルFORループでSPOOL出力する
  4. PL/SQLで動的に実行する方法
    1. 基本: EXECUTE IMMEDIATEで即時実行
    2. 削除件数を表示しながら実行する
    3. 例外処理で安全に実行する
    4. 引数で検索パターンを渡すプロシージャ
  5. 安全な運用パターン
    1. dry-run(確認用SELECT)→ 実行の2ステップ
    2. バックアップ(CREATE TABLE AS SELECT)してからDROP
    3. 外部キー依存の確認(USER_CONSTRAINTS)
    4. ログテーブルへの記録
  6. 実務でよく使うシナリオ
    1. テスト用テーブルの一括削除(TMP_*, TEST_*, BK_*)
    2. 日付付きバックアップテーブルの古いもの削除
    3. 特定スキーマのテーブル全削除
  7. よくあるミスと注意点
    1. DROP TABLEは即COMMIT(ロールバック不可)
    2. リサイクルビン(BIN$…)の存在
    3. 権限不足(DROP ANY TABLE)
    4. 他セッションのロック
    5. LIKE検索のワイルドカード誤りに注意
  8. まとめ

USER_TABLES / ALL_TABLESをLIKE検索でフィルタする

DROP TABLE文を一括生成するには、まず対象テーブルを特定する必要があります。Oracleではデータディクショナリビューを使って、テーブル名をパターンで検索できます。

USER_TABLESで自スキーマのテーブルを検索する

USER_TABLESは、現在のユーザーが所有するテーブルの一覧を返すデータディクショナリビューです。WHERE句にLIKEを指定することで、テーブル名のパターンマッチングができます。

SQL
-- TMP_ で始まるテーブルを検索
SELECT table_name
FROM   user_tables
WHERE  table_name LIKE 'TMP_%'
ORDER BY table_name;
実行結果
TABLE_NAME
------------------------------
TMP_CUSTOMERS
TMP_ORDERS
TMP_PRODUCTS
TMP_SALES_202401
TMP_SALES_202402

ポイント:Oracleのデータディクショナリでは、テーブル名はデフォルトで大文字で格納されます。そのため、LIKE検索のパターンも大文字で指定するのが基本です。ダブルクォーテーションで作成された小文字テーブル名の場合は注意が必要です。

ALL_TABLESで他スキーマも含めて検索する

他のユーザーが所有するテーブルも含めて検索する場合は、ALL_TABLESを使います。OWNER列でスキーマを絞り込むことで、より正確な検索ができます。

SQL
-- 特定スキーマの TMP_ テーブルを検索
SELECT owner, table_name
FROM   all_tables
WHERE  owner      = 'HR'
AND    table_name LIKE 'TMP_%'
ORDER BY table_name;

UPPER() / LOWER() で大文字小文字を吸収する

ダブルクォーテーション付きで作成されたテーブルは、小文字や混在した名前で格納されることがあります。このような場合、UPPER()関数を使えば大文字小文字を気にせず検索できます。

SQL
-- 大文字・小文字を問わず検索
SELECT table_name
FROM   user_tables
WHERE  UPPER(table_name) LIKE 'TMP_%'
ORDER BY table_name;

複数パターンで検索する(LIKE + OR / REGEXP_LIKE)

複数のプレフィックスに一致するテーブルを検索する場合、LIKEORで繋げるか、REGEXP_LIKEで正規表現を使います。

SQL
-- 方法1: LIKE + OR で複数パターンを指定
SELECT table_name
FROM   user_tables
WHERE  table_name LIKE 'TMP_%'
OR     table_name LIKE 'TEST_%'
OR     table_name LIKE 'BK_%'
ORDER BY table_name;
SQL
-- 方法2: REGEXP_LIKE で正規表現を使用
SELECT table_name
FROM   user_tables
WHERE  REGEXP_LIKE(table_name, '^(TMP|TEST|BK)_')
ORDER BY table_name;
方法 書き方 適するケース
LIKE + OR パターンごとにLIKEを記述 パターンが2〜3個の場合
REGEXP_LIKE 正規表現で1行にまとめる パターンが多い場合・複雑な条件

注意:REGEXP_LIKEはOracle 10g以降で使用できます。それ以前のバージョンではLIKE + ORを使ってください。また、正規表現はLIKEに比べてパフォーマンスが劣る場合があるため、大量のテーブルが存在する環境では注意が必要です。

後方一致・部分一致の検索パターン

プレフィックスだけでなく、サフィックスや部分一致でも検索できます。

SQL
-- _BK で終わるテーブル(バックアップ用テーブル)
SELECT table_name
FROM   user_tables
WHERE  table_name LIKE '%_BK';

-- 日付文字列を含むテーブル(例: ORDERS_20240101)
SELECT table_name
FROM   user_tables
WHERE  REGEXP_LIKE(table_name, '_[0-9]{8}$');

-- テーブル名に LOG を含むテーブル
SELECT table_name
FROM   user_tables
WHERE  table_name LIKE '%LOG%';

SELECT文でDROP TABLE文を動的生成する

対象テーブルが特定できたら、次はそのテーブル名を使ってDROP TABLE文を自動的に組み立てます。Oracleの文字列連結演算子||を使えば、SELECT結果としてDROP文を生成できます。

基本: || 演算子で文字列連結する

最もシンプルなパターンです。'DROP TABLE 'という文字列とテーブル名を||で連結し、実行可能なSQL文を生成します。

SQL
SELECT 'DROP TABLE ' || table_name || ';' AS drop_stmt
FROM   user_tables
WHERE  table_name LIKE 'TMP_%'
ORDER BY table_name;
実行結果
DROP_STMT
------------------------------------------
DROP TABLE TMP_CUSTOMERS;
DROP TABLE TMP_ORDERS;
DROP TABLE TMP_PRODUCTS;
DROP TABLE TMP_SALES_202401;
DROP TABLE TMP_SALES_202402;

この結果をコピーしてSQL*Plusやツールに貼り付ければ、一括でDROPを実行できます。

CASCADE CONSTRAINTSオプションを付ける

テーブルに外部キー制約が設定されている場合、通常のDROP TABLEはエラーになります。CASCADE CONSTRAINTSを付けると、関連する外部キー制約を自動で削除してからテーブルをDROPできます。

SQL
SELECT 'DROP TABLE ' || table_name || ' CASCADE CONSTRAINTS;' AS drop_stmt
FROM   user_tables
WHERE  table_name LIKE 'TMP_%'
ORDER BY table_name;
実行結果
DROP_STMT
--------------------------------------------------
DROP TABLE TMP_CUSTOMERS CASCADE CONSTRAINTS;
DROP TABLE TMP_ORDERS CASCADE CONSTRAINTS;
DROP TABLE TMP_PRODUCTS CASCADE CONSTRAINTS;

PURGEオプションでゴミ箱を回避する

Oracle 10g以降では、DROP TABLEを実行するとテーブルは完全に削除されず、リサイクルビン(ゴミ箱)に移動します。リサイクルビンに移動されたテーブルはBIN$...という名前で残り続けます。

PURGEオプションを付けると、リサイクルビンを経由せず完全に削除されます。テスト用テーブルなど復元の必要がないテーブルには、PURGEを付けるのが一般的です。

SQL
-- CASCADE CONSTRAINTS + PURGE の組み合わせ
SELECT 'DROP TABLE ' || table_name
       || ' CASCADE CONSTRAINTS PURGE;' AS drop_stmt
FROM   user_tables
WHERE  table_name LIKE 'TMP_%'
ORDER BY table_name;
オプション 効果 使うべき場面
なし リサイクルビンに移動(復元可能) 念のため復元できるようにしたい場合
CASCADE CONSTRAINTS 参照している外部キー制約も削除 他テーブルから参照されている場合
PURGE リサイクルビンを経由せず完全削除 テスト用・一時テーブルなど復元不要な場合
CASCADE CONSTRAINTS PURGE 両方の効果 テスト環境の一括クリーンアップ

他スキーマのテーブルを含むDROP文を生成する

他のスキーマが所有するテーブルをDROPする場合は、テーブル名の前にスキーマ名(OWNER)を付ける必要があります。

SQL
-- スキーマ名付きのDROP TABLE文を生成
SELECT 'DROP TABLE ' || owner || '.' || table_name
       || ' CASCADE CONSTRAINTS PURGE;' AS drop_stmt
FROM   all_tables
WHERE  owner IN ('HR', 'SCOTT')
AND    table_name LIKE 'TMP_%'
ORDER BY owner, table_name;
実行結果
DROP_STMT
----------------------------------------------------
DROP TABLE HR.TMP_EMPLOYEES CASCADE CONSTRAINTS PURGE;
DROP TABLE HR.TMP_DEPARTMENTS CASCADE CONSTRAINTS PURGE;
DROP TABLE SCOTT.TMP_BONUS CASCADE CONSTRAINTS PURGE;

ポイント:他スキーマのテーブルをDROPするには、DROP ANY TABLEシステム権限が必要です。権限がない場合はORA-01031: insufficient privilegesエラーになります。

LISTAGG / カーソルで一括生成する

前のセクションでは1行ずつDROP文が生成されましたが、LISTAGG関数を使えば複数のDROP文を1つの文字列にまとめられます。また、カーソルFORループを使えばSPOOLファイルに出力して一括実行するスクリプトを作成できます。

LISTAGGで改行区切りの一括出力

LISTAGG関数は複数行の値を1つの文字列に連結します。区切り文字に改行(CHR(10))を指定すれば、実行可能なスクリプトを一括で生成できます。

SQL
SELECT LISTAGG(
         'DROP TABLE ' || table_name || ' CASCADE CONSTRAINTS PURGE;',
         CHR(10)
       ) WITHIN GROUP (ORDER BY table_name) AS drop_script
FROM   user_tables
WHERE  table_name LIKE 'TMP_%';
実行結果
DROP_SCRIPT
--------------------------------------------------
DROP TABLE TMP_CUSTOMERS CASCADE CONSTRAINTS PURGE;
DROP TABLE TMP_ORDERS CASCADE CONSTRAINTS PURGE;
DROP TABLE TMP_PRODUCTS CASCADE CONSTRAINTS PURGE;
DROP TABLE TMP_SALES_202401 CASCADE CONSTRAINTS PURGE;
DROP TABLE TMP_SALES_202402 CASCADE CONSTRAINTS PURGE;

注意:LISTAGGの結果は最大4000バイト(VARCHAR2の上限)です。対象テーブルが多い場合はORA-01489: result of string concatenation is too longエラーになります。Oracle 12c R2以降ではLISTAGG ... ON OVERFLOW TRUNCATEで切り捨てが可能ですが、全件を取得したい場合はカーソルを使う方が確実です。

Oracle 12c R2以降: ON OVERFLOW対応

Oracle 12c R2(12.2)以降では、LISTAGGにオーバーフロー制御を追加できます。

SQL
-- 12c R2以降: 4000バイトを超えたら切り捨て
SELECT LISTAGG(
         'DROP TABLE ' || table_name || ' PURGE;',
         CHR(10)
         ON OVERFLOW TRUNCATE '...'
       ) WITHIN GROUP (ORDER BY table_name) AS drop_script
FROM   user_tables
WHERE  table_name LIKE 'TMP_%';

カーソルFORループでSPOOL出力する

テーブル数が多い場合や、SQL*Plusからファイルに出力したい場合は、カーソルFORループDBMS_OUTPUTを組み合わせます。SQL*PlusのSPOOLコマンドで出力先をファイルに指定すれば、実行用スクリプトを自動生成できます。

SQL*Plus
-- SQL*PlusでSPOOL出力するスクリプト
SET SERVEROUTPUT ON
SET FEEDBACK OFF
SPOOL /tmp/drop_tmp_tables.sql

BEGIN
  FOR rec IN (
    SELECT table_name
    FROM   user_tables
    WHERE  table_name LIKE 'TMP_%'
    ORDER BY table_name
  ) LOOP
    DBMS_OUTPUT.PUT_LINE(
      'DROP TABLE ' || rec.table_name
      || ' CASCADE CONSTRAINTS PURGE;'
    );
  END LOOP;
END;
/

SPOOL OFF

このスクリプトを実行すると、/tmp/drop_tmp_tables.sqlに以下のようなファイルが生成されます。

生成されたファイル(drop_tmp_tables.sql)
DROP TABLE TMP_CUSTOMERS CASCADE CONSTRAINTS PURGE;
DROP TABLE TMP_ORDERS CASCADE CONSTRAINTS PURGE;
DROP TABLE TMP_PRODUCTS CASCADE CONSTRAINTS PURGE;
DROP TABLE TMP_SALES_202401 CASCADE CONSTRAINTS PURGE;
DROP TABLE TMP_SALES_202402 CASCADE CONSTRAINTS PURGE;

生成されたファイルは@/tmp/drop_tmp_tables.sqlで実行できます。

SPOOL方式のメリット:生成されたスクリプトを実行前に目視確認できるため、意図しないテーブルが含まれていないかチェックできます。本番環境では、この「生成→確認→実行」の2ステップ方式が推奨されます。

PL/SQLで動的に実行する方法

ここまでの方法はDROP文を「生成」するだけでしたが、PL/SQLのEXECUTE IMMEDIATEを使えば、生成と実行を1ステップで自動化できます。テスト環境のクリーンアップや定期的なメンテナンスタスクに便利です。

基本: EXECUTE IMMEDIATEで即時実行

EXECUTE IMMEDIATEは、文字列として渡されたSQL文を動的に実行するPL/SQLの構文です。カーソルFORループと組み合わせることで、検索結果のすべてのテーブルを順にDROPできます。

PL/SQL
BEGIN
  FOR rec IN (
    SELECT table_name
    FROM   user_tables
    WHERE  table_name LIKE 'TMP_%'
  ) LOOP
    EXECUTE IMMEDIATE
      'DROP TABLE ' || rec.table_name || ' CASCADE CONSTRAINTS PURGE';
  END LOOP;
END;
/

注意:EXECUTE IMMEDIATEに渡すSQL文の末尾にセミコロン(;)を付けてはいけません。セミコロンを付けるとORA-00911: invalid characterエラーになります。セミコロンはSQL*Plusの区切り文字であり、SQL文の一部ではありません。

削除件数を表示しながら実行する

どのテーブルがDROPされたかを確認するために、DBMS_OUTPUTで処理状況を出力するパターンです。

PL/SQL
SET SERVEROUTPUT ON

DECLARE
  v_count  NUMBER := 0;
  v_total  NUMBER := 0;
BEGIN
  -- 対象件数を事前カウント
  SELECT COUNT(*) INTO v_total
  FROM   user_tables
  WHERE  table_name LIKE 'TMP_%';

  DBMS_OUTPUT.PUT_LINE('対象テーブル数: ' || v_total);
  DBMS_OUTPUT.PUT_LINE('----------------------------');

  FOR rec IN (
    SELECT table_name
    FROM   user_tables
    WHERE  table_name LIKE 'TMP_%'
    ORDER BY table_name
  ) LOOP
    EXECUTE IMMEDIATE
      'DROP TABLE ' || rec.table_name || ' CASCADE CONSTRAINTS PURGE';
    v_count := v_count + 1;
    DBMS_OUTPUT.PUT_LINE(
      v_count || '/' || v_total || ' DROP完了: ' || rec.table_name
    );
  END LOOP;

  DBMS_OUTPUT.PUT_LINE('----------------------------');
  DBMS_OUTPUT.PUT_LINE('完了: ' || v_count || 'テーブルをDROPしました');
END;
/
実行結果
対象テーブル数: 5
----------------------------
1/5 DROP完了: TMP_CUSTOMERS
2/5 DROP完了: TMP_ORDERS
3/5 DROP完了: TMP_PRODUCTS
4/5 DROP完了: TMP_SALES_202401
5/5 DROP完了: TMP_SALES_202402
----------------------------
完了: 5テーブルをDROPしました

例外処理で安全に実行する

複数テーブルを一括でDROPする際、途中でエラーが発生すると残りのテーブルが処理されません。EXCEPTIONブロックでエラーをキャッチして処理を続行するパターンが実務では重要です。

PL/SQL
SET SERVEROUTPUT ON

DECLARE
  v_success  NUMBER := 0;
  v_fail     NUMBER := 0;
BEGIN
  FOR rec IN (
    SELECT table_name
    FROM   user_tables
    WHERE  table_name LIKE 'TMP_%'
    ORDER BY table_name
  ) LOOP
    BEGIN
      EXECUTE IMMEDIATE
        'DROP TABLE ' || rec.table_name || ' CASCADE CONSTRAINTS PURGE';
      v_success := v_success + 1;
      DBMS_OUTPUT.PUT_LINE('[OK]  ' || rec.table_name);
    EXCEPTION
      WHEN OTHERS THEN
        v_fail := v_fail + 1;
        DBMS_OUTPUT.PUT_LINE(
          '[NG]  ' || rec.table_name
          || ' - ' || SQLERRM
        );
    END;
  END LOOP;

  DBMS_OUTPUT.PUT_LINE('==============================');
  DBMS_OUTPUT.PUT_LINE('成功: ' || v_success || ' / 失敗: ' || v_fail);
END;
/
実行結果(一部のテーブルがロックされていた場合)
[OK]  TMP_CUSTOMERS
[OK]  TMP_ORDERS
[NG]  TMP_PRODUCTS - ORA-00054: resource busy and acquire with NOWAIT specified or timeout expired
[OK]  TMP_SALES_202401
[OK]  TMP_SALES_202402
==============================
成功: 4 / 失敗: 1

ポイント:内側のBEGIN...EXCEPTION...ENDブロックをループの中に入れることで、1つのテーブルでエラーが起きても残りのテーブルの処理は継続されます。本番環境での一括削除では必ずこのパターンを使いましょう。

引数で検索パターンを渡すプロシージャ

汎用的に使えるよう、検索パターンを引数で受け取るストアドプロシージャにまとめることもできます。

PL/SQL
CREATE OR REPLACE PROCEDURE drop_tables_like (
  p_pattern  IN  VARCHAR2,
  p_purge    IN  BOOLEAN DEFAULT TRUE,
  p_dry_run  IN  BOOLEAN DEFAULT FALSE
) AS
  v_sql      VARCHAR2(500);
  v_success  NUMBER := 0;
  v_fail     NUMBER := 0;
BEGIN
  FOR rec IN (
    SELECT table_name
    FROM   user_tables
    WHERE  table_name LIKE p_pattern
    ORDER BY table_name
  ) LOOP
    v_sql := 'DROP TABLE ' || rec.table_name
             || ' CASCADE CONSTRAINTS';
    IF p_purge THEN
      v_sql := v_sql || ' PURGE';
    END IF;

    IF p_dry_run THEN
      DBMS_OUTPUT.PUT_LINE('[DRY-RUN] ' || v_sql);
    ELSE
      BEGIN
        EXECUTE IMMEDIATE v_sql;
        v_success := v_success + 1;
        DBMS_OUTPUT.PUT_LINE('[OK]  ' || rec.table_name);
      EXCEPTION
        WHEN OTHERS THEN
          v_fail := v_fail + 1;
          DBMS_OUTPUT.PUT_LINE('[NG]  ' || rec.table_name || ' - ' || SQLERRM);
      END;
    END IF;
  END LOOP;

  IF NOT p_dry_run THEN
    DBMS_OUTPUT.PUT_LINE('成功: ' || v_success || ' / 失敗: ' || v_fail);
  END IF;
END;
/

このプロシージャの使い方は以下のとおりです。

PL/SQL
-- dry-run: 削除対象を確認するだけ(実行しない)
EXEC drop_tables_like('TMP_%', p_dry_run => TRUE);

-- 本実行: テーブルをDROP
EXEC drop_tables_like('TMP_%');

-- PURGEなし(リサイクルビンに残す)
EXEC drop_tables_like('TEST_%', p_purge => FALSE);

安全な運用パターン

DROP TABLEはDDL文であるため、実行した瞬間に暗黙的COMMITが発生し、ロールバックできません。本番環境で一括削除を行う際は、以下の安全策を講じることが重要です。

dry-run(確認用SELECT)→ 実行の2ステップ

最も基本的な安全策は、まず対象テーブルの一覧を確認し、問題なければ実行するという2ステップ方式です。

SQL
-- ステップ1: 対象テーブルを確認(dry-run)
SELECT table_name,
       num_rows,
       last_analyzed
FROM   user_tables
WHERE  table_name LIKE 'TMP_%'
ORDER BY table_name;
実行結果
TABLE_NAME            NUM_ROWS  LAST_ANALYZED
--------------------  --------  -------------------
TMP_CUSTOMERS              150  2024-01-15 10:30:00
TMP_ORDERS                 500  2024-01-15 10:30:00
TMP_PRODUCTS                80  2024-01-15 10:30:00
TMP_SALES_202401         12000  2024-02-01 09:00:00
TMP_SALES_202402          8500  2024-03-01 09:00:00

NUM_ROWSLAST_ANALYZEDを確認することで、「まだ使われているテーブルではないか」「想定外の大きなテーブルが含まれていないか」をチェックできます。問題なければステップ2に進みます。

バックアップ(CREATE TABLE AS SELECT)してからDROP

万が一に備えて、DROPする前にテーブルのバックアップコピーを作成するパターンです。CREATE TABLE AS SELECT(CTAS)でデータごとコピーしてからDROPすることで、誤って削除した場合でもデータを復元できます。

PL/SQL
SET SERVEROUTPUT ON

DECLARE
  v_backup_name  VARCHAR2(128);
  v_date_suffix  VARCHAR2(8) := TO_CHAR(SYSDATE, 'YYYYMMDD');
BEGIN
  FOR rec IN (
    SELECT table_name
    FROM   user_tables
    WHERE  table_name LIKE 'TMP_%'
    ORDER BY table_name
  ) LOOP
    BEGIN
      -- バックアップテーブル名を生成
      v_backup_name := 'BK_' || rec.table_name || '_' || v_date_suffix;

      -- CTAS でバックアップを作成
      EXECUTE IMMEDIATE
        'CREATE TABLE ' || v_backup_name
        || ' AS SELECT * FROM ' || rec.table_name;
      DBMS_OUTPUT.PUT_LINE('[BK]  ' || v_backup_name || ' 作成完了');

      -- 元テーブルをDROP
      EXECUTE IMMEDIATE
        'DROP TABLE ' || rec.table_name || ' CASCADE CONSTRAINTS PURGE';
      DBMS_OUTPUT.PUT_LINE('[DROP] ' || rec.table_name || ' 削除完了');
    EXCEPTION
      WHEN OTHERS THEN
        DBMS_OUTPUT.PUT_LINE('[NG]  ' || rec.table_name || ' - ' || SQLERRM);
    END;
  END LOOP;
END;
/

外部キー依存の確認(USER_CONSTRAINTS)

DROPしようとしているテーブルが、他のテーブルから外部キーで参照されている場合があります。事前に依存関係を確認しておくことで、意図しない影響を防げます。

SQL
-- DROP対象テーブルを参照している外部キーを確認
SELECT c.table_name       AS referencing_table,
       c.constraint_name  AS fk_name,
       r.table_name       AS referenced_table
FROM   user_constraints c
JOIN   user_constraints r
  ON   c.r_constraint_name = r.constraint_name
WHERE  c.constraint_type = 'R'
AND    r.table_name LIKE 'TMP_%'
ORDER BY r.table_name, c.table_name;
実行結果
REFERENCING_TABLE     FK_NAME              REFERENCED_TABLE
--------------------  -------------------  --------------------
TMP_ORDER_DETAILS     FK_ORD_DTL_ORDERS    TMP_ORDERS
TMP_ORDER_DETAILS     FK_ORD_DTL_PRODUCTS  TMP_PRODUCTS

この結果から、TMP_ORDERSTMP_PRODUCTSTMP_ORDER_DETAILSから参照されていることが分かります。CASCADE CONSTRAINTSを付けてDROPするか、先にTMP_ORDER_DETAILSをDROPする必要があります。

ログテーブルへの記録

いつ・誰が・どのテーブルを削除したかを記録するログテーブルを用意しておくと、問題が発生した際の調査に役立ちます。

SQL
-- ログテーブルの作成
CREATE TABLE drop_table_log (
  log_id        NUMBER GENERATED ALWAYS AS IDENTITY,
  table_name    VARCHAR2(128),
  dropped_by    VARCHAR2(128),
  dropped_at    TIMESTAMP DEFAULT SYSTIMESTAMP,
  status        VARCHAR2(10),
  error_message VARCHAR2(4000)
);
PL/SQL
-- ログ付きDROPプロシージャ
CREATE OR REPLACE PROCEDURE drop_tables_with_log (
  p_pattern IN VARCHAR2
) AS
BEGIN
  FOR rec IN (
    SELECT table_name
    FROM   user_tables
    WHERE  table_name LIKE p_pattern
    AND    table_name != 'DROP_TABLE_LOG'
    ORDER BY table_name
  ) LOOP
    BEGIN
      EXECUTE IMMEDIATE
        'DROP TABLE ' || rec.table_name || ' CASCADE CONSTRAINTS PURGE';

      -- 成功ログを記録
      INSERT INTO drop_table_log (table_name, dropped_by, status)
      VALUES (rec.table_name, USER, 'SUCCESS');
    EXCEPTION
      WHEN OTHERS THEN
        -- 失敗ログを記録
        INSERT INTO drop_table_log (table_name, dropped_by, status, error_message)
        VALUES (rec.table_name, USER, 'FAIL', SQLERRM);
    END;
  END LOOP;
  COMMIT;
END;
/

ポイント:DROP TABLEはDDLなので暗黙的COMMITが発生し、INSERTしたログもCOMMITされます。ただし、DDLの前に未COMMITのDMLがある場合は、DDLの暗黙COMMITで一緒にCOMMITされるため、ログテーブルへのINSERT自体はDROP前に実行しないよう注意してください。上記のプロシージャではDROP成功後にINSERTしているため、問題ありません。

実務でよく使うシナリオ

ここでは、実際の開発・運用現場でLIKE検索によるDROP TABLE一括削除が活躍するシナリオを紹介します。そのまま使えるSQLテンプレートも掲載しています。

テスト用テーブルの一括削除(TMP_*, TEST_*, BK_*)

開発中に作成したテスト用テーブルが溜まっていくのは、よくある問題です。プレフィックスごとにまとめて削除する例を紹介します。

PL/SQL
-- テスト用プレフィックスのテーブルを一括削除
DECLARE
  TYPE t_patterns IS TABLE OF VARCHAR2(30);
  v_patterns t_patterns := t_patterns('TMP_%', 'TEST_%', 'BK_%', 'WK_%');
  v_count    NUMBER := 0;
BEGIN
  FOR i IN 1 .. v_patterns.COUNT LOOP
    FOR rec IN (
      SELECT table_name
      FROM   user_tables
      WHERE  table_name LIKE v_patterns(i)
    ) LOOP
      BEGIN
        EXECUTE IMMEDIATE
          'DROP TABLE ' || rec.table_name || ' CASCADE CONSTRAINTS PURGE';
        v_count := v_count + 1;
        DBMS_OUTPUT.PUT_LINE('[OK] ' || rec.table_name);
      EXCEPTION
        WHEN OTHERS THEN
          DBMS_OUTPUT.PUT_LINE('[NG] ' || rec.table_name || ' - ' || SQLERRM);
      END;
    END LOOP;
  END LOOP;
  DBMS_OUTPUT.PUT_LINE('合計 ' || v_count || ' テーブルを削除しました');
END;
/

日付付きバックアップテーブルの古いもの削除

バックアップを取る際にテーブル名_YYYYMMDDという命名規則を使っている場合、古い日付のテーブルだけを削除するパターンです。

PL/SQL
-- 30日以上前のバックアップテーブルを削除
DECLARE
  v_cutoff  VARCHAR2(8) := TO_CHAR(SYSDATE - 30, 'YYYYMMDD');
  v_suffix  VARCHAR2(8);
  v_count   NUMBER := 0;
BEGIN
  DBMS_OUTPUT.PUT_LINE('基準日: ' || v_cutoff || ' より古いテーブルを削除');

  FOR rec IN (
    SELECT table_name
    FROM   user_tables
    WHERE  REGEXP_LIKE(table_name, '_[0-9]{8}$')
    ORDER BY table_name
  ) LOOP
    -- テーブル名末尾の8桁を抽出
    v_suffix := SUBSTR(rec.table_name, LENGTH(rec.table_name) - 7);

    IF v_suffix < v_cutoff THEN
      BEGIN
        EXECUTE IMMEDIATE
          'DROP TABLE ' || rec.table_name || ' PURGE';
        v_count := v_count + 1;
        DBMS_OUTPUT.PUT_LINE('[OK] ' || rec.table_name);
      EXCEPTION
        WHEN OTHERS THEN
          DBMS_OUTPUT.PUT_LINE('[NG] ' || rec.table_name || ' - ' || SQLERRM);
      END;
    END IF;
  END LOOP;
  DBMS_OUTPUT.PUT_LINE('合計 ' || v_count || ' テーブルを削除しました');
END;
/

特定スキーマのテーブル全削除

テスト用スキーマを丸ごとクリーンアップする場合は、LIKE条件なしで全テーブルを対象にします。非常に危険な操作なので、必ずdry-runで確認してから実行してください。

PL/SQL
-- テストスキーマの全テーブルを削除(要注意)
DECLARE
  v_count NUMBER := 0;
BEGIN
  -- 安全確認: 現在のスキーマがテスト用であることを確認
  IF USER NOT IN ('TEST_SCHEMA', 'DEV_SCHEMA') THEN
    RAISE_APPLICATION_ERROR(-20001,
      'このプロシージャは TEST_SCHEMA / DEV_SCHEMA でのみ実行できます。現在: ' || USER);
  END IF;

  FOR rec IN (
    SELECT table_name
    FROM   user_tables
    ORDER BY table_name
  ) LOOP
    BEGIN
      EXECUTE IMMEDIATE
        'DROP TABLE ' || rec.table_name || ' CASCADE CONSTRAINTS PURGE';
      v_count := v_count + 1;
    EXCEPTION
      WHEN OTHERS THEN
        DBMS_OUTPUT.PUT_LINE('[NG] ' || rec.table_name || ' - ' || SQLERRM);
    END;
  END LOOP;
  DBMS_OUTPUT.PUT_LINE('合計 ' || v_count || ' テーブルを削除しました');
END;
/

注意:全テーブル削除は取り消しができません。上記のコードではUSER関数でスキーマ名をチェックし、テスト用スキーマ以外では実行できないように安全ガードを入れています。本番スキーマで誤って実行されるのを防ぐために、このようなチェックは必ず入れてください。

よくあるミスと注意点

LIKE検索でのDROP TABLE一括実行は非常に強力ですが、それだけにミスの影響も大きくなります。ここでは、実務で特に注意すべきポイントをまとめます。

DROP TABLEは即COMMIT(ロールバック不可)

DROP TABLEはDDL(データ定義言語)であり、実行した瞬間に暗黙的COMMITが発生します。DML(INSERT/UPDATE/DELETE)のようにROLLBACKで元に戻すことはできません。

SQL
-- これは効果がない!
DROP TABLE important_data PURGE;
ROLLBACK;  -- DROPは既にCOMMIT済み。ROLLBACKしても戻らない

重要:さらに注意すべき点として、DDL実行前にCOMMITされていないDML(INSERT/UPDATE/DELETE)がある場合、DDLの暗黙COMMITによってそのDMLも一緒にCOMMITされます。DROP TABLE実行前に、未COMMITのトランザクションがないか確認してください。

リサイクルビン(BIN$…)の存在

PURGEを付けずにDROP TABLEを実行すると、テーブルはリサイクルビンに移動し、BIN$...という名前で残ります。LIKE検索の対象から意図せず外れる(または含まれる)ことがあります。

SQL
-- リサイクルビンの内容を確認
SELECT object_name,
       original_name,
       type,
       droptime
FROM   user_recyclebin
ORDER BY droptime DESC;

-- リサイクルビンから復元する場合
FLASHBACK TABLE important_table TO BEFORE DROP;

-- リサイクルビンを空にする場合
PURGE RECYCLEBIN;

ポイント:USER_TABLESにはBIN$で始まるリサイクルビンのテーブルも表示される場合があります。LIKE検索時にAND table_name NOT LIKE 'BIN$%'を追加すると、リサイクルビンのテーブルを除外できます。

権限不足(DROP ANY TABLE)

他のスキーマのテーブルをDROPするにはDROP ANY TABLE権限が必要です。権限がない状態で実行すると、以下のエラーが発生します。

エラー例
ORA-01031: insufficient privileges
操作 必要な権限
自分のテーブルをDROP テーブルの所有者であれば権限不要
他スキーマのテーブルをDROP DROP ANY TABLEシステム権限
権限の確認 SELECT * FROM user_sys_privs WHERE privilege LIKE '%DROP%'

他セッションのロック

他のセッションがテーブルを使用中(SELECT中やDML実行中)の場合、DROP TABLE排他ロックを取得できず失敗します。

エラー例
ORA-00054: resource busy and acquire with NOWAIT specified or timeout expired

ロック状況を確認するには、以下のSQLを使います。

SQL
-- テーブルをロックしているセッションを確認
SELECT s.sid,
       s.serial#,
       s.username,
       o.object_name,
       l.locked_mode
FROM   v$locked_object l
JOIN   dba_objects o
  ON   l.object_id = o.object_id
JOIN   v$session s
  ON   l.session_id = s.sid
WHERE  o.object_name LIKE 'TMP_%';

LIKE検索のワイルドカード誤りに注意

LIKE検索のワイルドカード(%_)の指定ミスは、意図しないテーブルを削除してしまう最大の原因です。

パターン 意味 危険度
'TMP_%' TMP_ で始まるテーブル 低(意図通り)
'TMP%' TMP で始まる全テーブル(TMPFILE等も含む)
'%TMP%' TMP を含む全テーブル 非常に高
'%' 全テーブル 致命的

必ず守るべきルール:PL/SQLで動的にDROPを実行する前に、必ず同じ条件のSELECT文で対象テーブルを確認してください。'TMP_%''TMP%'は結果が大きく異なる可能性があります。

まとめ

Oracleでテーブル名をLIKE検索してDROP TABLE文を一括生成する方法について解説しました。最後に、パターン別の早見表を掲載します。

やりたいこと 方法 安全性
DROP文の一覧を確認したい SELECT + || 連結 最も安全(実行しない)
スクリプトファイルを生成したい SPOOL + カーソルFORループ 安全(目視確認可能)
1つの文字列にまとめたい LISTAGG + CHR(10) 安全(4000バイト制限あり)
自動で即時実行したい EXECUTE IMMEDIATE 要注意(例外処理必須)
安全に本番で実行したい dry-run → バックアップ → 実行 推奨パターン
汎用的に使い回したい ストアドプロシージャ化 安全(dry-runモード付き)

実務では、以下の手順を守ることで安全に一括削除を実行できます。

  1. SELECT文で対象を確認(dry-run): LIKE条件に一致するテーブルが意図通りかチェック
  2. 依存関係を確認: USER_CONSTRAINTSで外部キー参照がないか確認
  3. バックアップを取得: CREATE TABLE AS SELECTでデータをコピー
  4. 例外処理付きで実行: BEGIN…EXCEPTION…ENDでエラーをキャッチ
  5. 結果をログに記録: 成功/失敗をログテーブルに保存

DROP TABLEはロールバックできないDDLです。「生成→確認→実行」の2ステップを徹底し、本番環境では必ずバックアップを取ってから実行してください。