Oracle: Jeśli istnieje tabela


343

Piszę kilka skryptów migracyjnych dla bazy danych Oracle i miałem nadzieję, że Oracle ma coś podobnego do IF EXISTSkonstrukcji MySQL .

W szczególności, ilekroć chcę upuścić tabelę w MySQL, robię coś takiego

DROP TABLE IF EXISTS `table_name`;

W ten sposób, jeśli tabela nie istnieje, DROPnie powoduje błędu, a skrypt może być kontynuowany.

Czy Oracle ma podobny mechanizm? Zdaję sobie sprawę, że mogę użyć następującego zapytania, aby sprawdzić, czy tabela istnieje, czy nie

SELECT * FROM dba_tables where table_name = 'table_name';

ale składnia do wiązania tego razem ze znakiem DROPucieka mi.

Odpowiedzi:


586

Najlepszym i najbardziej wydajnym sposobem jest wychwycenie wyjątku „nie znaleziono tabeli”: pozwala to uniknąć narzutu sprawdzania, czy tabela istnieje dwukrotnie; i nie ma problemu z tym, że jeśli DROP zawiedzie z innego powodu (może to być ważne) wyjątek jest nadal zgłaszany do osoby dzwoniącej:

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

DODATEK W celach informacyjnych znajdują się równoważne bloki dla innych typów obiektów:

Sekwencja

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

Widok

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

Wyzwalacz

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

Indeks

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

Kolumna

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;

Link do bazy danych

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

Widok zmaterializowany

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

Rodzaj

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

Przymus

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;

Zadanie harmonogramu

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

Użytkownik / schemat

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;

Pakiet

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

Procedura

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

Funkcjonować

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

Przestrzeń tabel

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

Synonim

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

13
A porzucenie USER, SQLCODE do zignorowania to -1918.
— Andrew Swan,

14
Trzeba napisać procedurę, czy to zrobić? Czy nie ma lepszego sposobu na zrobienie tego?
— Wilson Freitas,

8
Jeśli dodam wiele EXECUTE IMMEDIATE 'DROP TABLE mytable';zdań (po jednym dla każdej tabeli w skrypcie), czy muszę wstawić jeden moduł obsługi wyjątków dla każdego z nich, czy wystarczy, aby wszystkie sentecy były zawinięte w jednym BEGIN ... EXCEPTION ... END;bloku?
— Throoze

8
@ jpmc26: Odpowiednikiem MS SQL jest IF OBJECT_ID('TblName') IS NOT NULL DROP TABLE TblName. Wydaje się, że gadatliwość języka SQL jest proporcjonalna do ceny.

6
@JeffreyKemp Nie pomyślałbyś, ale raz po raz odkryłem, że Oracle utrudnia wszystko. Kiedy spędzasz średnio godzinę na niejasnym błędzie składni lub próbujesz dowiedzieć się, jak zrobić coś, co jest oczywiste i łatwe w innej bazie danych (np. Warunkowo upuścić element) i tego rodzaju problemy pojawiają się codziennie, sumuje się. Szybki.
— 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;

Służy to do sprawdzenia, czy istnieje tabela w bieżącym schemacie. Aby sprawdzić, czy dana tabela już istnieje w innym schemacie, musisz użyć all_tableszamiast user_tablesi dodać warunekall_tables.owner = upper('schema_name')


34
+1 To jest lepsze, ponieważ nie polegaj na dekodowaniu wyjątków, aby zrozumieć, co robić. Kod będzie łatwiejszy do zarządzania i zrozumienia
— daitangio,

4
Zgadzam się z @daitangio - wydajność ogólnie nie ma większego wpływu na łatwość konserwacji dzięki skryptom wdrażania uruchamianym po uruchomieniu.
— pettys

1
Byłbym zainteresowany, aby zrozumieć, czy dorozumiane zatwierdzenie odgrywa tutaj rolę. Chcesz, aby SELECT i DROP były w tej samej transakcji. [Oczywiście ignoruje każdy kolejny DDL, który może zostać wykonany. ]
— Mathew

2
@Mathew, DROP jest poleceniem DDL, więc najpierw wyda COMMIT, upuści tabelę, a następnie wyda 2. COMMIT. Oczywiście w tym przykładzie nie ma transakcji (ponieważ wydano tylko zapytanie), więc nie ma znaczenia; ale jeśli użytkownik wcześniej wydał trochę DML, zostanie ono domyślnie zatwierdzone przed wykonaniem jakiegokolwiek DDL.
— Jeffrey Kemp,

28

Szukałem tego samego, ale ostatecznie napisałem procedurę, która mi pomoże:

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;

Mam nadzieję że to pomoże


