Oracle: Jika Table Ada

343

Saya sedang menulis beberapa skrip migrasi untuk database Oracle, dan berharap Oracle memiliki sesuatu yang mirip dengan IF EXISTSkonstruk MySQL .

Secara khusus, setiap kali saya ingin menjatuhkan tabel di MySQL, saya melakukan sesuatu seperti

DROP TABLE IF EXISTS `table_name`;

Dengan cara ini, jika tabel tidak ada, maka DROPtidak menghasilkan kesalahan, dan skrip dapat melanjutkan.

Apakah Oracle memiliki mekanisme serupa? Saya menyadari bahwa saya dapat menggunakan kueri berikut untuk memeriksa apakah ada tabel atau tidak

SELECT * FROM dba_tables where table_name = 'table_name';

tetapi sintaks untuk mengikat itu bersama-sama dengan DROPmelarikan diri saya.

Alan Storm
sumber

Jawaban:

585

Cara terbaik dan paling efisien adalah dengan menangkap pengecualian "tabel tidak ditemukan": ini menghindari overhead memeriksa apakah tabel ada dua kali; dan tidak mengalami masalah bahwa jika DROP gagal karena alasan lain (yang mungkin penting) pengecualian masih dimunculkan ke penelepon:

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

ADDENDUM Untuk referensi, berikut ini adalah blok yang setara untuk jenis objek lainnya:

Urutan

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

Melihat

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

Pelatuk

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;

Kolom

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;

Tautan Basis Data

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

Tampilan Terwujud

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

Tipe

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

Paksaan

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;

Pekerjaan Penjadwal

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

Skema Pengguna /

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;

Prosedur

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

Fungsi

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;

Persamaan Kata

BEGIN
  EXECUTE IMMEDIATE 'DROP SYNONYM ' || synonym_name;
EXCEPTION
  WHEN OTHERS THEN
    IF SQLCODE != -1434 THEN
      RAISE;
    END IF;
END;
Jeffrey Kemp
sumber
13
Dan untuk menjatuhkan PENGGUNA, mengabaikan SQLCODE adalah -1918.
Andrew Swan
14
Orang perlu menulis prosedur melakukan itu? Apakah tidak ada cara yang lebih baik untuk melakukan itu?
Wilson Freitas
8
Jika saya menambahkan banyak EXECUTE IMMEDIATE 'DROP TABLE mytable';kalimat (satu untuk setiap tabel dalam skrip), apakah saya harus meletakkan satu handler pengecualian untuk masing-masing, atau apakah cukup untuk membungkus semua senteces dalam satu BEGIN ... EXCEPTION ... END;blok?
Throoze
8
@ jpmc26: Setara untuk MS SQL adalah IF OBJECT_ID('TblName') IS NOT NULL DROP TABLE TblName. Tampaknya verbositas dari bahasa SQL sebanding dengan harganya.
6
@ JeffreyKemp Anda tidak akan berpikir begitu, tetapi saya telah menemukan waktu dan waktu lagi bahwa Oracle membuat semuanya sulit. Ketika Anda menghabiskan rata-rata satu jam per kesalahan sintaksis yang tidak jelas atau mencoba mencari cara untuk melakukan sesuatu yang jelas dan mudah dalam database lain (seperti menjatuhkan elemen secara kondisional) dan masalah-masalah seperti itu muncul setiap hari, itu bertambah. Cepat.
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;

Itu untuk memeriksa apakah ada tabel dalam skema saat ini. Untuk memeriksa apakah tabel yang diberikan sudah ada dalam skema yang berbeda, Anda harus menggunakan all_tablesalih-alih user_tablesdan menambahkan kondisinyaall_tables.owner = upper('schema_name')

Marius Burz
sumber
34
+1 Ini lebih baik karena jangan menyampaikan decoding pengecualian untuk memahami apa yang harus dilakukan. Kode akan lebih mudah untuk dipertahankan dan dipahami
daitangio
4
Setuju dengan @daitangio - kinerja umumnya tidak melampaui rawatan dengan skrip penerapan run-once.
pettys
1
Saya akan tertarik untuk memahami jika commit implisit berperan di sini. Anda ingin SELECT dan DROP berada di dalam transaksi yang sama. [Jelas mengabaikan DDL selanjutnya yang mungkin dieksekusi. ]
Mathew
2
@Matthew, DROP adalah perintah DDL, jadi pertama-tama akan mengeluarkan COMMIT, letakkan tabelnya, lalu berikan COMMIT ke-2. Tentu saja, dalam contoh ini tidak ada transaksi (karena hanya mengeluarkan kueri) sehingga tidak ada bedanya; tetapi jika pengguna sebelumnya mengeluarkan beberapa DML, itu akan secara implisit dilakukan sebelum DDL dijalankan.
Jeffrey Kemp
28

Saya telah mencari yang sama tetapi saya akhirnya menulis prosedur untuk membantu saya:

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;

Semoga ini membantu

Robert Vabo
sumber
Setelah saya buat proc di atas. delobject, saya mencoba menyebutnya mengeluarkan SQL berikut. Tetapi itu tidak berhasil. delobject ('MyTable', 'TABLE'); Saya mendapatkan kesalahan berikut -------------------------------- Kesalahan dimulai pada baris 1 dalam perintah: delobject ('MyTable ',' TABLE ') Laporan kesalahan: Perintah Tidak Dikenal
Shai
1
gunakan Perintah EXECUTE - EXECUTE DelObject ('MyTable', 'TABLE');
idanuda
13

