Oracle:テーブルが存在する場合


343

私はOracleデータベース用の移行スクリプトをいくつか書いていて、OracleがMySQLのIF EXISTS構成に似ていることを望んでいました。

具体的には、MySQLにテーブルをドロップするときはいつでも、次のようにします

DROP TABLE IF EXISTS `table_name`;

この方法では、テーブルが存在しない場合でも、DROPエラーは発生せず、スクリプトを続行できます。

Oracleにも同様のメカニズムがありますか?次のクエリを使用して、テーブルが存在するかどうかを確認できることに気付きました

SELECT * FROM dba_tables where table_name = 'table_name';

しかし、それをa DROPと結び付ける構文は、私から逃れています。

回答:


585

最善かつ最も効率的な方法は、「テーブルが見つかりません」という例外をキャッチすることです。これにより、テーブルが2度存在するかどうかを確認するオーバーヘッドが回避されます。また、DROPが他の何らかの理由(それが重要である可能性がある)で失敗した場合でも、例外が呼び出し元に発生するという問題に悩まされることはありません。

BEGIN
   EXECUTE IMMEDIATE 'DROP TABLE ' || table_name;
EXCEPTION
   WHEN OTHERS THEN
      IF SQLCODE != -942 THEN
         RAISE;
      END IF;
END;

補足 参考までに、他のオブジェクトタイプの同等のブロックを以下に示します。

シーケンス

BEGIN
  EXECUTE IMMEDIATE 'DROP SEQUENCE ' || sequence_name;
EXCEPTION
  WHEN OTHERS THEN
    IF SQLCODE != -2289 THEN
      RAISE;
    END IF;
END;

見る

BEGIN
  EXECUTE IMMEDIATE 'DROP VIEW ' || view_name;
EXCEPTION
  WHEN OTHERS THEN
    IF SQLCODE != -942 THEN
      RAISE;
    END IF;
END;

引き金

BEGIN
  EXECUTE IMMEDIATE 'DROP TRIGGER ' || trigger_name;
EXCEPTION
  WHEN OTHERS THEN
    IF SQLCODE != -4080 THEN
      RAISE;
    END IF;
END;

インデックス

BEGIN
  EXECUTE IMMEDIATE 'DROP INDEX ' || index_name;
EXCEPTION
  WHEN OTHERS THEN
    IF SQLCODE != -1418 THEN
      RAISE;
    END IF;
END;

カラム

BEGIN
  EXECUTE IMMEDIATE 'ALTER TABLE ' || table_name
                || ' DROP COLUMN ' || column_name;
EXCEPTION
  WHEN OTHERS THEN
    IF SQLCODE != -904 AND SQLCODE != -942 THEN
      RAISE;
    END IF;
END;

データベースリンク

BEGIN
  EXECUTE IMMEDIATE 'DROP DATABASE LINK ' || dblink_name;
EXCEPTION
  WHEN OTHERS THEN
    IF SQLCODE != -2024 THEN
      RAISE;
    END IF;
END;

マテリアライズドビュー

BEGIN
  EXECUTE IMMEDIATE 'DROP MATERIALIZED VIEW ' || mview_name;
EXCEPTION
  WHEN OTHERS THEN
    IF SQLCODE != -12003 THEN
      RAISE;
    END IF;
END;

タイプ

BEGIN
  EXECUTE IMMEDIATE 'DROP TYPE ' || type_name;
EXCEPTION
  WHEN OTHERS THEN
    IF SQLCODE != -4043 THEN
      RAISE;
    END IF;
END;

拘束

BEGIN
  EXECUTE IMMEDIATE 'ALTER TABLE ' || table_name
            || ' DROP CONSTRAINT ' || constraint_name;
EXCEPTION
  WHEN OTHERS THEN
    IF SQLCODE != -2443 AND SQLCODE != -942 THEN
      RAISE;
    END IF;
END;

スケジューラジョブ

BEGIN
  DBMS_SCHEDULER.drop_job(job_name);
EXCEPTION
  WHEN OTHERS THEN
    IF SQLCODE != -27475 THEN
      RAISE;
    END IF;
END;

ユーザー/スキーマ

BEGIN
  EXECUTE IMMEDIATE 'DROP USER ' || user_name;
  /* you may or may not want to add CASCADE */
EXCEPTION
  WHEN OTHERS THEN
    IF SQLCODE != -1918 THEN
      RAISE;
    END IF;
END;

パッケージ

BEGIN
  EXECUTE IMMEDIATE 'DROP PACKAGE ' || package_name;
EXCEPTION
  WHEN OTHERS THEN
    IF SQLCODE != -4043 THEN
      RAISE;
    END IF;
END;

手順

BEGIN
  EXECUTE IMMEDIATE 'DROP PROCEDURE ' || procedure_name;
