ओरेकल: यदि टेबल मौजूद है


343

मैं एक Oracle डेटाबेस के लिए कुछ माइग्रेशन स्क्रिप्ट लिख रहा हूँ, और उम्मीद कर रहा था कि Oracle MySQL के IF EXISTSनिर्माण के समान हो ।

विशेष रूप से, जब भी मैं MySQL में एक तालिका को गिराना चाहता हूं, मैं कुछ ऐसा करता हूं

DROP TABLE IF EXISTS `table_name`;

इस तरह, यदि तालिका मौजूद नहीं है, तो DROPत्रुटि उत्पन्न नहीं होती है, और स्क्रिप्ट जारी रह सकती है।

क्या ओरेकल के पास एक समान तंत्र है? मुझे महसूस होता है कि मैं जांचने के लिए निम्न तालिका का उपयोग कर सकता हूं कि क्या कोई तालिका मौजूद है या नहीं

SELECT * FROM dba_tables where table_name = 'table_name';

लेकिन एक साथ बांधने के लिए वाक्य रचना DROPमुझे बच रहा है।

जवाबों:


585

सबसे अच्छा और सबसे कुशल तरीका "तालिका नहीं मिली" अपवाद को पकड़ना है: यह जाँच के ओवरहेड से बचता है यदि तालिका दो बार मौजूद है; और इस समस्या से ग्रस्त नहीं है कि अगर 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 अनदेखा करना है।
एंड्रयू स्वान

14
एक प्रक्रिया लिखने की क्या ज़रूरत है? वहाँ ऐसा करने के लिए एक बेहतर तरीका नहीं है?
विल्सन फ्रीटस

8
यदि मैं कई EXECUTE IMMEDIATE 'DROP TABLE mytable';वाक्यों (स्क्रिप्ट में प्रत्येक तालिका के लिए) को जोड़ता हूं , तो क्या मुझे हर एक के लिए एक अपवाद हैंडलर लगाना होगा, या क्या यह सभी प्रेषण को एक BEGIN ... EXCEPTION ... END;ब्लॉक में लपेटने के लिए पर्याप्त है ?
थ्रूज़

8
@ jpmc26: MS SQL के लिए बराबर है IF OBJECT_ID('TblName') IS NOT NULL DROP TABLE TblName। ऐसा लगता है कि एक SQL भाषा की क्रिया की कीमत के लिए आनुपातिक है।

6
@JeffreyKemp आप ऐसा नहीं सोचेंगे, लेकिन मैंने समय और समय फिर से पाया है कि ओरेकल सब कुछ कठिन बना देता है। जब आप प्रति घंटे एक औसत वाक्य रचना त्रुटि या किसी अन्य डेटाबेस में स्पष्ट और आसान तरीके से कुछ करने की कोशिश कर रहे हैं (जैसे सशर्त रूप से एक तत्व को छोड़ना) और उन प्रकार की समस्याओं को दैनिक रूप से पॉप अप करने की कोशिश करना, यह जोड़ता है। तेज।
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;

यह जाँचने के लिए है कि क्या वर्तमान स्कीमा में कोई तालिका मौजूद है। यह जांचने के लिए कि क्या एक दी गई तालिका पहले से ही एक अलग स्कीमा में मौजूद है, आपको शर्त के all_tablesबजाय इसका उपयोग करना होगाuser_tablesall_tables.owner = upper('schema_name')


34
+1 यह बेहतर है क्योंकि अपवाद डिकोडिंग पर रिले न करें समझने के लिए कि क्या करना है। कोड को
मंत्रमुग्ध करना

4
@Daitangio से सहमत - प्रदर्शन आम तौर पर रन-एक बार परिनियोजन स्क्रिप्ट के साथ रखरखाव को ट्रम्प नहीं करता है।
पेटीएम

1
मुझे यह समझने में दिलचस्पी होगी कि क्या निहितार्थ-प्रतिबद्ध यहां एक हिस्सा निभाता है। आप चाहते हैं कि SELECT और DROP समान लेन-देन के अंदर हों। [जाहिर है कि बाद में निष्पादित किए जाने वाले किसी भी DDL को अनदेखा किया जा सकता है। ]
मैथ्यू

2
@ मैथ्यू, DROP एक DDL कमांड है, इसलिए यह पहले एक COMMIT जारी करेगा, तालिका गिराएगा, फिर दूसरा COMMIT जारी करेगा। बेशक, इस उदाहरण में कोई लेनदेन नहीं है (क्योंकि यह केवल एक क्वेरी जारी की गई है) इसलिए इससे कोई फर्क नहीं पड़ता; लेकिन अगर उपयोगकर्ता ने पहले कुछ डीएमएल जारी किया था, तो किसी भी डीडीएल को निष्पादित करने से पहले यह अंतर्निहित रूप से प्रतिबद्ध होगा।
जेफरी केम्प

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, मैंने इसे निम्नलिखित एसक्यूएल जारी करने के लिए कॉल करने की कोशिश की। लेकिन यह काम नहीं आया। delobject ('MyTable', 'TABLE'); मुझे निम्न त्रुटि मिल रही है -------------------------------- कमांड में लाइन 1 पर शुरू होने में त्रुटि: delobject ('MyTable) ',' TABLE ') त्रुटि रिपोर्ट: अज्ञात कमांड
Shai

