Oracle: Tablo Varsa


343

Bir Oracle veritabanı için bazı geçiş komut dosyaları yazıyorum ve Oracle'ın MySQL'in IF EXISTSyapısına benzer bir şey olmasını umuyordum .

Özellikle, MySQL'de bir tablo bırakmak istediğimde,

DROP TABLE IF EXISTS `table_name`;

Bu şekilde, tablo yoksa, DROP hata oluşmaz ve komut dosyası devam edebilir.

Oracle'ın benzer bir mekanizması var mı? Bir tablonun var olup olmadığını kontrol etmek için aşağıdaki sorguyu kullanabileceğimin farkındayım

SELECT * FROM dba_tables where table_name = 'table_name';

ama bunu a ile birleştirmenin sözdizimi DROPkaçıyor.

Yanıtlar:


586

En iyi ve en etkili yol, "tablo bulunamadı" istisnasını yakalamaktır: Bu, tablonun iki kez var olup olmadığını kontrol etmenin önüne geçer; ve DROP'un başka bir nedenden dolayı başarısız olması durumunda (bu önemli olabilir) istisna hala arayan kişiye yöneltilir:

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

ADDENDUM Referans olarak, diğer nesne türleri için eşdeğer bloklar aşağıda verilmiştir:

Sıra

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

Görünüm

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

tetik

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;

sütun

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;

Veritabanı Bağlantısı

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

Materyalleştirilmiş Görünüm

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

tip

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

Kısıtlama

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;

Zamanlayıcı İşi

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

Kullanıcı / Şema

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;

prosedür

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

fonksiyon

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;

Eşanlamlı sözcük

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

13
Ve bir KULLANICI düşürmek için, göz ardı edilecek SQLCODE -1918'dir.
Andrew

14
Bunu yapmak için bir prosedür yazmak gerekir? Bunu yapmanın daha iyi bir yolu yok mu?
Wilson Freitas

8
Çok sayıda EXECUTE IMMEDIATE 'DROP TABLE mytable';cümle eklersem (komut dosyasındaki her tablo için bir tane), her biri için bir istisna işleyicisi koymak zorunda mıyım yoksa tüm cümleleri tek bir BEGIN ... EXCEPTION ... END;blokta sarmak için yeterli mi?
Aralık'ta Throoze

8
@ jpmc26: MS SQL için eşdeğerdir IF OBJECT_ID('TblName') IS NOT NULL DROP TABLE TblName. Bir SQL dilinin ayrıntı düzeyi fiyatla orantılı görünüyor.

6
@JeffreyKemp Böyle düşünmezsiniz, ama Oracle'ın her şeyi zorlaştırdığını defalarca buldum. Belirsiz sözdizimi hatası başına ortalama bir saat harcadığınızda veya başka bir veritabanında (koşullu olarak bir öğeyi bırakma gibi) açık ve kolay bir şey yapmayı anlamaya çalıştığınızda ve bu tür sorunlar günlük olarak ortaya çıkar. Hızlı.
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;

Geçerli şemadaki bir tablonun var olup olmadığını kontrol etmek içindir. Belirli bir tablonun farklı bir şemada var olup olmadığını kontrol etmek için , koşulu all_tablesyerine kullanmanız user_tablesve koşulu eklemeniz gerekirall_tables.owner = upper('schema_name')


34
+1 Bu daha iyi çünkü ne yapılacağını anlamak için istisna kod çözme işlemine geçmeyin. Kodun
mantolanması

4
@Daitangio ile aynı fikirde olun - performans genellikle bir kez çalıştırılan dağıtım komut dosyalarıyla sürdürülebilirliği bozmaz.
Mart'ta pettys