EXCEPTION
  WHEN OTHERS THEN
    IF SQLCODE != -4043 THEN
      RAISE;
    END IF;
END;

関数

BEGIN
  EXECUTE IMMEDIATE 'DROP FUNCTION ' || function_name;
EXCEPTION
  WHEN OTHERS THEN
    IF SQLCODE != -4043 THEN
      RAISE;
    END IF;
END;

テーブルスペース

BEGIN
  EXECUTE IMMEDIATE 'DROP TABLESPACE' || tablespace_name;
EXCEPTION
  WHEN OTHERS THEN
    IF SQLCODE != -959 THEN
      RAISE;
    END IF;
END;

シノニム

BEGIN
  EXECUTE IMMEDIATE 'DROP SYNONYM ' || synonym_name;
EXCEPTION
  WHEN OTHERS THEN
    IF SQLCODE != -1434 THEN
      RAISE;
    END IF;
END;

13
また、USERをドロップする場合、無視するSQLCODEは-1918です。
Andrew Swan

14
プロシージャを作成する必要がありますか?それを行うためのより良い方法はありませんか?
Wilson Freitas 2012

8
多くのEXECUTE IMMEDIATE 'DROP TABLE mytable';文(スクリプト内のテーブルごとに1つ)を追加する場合、それぞれに1つの例外ハンドラーを配置する必要がありBEGIN ... EXCEPTION ... END;ますか、それともすべての監視を1つのブロックにラップするのに十分ですか?
Throoze、2013

8
@ jpmc26:MS SQLに相当するものはIF OBJECT_ID('TblName') IS NOT NULL DROP TABLE TblNameです。SQL言語の冗長性は価格に比例するようです。

6
@JeffreyKempあなたはそうは思わないでしょうが、私はOracleがすべてを困難にすることを何度も繰り返し見つけました。あいまいな構文エラーごとに平均1時間を費やしたり、別のデータベースで明白で簡単な方法(条件付きで要素を削除するなど)を実行したり、これらの種類の問題が毎日ポップアップしたりする方法を見つけようとすると、その問題は毎日増えていきます。速い。
jpmc26 2014

135
declare
   c int;
begin
   select count(*) into c from user_tables where table_name = upper('table_name');
   if c = 1 then
      execute immediate 'drop table table_name';
   end if;
end;

これは、現在のスキーマにテーブルが存在するかどうかを確認するためのものです。特定のテーブルが別のスキーマにすでに存在するかどうかを確認するall_tablesにはuser_tables、代わりにを使用して条件を追加する必要がありますall_tables.owner = upper('schema_name')


34
+1何をすべきかを理解するために例外デコードを中継しないので、これはより良いです。コードの管理と理解が容易になります
daitangio

4
@daitangioに同意する-パフォーマンスは通常、1回限りのデプロイメントスクリプトの保守性よりも優先されません。
pettys 2013年

1
ここで暗黙のコミットが役割を果たすかどうかを知りたいと思います。SELECTとDROPを同じトランザクション内に置く必要があります。[実行される可能性がある後続のDDLを明らかに無視します。]
Mathew、2014

2
@マシュー、DROPはDDLコマンドであるため、最初にCOMMITを発行し、テーブルを削除してから、2番目のCOMMITを発行します。もちろん、この例ではトランザクションがないため(クエリが発行されるだけなので)、違いはありません。ただし、ユーザーが以前にDMLを発行したことがある場合は、DDLが実行される前に暗黙的にコミットされます。
ジェフリーケンプ2015

28

私は同じものを探していましたが、私を助けるための手順を書いてしまいました:

CREATE OR REPLACE PROCEDURE DelObject(ObjName varchar2,ObjType varchar2)
IS
 v_counter number := 0;   
begin    
  if ObjType = 'TABLE' then
    select count(*) into v_counter from user_tables where table_name = upper(ObjName);
    if v_counter > 0 then          
      execute immediate 'drop table ' || ObjName || ' cascade constraints';        
    end if;   
  end if;
  if ObjType = 'PROCEDURE' then
    select count(*) into v_counter from User_Objects where object_type = 'PROCEDURE' and OBJECT_NAME = upper(ObjName);
      if v_counter > 0 then          
        execute immediate 'DROP PROCEDURE ' || ObjName;        
      end if; 
  end if;
  if ObjType = 'FUNCTION' then
    select count(*) into v_counter from User_Objects where object_type = 'FUNCTION' and OBJECT_NAME = upper(ObjName);
      if v_counter > 0 then          
        execute immediate 'DROP FUNCTION ' || ObjName;        
      end if; 
  end if;
  if ObjType = 'TRIGGER' then
    select count(*) into v_counter from User_Triggers where TRIGGER_NAME = upper(ObjName);
      if v_counter > 0 then          
        execute immediate 'DROP TRIGGER ' || ObjName;
      end if; 
  end if;
  if ObjType = 'VIEW' then
    select count(*) into v_counter from User_Views where VIEW_NAME = upper(ObjName);
      if v_counter > 0 then          
        execute immediate 'DROP VIEW ' || ObjName;        
      end if; 
  end if;
  if ObjType = 'SEQUENCE' then
    select count(*) into v_counter from user_sequences where sequence_name = upper(ObjName);
      if v_counter > 0 then          
        execute immediate 'DROP SEQUENCE ' || ObjName;        
      end if; 
  end if;