hanya ingin memposting kode lengkap yang akan membuat tabel dan menjatuhkannya jika sudah ada menggunakan kode Jeffrey (pujian kepadanya, bukan aku!).

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;
mishkin
sumber
2
Secara pribadi, saya meletakkan CREATE TABLE dalam langkah terpisah, karena tidak perlu dilakukan secara dinamis dan tidak perlu handler pengecualian.
Jeffrey Kemp
11

Dengan SQL * PLUS, Anda juga dapat menggunakan perintah WHENEVER SQLERROR:

WHENEVER SQLERROR CONTINUE NONE
DROP TABLE TABLE_NAME;

WHENEVER SQLERROR EXIT SQL.SQLCODE
DROP TABLE TABLE_NAME;

Dengan CONTINUE NONEkesalahan dilaporkan, tetapi skrip akan terus berlanjut. Dengan EXIT SQL.SQLCODEscript akan diakhiri jika terjadi kesalahan.

lihat juga: SETIAP SAJA SQLERROR Docs

trunkc
sumber
3

Tidak ada 'DROP TABLE IF EXISTS' di oracle, Anda harus melakukan pernyataan pilih.

coba ini (saya tidak menggunakan sintaks oracle, jadi jika variabel saya ify, tolong maafkan saya):

declare @count int
select @count=count(*) from all_tables where table_name='Table_name';
if @count>0
BEGIN
    DROP TABLE tableName;
END
Erich
sumber
Saya telah berupaya menerjemahkan naskah ke sintaksis oracle.
Tom
3
mendeklarasikan nomor hitungan; mulai pilih count (*) ke count dari all_tables di mana table_name = 'x'; jika hitung> 0 maka jalankan langsung 'drop table x'; berakhir jika; akhir; Anda tidak dapat menjalankan DDL langsung dari blok transaksi, Anda harus menggunakan eksekusi.
Khb
Terima kasih banyak! Saya tidak menyadari bahwa sintaksnya berbeda. Saya tahu Anda perlu membungkus semuanya di awal / akhir, tetapi saya pikir itu sedang dijalankan di tengah-tengah skrip lain. Tom: Saya memutuskan untuk meninggalkan versi saya dan tidak menyalin versi Anda, jadi saya tidak mengambil suara dari Anda, yang jelas memiliki jawaban yang tepat.
Erich
Saya tidak berpikir ini akan dikompilasi. Mungkin juga penting untuk menyertakan pemilik skema di sini atau Anda mungkin mendapatkan 'true' untuk tabel yang tidak Anda maksudkan dengan nama yang sama.
Allen
Jawaban Anda digantikan oleh sintaks Oracle yang benar 10 menit setelah ini diposting.
jpmc26
3

Saya lebih suka mengikuti solusi ekonomi

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
sumber
2

Metode lain adalah mendefinisikan pengecualian dan kemudian hanya menangkap pengecualian itu membiarkan semua orang lain menyebar.

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
sumber
@ Sk8erPeter "sudah tidak ada" vs. "memang ada, tetapi tidak ada lagi" :)
Jeffrey Kemp
2

Salah satu caranya adalah dengan menggunakan DBMS_ASSERT.SQL_OBJECT_NAME :

Fungsi ini memverifikasi bahwa string parameter input adalah pengidentifikasi SQL yang memenuhi syarat dari objek SQL yang ada.

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

Demo DBFiddle

Lukasz Szozda
sumber
2
Tapi itu mungkin bukan nama tabel.
Jeffrey Kemp
Mungkin juga ada berbagai tabel menggunakan nama itu di skema yang berbeda.
Hybris95
0

Sayangnya tidak, tidak ada yang namanya drop jika ada, atau BUAT JIKA TIDAK ADA

Anda bisa menulis skrip plsql untuk memasukkan logika di sana.

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

Saya tidak terlalu menyukai Oracle Syntax, tetapi saya pikir skrip @ Erich akan menjadi seperti ini.

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
sumber
8
Apakah ini bahkan mengkompilasi?
quillbreaker
0

Anda selalu bisa menangkap kesalahan sendiri.

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

Ini dianggap praktik buruk untuk terlalu sering menggunakan ini, mirip dengan tangkapan kosong () dalam bahasa lain.

Salam
K

Khb
sumber
1
Tidak, tidak pernah "pengecualian ketika orang lain
membatalkan
0

Saya lebih suka menentukan tabel dan pemilik skema.

Berhati-hatilah dengan sensitivitas case. (lihat klausa "atas" di bawah).

Saya melemparkan beberapa objek yang berbeda untuk menunjukkan bahwa dapat digunakan di tempat selain 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;  

Dan contoh TABEL:

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
sumber
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;

// Melakukan kode ini, memeriksa apakah tabel ada dan kemudian membuat tabel maks. ini hanya bekerja dalam satu kompilasi

Mahesh Pandeya
sumber
2
Saya percaya ini hanya membuat tabel ketika kesalahan dilemparkan.
Biskuit Ikan
0

Dan jika Anda ingin membuatnya kembali masuk dan meminimalkan drop / membuat siklus, Anda bisa men-cache DDL menggunakan dbms_metadata.get_ddl dan membuat ulang semuanya menggunakan konstruk seperti ini: 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; Ini hanya sampel, harus ada loop di dalam dengan Jenis, nama, dan pemilik menjadi variabel.

Andrei Nossov
sumber
0

Blok seperti ini bisa berguna bagi Anda.

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;  

sumber