Oracle: Wenn Tabelle vorhanden ist

343

Ich schreibe einige Migrationsskripte für eine Oracle-Datenbank und hoffte, dass Oracle etwas Ähnliches wie MySQL hat IF EXISTS Konstrukt hat.

Insbesondere wenn ich eine Tabelle in MySQL löschen möchte, mache ich so etwas wie

DROP TABLE IF EXISTS `table_name`;

Wenn die Tabelle nicht vorhanden ist, wird auf DROPdiese Weise kein Fehler ausgegeben, und das Skript kann fortgesetzt werden.

Hat Oracle einen ähnlichen Mechanismus? Mir ist klar, dass ich mit der folgenden Abfrage prüfen kann, ob eine Tabelle vorhanden ist oder nicht

SELECT * FROM dba_tables where table_name = 'table_name';

aber die Syntax, um das mit a zu verbinden, DROPentgeht mir.

Alan Storm
quelle

Antworten:

585

Der beste und effizienteste Weg besteht darin, die Ausnahme "Tabelle nicht gefunden" abzufangen: Dadurch wird der Aufwand für die Überprüfung vermieden, ob die Tabelle zweimal vorhanden ist. und leidet nicht unter dem Problem, dass die Ausnahme immer noch für den Anrufer ausgelöst wird, wenn der DROP aus einem anderen Grund ausfällt (der möglicherweise wichtig ist):

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

ADDENDUM Als Referenz sind hier die entsprechenden Blöcke für andere Objekttypen:

Reihenfolge

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

Aussicht

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

Auslösen

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

Index

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

Säule

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;

Datenbanklink

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

Materialisierte Ansicht

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

Art

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

Zwang

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;

Scheduler-Job

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

Benutzer / Schema

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;

Paket

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

Verfahren

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

Funktion

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

Tablespace

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

Synonym

BEGIN
  EXECUTE IMMEDIATE 'DROP SYNONYM ' || synonym_name;
EXCEPTION
  WHEN OTHERS THEN
    IF SQLCODE != -1434 THEN
      RAISE;
    END IF;
END;
Jeffrey Kemp
quelle
13
Und um einen USER fallen zu lassen, muss der zu ignorierende SQLCODE -1918 sein.
Andrew Swan
14
Man muss eine Prozedur schreiben, um das zu tun? Gibt es nicht einen besseren Weg, das zu tun?
Wilson Freitas
8
Wenn ich viele EXECUTE IMMEDIATE 'DROP TABLE mytable';Sätze hinzufüge (einen für jede Tabelle im Skript), muss ich für jeden einen Ausnahmebehandler einfügen, oder reicht es aus, alle Senteces in einen BEGIN ... EXCEPTION ... END;Block zu packen ?
Throoze
8
@ jpmc26: Das Äquivalent für MS SQL ist IF OBJECT_ID('TblName') IS NOT NULL DROP TABLE TblName. Es scheint, dass die Ausführlichkeit einer SQL-Sprache proportional zum Preis ist.
6
@ JeffreyKemp Das würde man nicht glauben, aber ich habe immer wieder festgestellt, dass Oracle alles schwer macht. Wenn Sie durchschnittlich eine Stunde pro obskurem Syntaxfehler verbringen oder versuchen, herauszufinden, wie Sie etwas tun, das in einer anderen Datenbank offensichtlich und einfach ist (z. B. ein Element bedingt löschen), und diese Art von Problemen täglich auftreten, summiert sich dies. Schnell.
jpmc26
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;

Hiermit wird überprüft, ob eine Tabelle im aktuellen Schema vorhanden ist. Um zu überprüfen, ob eine bestimmte Tabelle bereits in einem anderen Schema vorhanden ist, müssen Sie all_tablesstattdessen user_tablesdie Bedingung verwenden und hinzufügenall_tables.owner = upper('schema_name')