1
Burada örtük-taahhütün bir rol oynayıp oynamadığını anlamak isterim. SELECT ve DROP öğelerinin aynı işlem içinde olmasını istersiniz. [Yürütülebilecek sonraki DDL'leri yoksaymak. ]
Mathew

2
@Matthew, DROP bir DDL komutudur, bu yüzden önce bir COMMIT verir, tabloyu bırakır, sonra 2. bir COMMIT verir. Tabii ki, bu örnekte herhangi bir işlem yoktur (sadece bir sorgu yayınlandığından) hiçbir fark yaratmaz; ancak kullanıcı daha önce bir DML yayınladıysa, herhangi bir DDL yürütülmeden önce örtük olarak işlenir.
Jeffrey Kemp

28

Ben de aynı şeyi arıyordum ama bana yardım etmek için bir prosedür yazdım:

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;

Bu yardımcı olur umarım


Ben yukarıda proc oluşturduktan sonra. delobject, ben aşağıdaki SQL veren çağırmaya çalıştı. Ama çalışmadı. delobject ('MyTable', 'TABLE'); Aşağıdaki hatayı alıyorum -------------------------------- komut 1 satırında başlayan hata: delobject ('MyTable ',' TABLE ') Hata raporu: Bilinmeyen Komut
Shai

1
EXECUTE Komutunu kullanın - EXECUTE DelObject ('MyTable', 'TABLE');
idanuda

13

sadece bir tablo oluşturacak tam bir kod göndermek istedim ve zaten Jeffrey kodunu kullanarak varsa onu bırakın (ona değil 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
Şahsen, CREATE TABLE'ı ayrı bir adımda koyardım, çünkü dinamik olarak yapılması gerekmiyor ve bir istisna işleyicisi gerektirmiyor.
Jeffrey Kemp

11

SQL * PLUS ile WHENEVER SQLERROR komutunu da kullanabilirsiniz:

WHENEVER SQLERROR CONTINUE NONE
DROP TABLE TABLE_NAME;

WHENEVER SQLERROR EXIT SQL.SQLCODE
DROP TABLE TABLE_NAME;

İle CONTINUE NONEbir hata bildirilir, ama senaryo devam edecek. İle EXIT SQL.SQLCODEkomut bir hata durumunda sonlandırılacaktır.

ayrıca bakınız: SIKÇA SORUNSA


3

Oracle'de 'EĞER VARSA DROP TABLOSU' yoktur, select deyimini yapmanız gerekir.

Bunu deneyin (oracle sözdiziminde değilim, bu yüzden değişkenlerim ify ise, lütfen beni affet):

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

Senaryoyu oracle sözdizimine çevirmeye çalıştım.
Tom

3
sayım numarasını beyan eder; Tablo_adı = 'x' olmak üzere all_tables'ten count olarak count (*) seçin; count> 0 ise hemen 'drop table x' komutunu çalıştırın; eğer; son; DDL'yi doğrudan bir işlem bloğundan çalıştıramazsınız, yürütmeyi kullanmanız gerekir.
Khb

Çok teşekkürler! Sözdiziminin o kadar farklı olduğunu fark etmemiştim. Her şeyi bir başlangıç ​​/ sonda sarmanız gerektiğini biliyorum, ama başka bir komut dosyasının ortasında çalıştığını düşündüm. Tom: Sürümümü bırakmaya ve seninkini kopyalamamaya karar verdim, bu yüzden senden hiç oy almıyorum, açıkça doğru cevap veren.
Erich

Bunun derleneceğini sanmıyorum. Şema sahibini buraya dahil etmek de önemli olabilir veya aynı adla almak istemediğiniz bir tablo için 'doğru' alabilirsiniz.
Allen

Yanıtınız , gönderildikten 10 dakika sonra doğru Oracle sözdizimi ile değiştirildi .
jpmc26

3

Ekonomik çözümü takip etmeyi tercih ederim

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

Başka bir yöntem, bir istisnayı tanımlamak ve daha sonra sadece diğerlerinin yayılmasına izin veren istisnayı yakalamaktır.

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 "zaten mevcut değil" vs. "var, ancak artık yok" :)
Jeffrey Kemp

2

Bunun bir yolu DBMS_ASSERT.SQL_OBJECT_NAME kullanmaktır :

Bu işlev, giriş parametresi dizesinin mevcut bir SQL nesnesinin nitelikli bir SQL tanımlayıcısı olduğunu doğrular.

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 Demosu


2
Ancak bir tablonun adı olmayabilir.
Jeffrey Kemp

Farklı şemalarda bu adı kullanan çeşitli tablolar da olabilir.
Hybris95

0

Ne yazık ki hayır, varsa damla veya var değilse oluştur gibi bir şey yoktur

Buraya mantığı dahil etmek için bir plsql betiği yazabilirsiniz.

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

Çok Oracle Syntax içine değilim, ama @ Erich'ın senaryosu böyle bir şey olacağını düşünüyorum.

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
Bu bile derleniyor mu?
quillbreaker

0

Hatayı her zaman kendiniz yakalayabilirsiniz.

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

Diğer dillerde boş catch () 'lere benzer şekilde, aşırı kullanım kötü bir uygulama olarak kabul edilir.

Saygılarımızla
K


1
Hayır, asla "diğerleri sonra null olduğunda istisna"
miracle173

0

Tabloyu ve şema sahibini belirtmeyi tercih ederim.

Büyük / küçük harf duyarlılığına da dikkat edin. (aşağıdaki "üst" maddeye bakın).

Tabloların yanında yerlerde kullanılabileceğini göstermek için birkaç farklı nesne attım.

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

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;  

Ve bir TABLO örneği:

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;

// Bu kodu yaparak tablonun var olup olmadığını kontrol eder ve daha sonra tablo maks. bu sadece tek bir derlemede çalışır


2
Bunun sadece hata atıldığında tablo oluşturduğuna inanıyorum.
Balık Bisküvi

0

Ve yeniden girilebilir hale getirmek ve bırak / oluştur döngülerini en aza indirmek istiyorsanız, dbms_metadata.get_ddl kullanarak DDL'yi önbelleğe alabilir ve böyle bir yapı kullanarak her şeyi yeniden oluşturabilirsiniz: 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; Bu sadece bir örnek, içinde bir döngü olmalı DDL türü, adı ve değişken değişkenidir.


0

Böyle bir blok sizin için yararlı olabilir.

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;  
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.