Po utworzeniu powyżej proc. delobject, próbowałem to nazwać wydając następujący SQL. Ale to nie zadziałało. delobject („MyTable”, „TABLE”); Otrzymuję następujący błąd -------------------------------- Błąd rozpoczynania od wiersza 1 polecenia: delobject ('MyTable ”,„ TABELA ”) Raport o błędzie: Nieznane polecenie
— Shai

1
użyj polecenia EXECUTE - EXECUTE DelObject („MyTable”, „TABLE”);
— idanuda

13

chciałem tylko napisać pełny kod, który utworzy tabelę i upuści ją, jeśli już istnieje przy użyciu kodu Jeffreya (uznanie dla niego, nie dla mnie!).

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
Osobiście umieściłem CREATE TABLE w osobnym kroku, ponieważ nie trzeba tego robić dynamicznie i nie trzeba obsługiwać wyjątków.
— Jeffrey Kemp,

11

Za pomocą SQL * PLUS można także użyć polecenia WHENEVER SQLERROR:

WHENEVER SQLERROR CONTINUE NONE
DROP TABLE TABLE_NAME;

WHENEVER SQLERROR EXIT SQL.SQLCODE
DROP TABLE TABLE_NAME;

Z CONTINUE NONEbłędem jest zgłaszany, ale skrypt będzie kontynuowany. Ze EXIT SQL.SQLCODEskryptu zostanie rozwiązana w przypadku wystąpienia błędu.

zobacz także: KIEDYKOLWIEK Dokumenty SQLERROR


3

W wyroczni nie ma „STOŁU UPADKU, JEŚLI ISTNIEJE”, należy wykonać instrukcję select.

spróbuj tego (nie używam składni Oracle, więc jeśli moje zmienne są ify, proszę wybacz mi):

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

Podjąłem próbę przetłumaczenia skryptu na składnię Oracle.
— Tom

3
zadeklaruj liczbę zliczeń; zacznij wybrać count (*) na count z all_tables gdzie table_name = 'x'; jeśli liczba> 0, wykonaj natychmiastową „tablicę upuszczania x”; koniec jeśli; koniec; Nie można uruchomić DDL bezpośrednio z bloku transakcji, należy użyć polecenia execute.
— Khb

Dziękuję bardzo! Nie zdawałem sobie sprawy, że składnia jest tak inna. Wiem, że musisz wszystko zawinąć na początku / na końcu, ale pomyślałem, że jest uruchamiany w środku innego skryptu. Tom: Postanowiłem opuścić moją wersję i nie kopiować twojej, więc nie biorę od ciebie głosów, które oczywiście mają właściwą odpowiedź.
— Erich,

Nie sądzę, że to się skompiluje. Może być również ważne, aby dołączyć tutaj właściciela schematu lub możesz uzyskać wartość „prawda” dla tabeli, której nie chciałeś uzyskać pod tą samą nazwą.
— Allen

Twoja odpowiedź została zastąpiona prawidłową składnią Oracle 10 minut po opublikowaniu.
— jpmc26,

3

Wolę następujące rozwiązanie ekonomiczne

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

Inną metodą jest zdefiniowanie wyjątku, a następnie tylko złapanie tego wyjątku, umożliwiając wszystkim innym propagację.

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 „już nie istnieje” vs. „istniało, ale już nie” :)
— Jeffrey Kemp

2

Jednym ze sposobów jest użycie DBMS_ASSERT.SQL_OBJECT_NAME :

Ta funkcja sprawdza, czy łańcuch parametru wejściowego jest kwalifikowanym identyfikatorem SQL istniejącego obiektu 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 Demo


2
Ale to może nie być nazwa stołu.
— Jeffrey Kemp

Mogą istnieć również różne tabele używające tej nazwy w różnych schematach.
— Hybris95

0

Niestety nie, nie ma czegoś takiego jak upuszczenie, jeśli istnieje, lub UTWÓRZ, JEŚLI NIE ISTNIEJE

Możesz napisać skrypt plsql, aby uwzględnić tam logikę.

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

Nie przepadam za składnią Oracle, ale myślę, że skrypt @ Ericha byłby mniej więcej taki.

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
Czy to się nawet kompiluje?
— quillbreaker

0

Zawsze możesz sam złapać błąd.

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

Nadużywanie tego, podobnie jak puste catch () w innych językach, jest uważane za złą praktykę.

Pozdrawiam
K.


1
Nie, nigdy „wyjątek, gdy inni są wtedy
— nieważni

0

Wolę określić tabelę i właściciela schematu.

Uważaj również na rozróżnianie wielkości liter. (patrz „górna” klauzula poniżej).

Wrzuciłem kilka różnych przedmiotów, aby pokazać, że można ich używać w innych miejscach niż TABELE.

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

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;  

I przykład TABELI:

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;

// Wykonując ten kod, sprawdza, czy tabela istnieje, a później tworzy tabelę max. to po prostu działa w jednej kompilacji


2
Wierzę, że to tworzy tabelę tylko wtedy, gdy zostanie zgłoszony błąd.
— Fish Biscuit

0

A jeśli chcesz, aby był on ponownie dostępny i minimalizował cykle upuszczania / tworzenia, możesz buforować DDL za pomocą dbms_metadata.get_ddl i odtworzyć wszystko za pomocą takiej konstrukcji: 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; To tylko próbka, wewnątrz powinna być pętla z Typ DDL, nazwa i właściciel są zmiennymi.


0

Taki blok może ci się przydać.

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;  
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.