Marius Burz
quelle
34
+1 Dies ist besser, da Sie die Ausnahmedecodierung nicht weiterleiten, um zu verstehen, was zu tun ist. Code wird leichter zu pflegen und zu verstehen sein
daitangio
4
Stimmen Sie mit @daitangio überein - die Leistung übertrifft die Wartbarkeit bei einmaligen Bereitstellungsskripten im Allgemeinen nicht.
Pettys
1
Es würde mich interessieren zu verstehen, ob implizites Commit hier eine Rolle spielt. Sie möchten, dass sich SELECT und DROP in derselben Transaktion befinden. [Ignoriert offensichtlich jede nachfolgende DDL, die ausgeführt werden kann. ]
Mathew
2
@Matthew, DROP ist ein DDL-Befehl, daher wird zuerst ein COMMIT ausgegeben, die Tabelle gelöscht und dann ein 2. COMMIT ausgegeben. In diesem Beispiel gibt es natürlich keine Transaktion (da nur eine Abfrage ausgegeben wird), sodass dies keinen Unterschied macht. Wenn der Benutzer jedoch zuvor eine DML ausgegeben hat, wird diese implizit festgeschrieben, bevor eine DDL ausgeführt wird.
Jeffrey Kemp
28

Ich habe nach dem gleichen gesucht, aber am Ende habe ich ein Verfahren geschrieben, das mir hilft:

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;

Hoffe das hilft

Robert Vabo
quelle
Nachdem ich oben proc erstellt habe. delobject, ich habe versucht, es mit der folgenden SQL zu nennen. Aber es hat nicht funktioniert. Delobjekt ('MyTable', 'TABLE'); Ich erhalte die folgende Fehlermeldung -------------------------------- Fehler ab Zeile 1 im Befehl: delobject ('MyTable ',' TABLE ') Fehlerbericht: Unbekannter Befehl
Shai
1
Verwenden Sie den Befehl EXECUTE - EXECUTE DelObject ('MyTable', 'TABLE').
Idanuda
13

Ich wollte nur einen vollständigen Code veröffentlichen, der eine Tabelle erstellt und löscht, wenn er bereits mit Jeffreys Code existiert (ein großes Lob an ihn, nicht an mich!).

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;
Mischkin
quelle
2
Persönlich würde ich die CREATE TABLE in einen separaten Schritt setzen, da dies nicht dynamisch erfolgen muss und keinen Ausnahmebehandler benötigt.
Jeffrey Kemp
11

Mit SQL * PLUS können Sie auch den Befehl WHENEVER SQLERROR verwenden:

WHENEVER SQLERROR CONTINUE NONE
DROP TABLE TABLE_NAME;

WHENEVER SQLERROR EXIT SQL.SQLCODE
DROP TABLE TABLE_NAME;

Wenn CONTINUE NONEein Fehler gemeldet wird, wird das Skript fortgesetzt. Mit EXIT SQL.SQLCODEdem Skript wird im Fehlerfall beendet.

Siehe auch: WENN SQLERROR-Dokumente

trunkc
quelle
3

In Oracle gibt es keine 'DROP TABLE IF EXISTS'. Sie müssten die select-Anweisung ausführen.

Versuchen Sie dies (ich bin nicht mit der Orakelsyntax vertraut. Wenn meine Variablen also ify sind, verzeihen Sie mir bitte):

declare @count int
select @count=count(*) from all_tables where table_name='Table_name';
if @count>0
BEGIN
    DROP TABLE tableName;