1
EXECUTE कमांड का उपयोग करें - EXECUTE DelObject ('MyTable', 'TABLE');
इडानुडा

13

बस एक पूर्ण कोड पोस्ट करना चाहता था जो एक तालिका बनाएगा और इसे छोड़ देगा यदि यह पहले से ही जेफरी के कोड (उसके लिए यश नहीं, तो!) का उपयोग करके मौजूद है।

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
व्यक्तिगत रूप से, मैं क्रिएट टेबल को एक अलग चरण में रखूंगा, क्योंकि इसे गतिशील रूप से करने की आवश्यकता नहीं है और अपवाद हैंडलर की आवश्यकता नहीं है।
जेफरी केम्प

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स्क्रिप्ट एक त्रुटि के मामले में समाप्त हो जाएगा।

यह भी देखें: जब SQL SQL दस्तावेज़ों


3

दैवज्ञ में कोई 'DROP TABLE IF EXISTS' नहीं है, आपको चयन कथन करना होगा।

इसे आज़माएं (मैं ओरेकल सिंटैक्स पर नहीं हूं, इसलिए यदि मेरे चर आइवी हैं, तो कृपया मुझे क्षमा करें):

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

मैंने स्क्रिप्ट को ओरेकल सिंटैक्स में अनुवाद करने का प्रयास किया है।
टॉम

3
घोषणा संख्या की संख्या; जहां सभी तालिका से तालिका में गणना (*) का चयन करें, जहां तालिका_नाम = 'x'; अगर गिनती> 0 तो तत्काल 'ड्रॉप टेबल x' निष्पादित करें; अगर अंत; समाप्त; आप डीडीएल को सीधे लेनदेन ब्लॉक से नहीं चला सकते हैं, आपको निष्पादन का उपयोग करने की आवश्यकता है।
19

बहुत बहुत धन्यवाद! मुझे एहसास नहीं था कि वाक्यविन्यास अलग था। मुझे पता है कि आपको एक शुरुआत / अंत में पूरी बात लपेटने की आवश्यकता है, लेकिन मुझे लगा कि यह किसी अन्य स्क्रिप्ट के बीच में चलाया जा रहा है। टॉम: मैंने अपना संस्करण छोड़ने का फैसला किया है और आपकी नकल नहीं की है, इसलिए मैं आपसे कोई वोट नहीं लेता, जो जाहिर तौर पर सही ansswer है।
एरच

मुझे नहीं लगता कि यह संकलन होगा। स्कीमा मालिक को यहां शामिल करना भी महत्वपूर्ण हो सकता है या आप एक तालिका के लिए 'सत्य' प्राप्त कर सकते हैं जिसका आपको समान नाम से मतलब नहीं था।
एलन

पोस्ट किए जाने के 10 मिनट बाद आपका उत्तर सही Oracle सिंटैक्स द्वारा ले लिया गया था।
jpmc26

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 "पहले से मौजूद नहीं है" बनाम " मौजूद नहीं था, लेकिन अब नहीं है" :)
जेफरी केम्प

2

एक तरीका 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
लेकिन यह एक तालिका का नाम नहीं हो सकता है।
जेफरी केम्प

विभिन्न स्कीमाओं में उस नाम का उपयोग करने वाली विभिन्न सारणियाँ भी हो सकती हैं।
हाइब्रिस 95

0

अफसोस की बात है कि अगर मौजूद नहीं है, तो ड्रॉप जैसी कोई चीज नहीं है, या अगर कोई चीज नहीं है

आप तर्क को वहां शामिल करने के लिए एक plsql स्क्रिप्ट लिख सकते हैं।

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

मैं ओरेकल सिंटैक्स में ज्यादा नहीं हूँ, लेकिन मुझे लगता है कि @ एरिच की स्क्रिप्ट कुछ इस तरह होगी।

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
क्या यह भी संकलन करता है?
क्विलब्रेकर

0

आप हमेशा स्वयं त्रुटि पकड़ सकते हैं।

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

अन्य भाषाओं में खाली कैच () के समान ही इसका अधिक उपयोग करना बुरा व्यवहार माना जाता है।

सादर
के


1
नहीं, कभी भी "अपवाद जब दूसरे तब अशक्त"
चमत्कार 173

0

मैं तालिका और स्कीमा स्वामी निर्दिष्ट करना पसंद करता हूं।

केस संवेदनशीलता के लिए भी देखें। (नीचे "ऊपरी" खंड देखें)।

मैंने विभिन्न वस्तुओं के एक जोड़े को यह दिखाने के लिए फेंक दिया कि इसका उपयोग टेबल के अलावा स्थानों में भी किया जा सकता है।

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

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;  

और एक टेबल उदाहरण:

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;

// इस कोड को करते हुए, जाँचता है कि क्या तालिका मौजूद है और बाद में यह तालिका अधिकतम बनाता है। यह बस एक संकलन में काम करता है


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; यह सिर्फ एक नमूना है, इसमें एक लूप होना चाहिए। डीडीएल प्रकार, नाम और मालिक चर रहे हैं।


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;  
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.