end;

お役に立てれば


上記のプロシージャを作成した後。delobject、次のSQLを発行してそれを呼び出そうとしました。しかし、それはうまくいきませんでした。delobject( 'MyTable'、 'TABLE'); 次のエラーが表示される--------------------------------コマンドの1行目からエラーが発生しました:delobject( 'MyTable '、' TABLE ')エラーレポート:不明なコマンド
Shai

1
EXECUTEコマンドを使用する-EXECUTE DelObject( 'MyTable'、 'TABLE');
idanuda 2014

13

テーブルを作成し、Jeffreyのコードを使用して既に存在する場合はそれを削除する完全なコードを投稿したかっただけです(私ではなく、彼にKudosを!)。

BEGIN
    BEGIN
         EXECUTE IMMEDIATE 'DROP TABLE tablename';
    EXCEPTION
         WHEN OTHERS THEN
                IF SQLCODE != -942 THEN
                     RAISE;
                END IF;
    END;

    EXECUTE IMMEDIATE 'CREATE TABLE tablename AS SELECT * FROM sourcetable WHERE 1=0';

END;

2
個人的には、動的に実行する必要がなく、例外ハンドラーも必要ないため、CREATE TABLEを別のステップに配置します。
ジェフリーケンプ

11

SQL * PLUSでは、WHENEVER SQLERRORコマンドも使用できます。

WHENEVER SQLERROR CONTINUE NONE
DROP TABLE TABLE_NAME;

WHENEVER SQLERROR EXIT SQL.SQLCODE
DROP TABLE TABLE_NAME;

CONTINUE NONEエラー報告されていますが、スクリプトは続行されます。ではEXIT SQL.SQLCODE、スクリプトエラーが発生した場合に終了します。

参照:WHENEVER SQLERRORドキュメント


3

Oracleには「DROP TABLE IF EXISTS」はありません。selectステートメントを実行する必要があります。

これを試してください(私はOracle構文ではありませんので、私の変数が正しければ、許してください):

declare @count int
select @count=count(*) from all_tables where table_name='Table_name';
if @count>0
BEGIN
    DROP TABLE tableName;
END

スクリプトをオラクルの構文に変換しようとしました。
トム

3
カウント数を宣言します。all_tablesからcountにselect count(*)を開始します。ここでtable_name = 'x'; count> 0の場合、すぐに 'drop table x'を実行します。終了する場合; 終わり; トランザクションブロックから直接DDLを実行することはできません。実行を使用する必要があります。
Khb 2009年

どうもありがとう!構文がそれほど違うことに気づかなかった。すべてを開始/終了でラップする必要があることはわかっていましたが、別のスクリプトの途中で実行されていると思いました。トム:私は自分のバージョンを残して、あなたのバージョンをコピーしないことにしました。そのため、正しい答えを持っているあなたからの投票はしません。
エーリヒ

これがコンパイルされるとは思いません。ここにスキーマの所有者を含めることも重要です。そうしないと、同じ名前で取得するつもりがなかったテーブルで「true」が取得される場合があります。
アレン

あなたの答え、投稿されてから10分後に正しいOracle構文に置き換えられました。
jpmc26 2017年

3

私は経済的な解決策を好む

BEGIN
    FOR i IN (SELECT NULL FROM USER_OBJECTS WHERE OBJECT_TYPE = 'TABLE' AND OBJECT_NAME = 'TABLE_NAME') LOOP
            EXECUTE IMMEDIATE 'DROP TABLE TABLE_NAME';
    END LOOP;
END;

2

別の方法は、例外を定義してから、その例外のみをキャッチして、他のすべてを伝播させることです。

Declare
   eTableDoesNotExist Exception;
   PRAGMA EXCEPTION_INIT(eTableDoesNotExist, -942);
Begin
   EXECUTE IMMEDIATE ('DROP TABLE myschema.mytable');
Exception
   When eTableDoesNotExist Then
      DBMS_Output.Put_Line('Table already does not exist.');
End;

@ Sk8erPeter 「すでに存在しません」「存在しましたが、もう存在しません」 :)
Jeffrey Kemp

2

1つの方法は、DBMS_ASSERT.SQL_OBJECT_NAMEを使用することです

この関数は、入力パラメーター文字列が既存のSQLオブジェクトの修飾SQL識別子であることを確認します。