END
Erich
quelle
Ich habe versucht, das Skript in die Oracle-Syntax zu übersetzen.
Tom
3
Zählnummer angeben; Beginnen Sie mit der Auswahl von count (*) in count aus all_tables, wobei table_name = 'x'; Wenn count> 0 ist, führen Sie sofort 'drop table x' aus. ende wenn; Ende; Sie können DDL nicht direkt von einem Transaktionsblock aus ausführen, sondern müssen execute ausführen.
Khb
Vielen Dank! Ich hatte nicht bemerkt, dass die Syntax so unterschiedlich war. Ich wusste, dass Sie das Ganze in einen Anfang / Ende einwickeln müssen, aber ich dachte, es würde mitten in einem anderen Skript ausgeführt. Tom: Ich habe beschlossen, meine Version zu verlassen und deine nicht zu kopieren, also nehme ich keine Stimmen von dir, der offensichtlich die richtige Antwort hat.
Erich
Ich denke nicht, dass dies kompiliert wird. Es kann auch wichtig sein, den Schemabesitzer hier anzugeben, oder Sie erhalten "true" für eine Tabelle, die Sie nicht mit demselben Namen erhalten wollten.
Allen
Ihre Antwort wurde 10 Minuten nach der Veröffentlichung durch die korrekte Oracle-Syntax ersetzt.
jpmc26
3

Ich folge lieber einer wirtschaftlichen Lösung

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;
Pavel S.
quelle
2

Eine andere Methode besteht darin, eine Ausnahme zu definieren und diese Ausnahme dann nur abzufangen, damit sich alle anderen verbreiten können.

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;
Leigh Riffel
quelle
@ Sk8erPeter "existiert bereits nicht" vs. "existierte, existiert aber nicht mehr" :)
Jeffrey Kemp
2

Eine Möglichkeit ist die Verwendung von DBMS_ASSERT.SQL_OBJECT_NAME :

Diese Funktion überprüft, ob die Eingabeparameterzeichenfolge eine qualifizierte SQL-Kennung eines vorhandenen SQL-Objekts ist.

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 Demo

Lukasz Szozda
quelle
2
Aber es könnte nicht der Name einer Tabelle sein.
Jeffrey Kemp
Es kann auch verschiedene Tabellen geben, die diesen Namen in verschiedenen Schemata verwenden.
Hybris95
0

Leider nein, es gibt keinen Tropfen, falls vorhanden, oder ERSTELLEN, WENN NICHT EXISTIEREN

Sie könnten ein plsql-Skript schreiben, um die Logik dort einzuschließen.

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

Ich mag Oracle Syntax nicht besonders, aber ich denke, @ Erichs Skript wäre so etwas.

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;
Tom
quelle
8
Kompiliert das überhaupt?
Federbrecher
0

Sie können den Fehler jederzeit selbst abfangen.

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

Es wird als schlechte Praxis angesehen, dies zu überbeanspruchen, ähnlich wie bei leeren catch () in anderen Sprachen.

Grüße
K.

Khb
quelle
1
Nein, niemals "Ausnahme, wenn andere dann null sind"
miracle173
0

Ich bevorzuge es, die Tabelle und den Schemabesitzer anzugeben.

Achten Sie auch auf die Groß- und Kleinschreibung. (siehe "obere" Klausel unten).

Ich habe ein paar verschiedene Objekte hineingeworfen, um zu zeigen, dass es neben TABELLEN auch an anderen Orten verwendet werden kann.

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

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;  

Und ein Tabellenbeispiel:

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;
/   
granadaCoder
quelle
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;

// Überprüft mit diesem Code, ob die Tabelle vorhanden ist, und erstellt später die Tabelle max. Dies funktioniert einfach in einer einzigen Kompilierung

Mahesh Pandeya
quelle
2
Ich glaube, dies erstellt die Tabelle nur, wenn der Fehler ausgelöst wird.
Fischkeks
0

Und wenn Sie es wieder eingeben möchten und Drop / Create-Zyklen minimieren möchten, können Sie die DDL mit dbms_metadata.get_ddl zwischenspeichern und alles mit einem Konstrukt wie dem folgenden neu erstellen: 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; Dies ist nur ein Beispiel, es sollte eine Schleife mit enthalten DDL-Typ, Name und Eigentümer sind Variablen.

Andrei Nossov
quelle
0

Ein Block wie dieser könnte für Sie nützlich sein.

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;  

quelle