DECLARE
    V_OBJECT_NAME VARCHAR2(30);
BEGIN
   BEGIN
        V_OBJECT_NAME  := DBMS_ASSERT.SQL_OBJECT_NAME('tab1');
        EXECUTE IMMEDIATE 'DROP TABLE tab1';

        EXCEPTION WHEN OTHERS THEN NULL;
   END;
END;
/

DBFiddleデモ


2
しかし、それはテーブルの名前ではないかもしれません。
ジェフリーケンプ

異なるスキーマでその名前を使用するさまざまなテーブルが存在する場合もあります。
Hybris95

0

悲しいことに、存在しない場合はドロップする、存在しない場合は作成するなどのことはありません

そこにロジックを含めるためにplsqlスクリプトを書くことができます。

http://download.oracle.com/docs/cd/B12037_01/server.101/b10759/statements_9003.htm

私はOracle構文にはあまり詳しくありませんが、@ Erichのスクリプトは次のようになると思います。

declare 
cant integer
begin
select into cant count(*) from dba_tables where table_name='Table_name';
if count>0 then
BEGIN
    DROP TABLE tableName;
END IF;
END;

8
これでもコンパイルできますか?
キルブレーカー2010

0

エラーはいつでも自分でキャッチできます。

begin
execute immediate 'drop table mytable';
exception when others then null;
end;

他の言語の空のcatch()と同様に、これを使いすぎることは悪い習慣と考えられています。

よろしく
K


1
いいえ、「他の人がnullの場合を除いて例外はありません」
miracle173

0

テーブルとスキーマの所有者を指定することを好みます。

大文字と小文字の区別にも注意してください。(以下の「上部」節を参照)。

TABLE以外の場所で使用できることを示すために、いくつかの異なるオブジェクトを投入しました。

.............

declare
   v_counter int;
begin
 select count(*) into v_counter from dba_users where upper(username)=upper('UserSchema01');
   if v_counter > 0 then
      execute immediate 'DROP USER UserSchema01 CASCADE';
   end if; 
end;
/



CREATE USER UserSchema01 IDENTIFIED BY pa$$word
  DEFAULT TABLESPACE users
  TEMPORARY TABLESPACE temp
  QUOTA UNLIMITED ON users;

grant create session to UserSchema01;  

そしてTABLEの例:

declare
   v_counter int;
begin
 select count(*) into v_counter from all_tables where upper(TABLE_NAME)=upper('ORDERS') and upper(OWNER)=upper('UserSchema01');
   if v_counter > 0 then
      execute immediate 'DROP TABLE UserSchema01.ORDERS';
   end if; 
end;
/   

0
BEGIN
   EXECUTE IMMEDIATE 'DROP TABLE "IMS"."MAX" ';
EXCEPTION
   WHEN OTHERS THEN
      IF SQLCODE != -942 THEN
         RAISE;
          END IF;
         EXECUTE IMMEDIATE ' 
  CREATE TABLE "IMS"."MAX" 
   (    "ID" NUMBER NOT NULL ENABLE, 
    "NAME" VARCHAR2(20 BYTE), 
     CONSTRAINT "MAX_PK" PRIMARY KEY ("ID")
  USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255 
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
  TABLESPACE "SYSAUX"  ENABLE
   ) SEGMENT CREATION IMMEDIATE 
  PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 NOCOMPRESS LOGGING
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
  TABLESPACE "SYSAUX"  ';


END;

//このコードを実行して、テーブルが存在するかどうかを確認し、後でテーブルmaxを作成します。これは単に単一のコンパイルで機能します


2
エラーがスローされたときにのみテーブルが作成されると思います。
フィッシュビスケット

0

そして、それを再入力可能にし、ドロップ/作成サイクルを最小限にしたい場合は、dbms_metadata.get_ddlを使用してDDLをキャッシュし、次のような構成を使用してすべてを再作成できます。 declare v_ddl varchar2(4000); begin select dbms_metadata.get_ddl('TABLE','DEPT','SCOTT') into v_ddl from dual; [COMPARE CACHED DDL AND EXECUTE IF NO MATCH] exception when others then if sqlcode = -31603 then [GET AND EXECUTE CACHED DDL] else raise; end if; end; これは単なるサンプルであり、内部にループがあるはずですDDLタイプ、名前、および所有者は変数です。


0

このようなブロックはあなたに役立つかもしれません。

DECLARE
    table_exist INT;

BEGIN
    SELECT Count(*)
    INTO   table_exist
    FROM   dba_tables
    WHERE  owner = 'SCHEMA_NAME' 
    AND table_name = 'EMPLOYEE_TABLE';

    IF table_exist = 1 THEN
      EXECUTE IMMEDIATE 'drop table EMPLOYEE_TABLE';
    END IF;
END;  
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.