Oracle: ถ้า Table มีอยู่


343

ฉันเขียนสคริปต์การย้ายข้อมูลบางอย่างสำหรับฐานข้อมูล Oracle และหวังว่า Oracle จะมีสิ่งที่คล้ายกับIF EXISTSโครงสร้างของ MySQL

โดยเฉพาะอย่างยิ่งเมื่อใดก็ตามที่ฉันต้องการวางตารางใน MySQL ฉันทำอะไรเช่น

DROP TABLE IF EXISTS `table_name`;

วิธีนี้หากตารางไม่มีอยู่DROPจะไม่เกิดข้อผิดพลาดและสคริปต์สามารถดำเนินการต่อได้

Oracle มีกลไกที่คล้ายกันหรือไม่ ฉันรู้ว่าฉันสามารถใช้แบบสอบถามต่อไปนี้เพื่อตรวจสอบว่ามีตารางอยู่หรือไม่

SELECT * FROM dba_tables where table_name = 'table_name';

แต่ไวยากรณ์สำหรับการคาดเดาสิ่งนั้นพร้อมกับ a DROPคือการหลบหนีฉัน

คำตอบ:


586

วิธีที่ดีที่สุดและมีประสิทธิภาพมากที่สุดคือการจับข้อยกเว้น "ไม่พบตาราง": วิธีนี้จะช่วยหลีกเลี่ยงค่าใช้จ่ายในการตรวจสอบว่าตารางมีอยู่สองครั้งหรือไม่ และไม่ประสบปัญหาว่าหาก DROP ล้มเหลวด้วยเหตุผลอื่น (นั่นอาจสำคัญ) ข้อยกเว้นจะยังคงถูกยกไปยังผู้เรียก:

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

ADDENDUM สำหรับการอ้างอิงต่อไปนี้เป็นบล็อกที่เทียบเท่าสำหรับวัตถุประเภทอื่น:

ลำดับ

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;

tablespace

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
และสำหรับการทิ้งผู้ใช้ SQLCODE เพื่อเพิกเฉยคือ -1918
Andrew Swan

14
หนึ่งต้องเขียนขั้นตอนจะทำเช่นนั้น? ไม่มีวิธีที่ดีกว่าในการทำเช่นนั้นหรือ
Wilson Freitas

8
หากฉันเพิ่มEXECUTE IMMEDIATE 'DROP TABLE mytable';ประโยคจำนวนมาก(หนึ่งรายการสำหรับแต่ละตารางในสคริปต์) ฉันต้องใส่ตัวจัดการข้อยกเว้นหนึ่งข้อสำหรับแต่ละข้อหรือไม่หรือว่าเพียงพอที่จะรวมส่วนที่ห่อไว้ทั้งหมดในหนึ่งBEGIN ... EXCEPTION ... END;บล็อก?
Throoze

8
@ jpmc26: เทียบเท่าสำหรับ MS SQL IF OBJECT_ID('TblName') IS NOT NULL DROP TABLE TblNameเป็น ดูเหมือนว่าคำฟุ่มเฟื่อยของภาษา SQL เป็นสัดส่วนกับราคา

6
@JeffreyKemp คุณคงไม่คิดอย่างนั้น แต่ฉันได้พบครั้งแล้วครั้งเล่าที่ Oracle ทำให้ทุกอย่างยากลำบาก เมื่อคุณใช้เวลาเฉลี่ยประมาณหนึ่งชั่วโมงต่อข้อผิดพลาดทางไวยากรณ์ที่ไม่ชัดเจนหรือพยายามหาวิธีที่จะทำสิ่งที่ชัดเจนและง่ายในฐานข้อมูลอื่น (เช่นวางองค์ประกอบ) และปัญหาเหล่านั้นปรากฏขึ้นทุกวันมันจะเพิ่มขึ้น รวดเร็ว
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_tablesและเพิ่มเงื่อนไขall_tables.owner = upper('schema_name')


34
+1 สิ่งนี้ดีกว่าเพราะอย่าส่งต่อการถอดรหัสเพื่อเข้าใจสิ่งที่ต้องทำ รหัสจะง่ายต่อการเข้าใจและเข้าใจ
daitangio

4
เห็นด้วยกับ @daitangio - ประสิทธิภาพโดยทั่วไปไม่ได้อยู่ที่ความสามารถในการบำรุงรักษาของสคริปต์การปรับใช้ที่ทำงานครั้งเดียว
จิ๊บจ๊อย

1
ฉันสนใจที่จะเข้าใจว่าการกระทำโดยนัยมีส่วนร่วมที่นี่หรือไม่ คุณต้องการให้ SELECT และ DROP อยู่ในธุรกรรมเดียวกัน [เห็นได้ชัดว่าละเว้น DDL ใด ๆ ที่ตามมาซึ่งอาจถูกดำเนินการ ]
แม็ตธิว

2
@Matthew, DROP เป็นคำสั่ง DDL ดังนั้นมันจะออกคำสั่ง COMMIT ก่อนวางตารางจากนั้นออก COMMIT ที่ 2 แน่นอนในตัวอย่างนี้ไม่มีการทำธุรกรรม (เนื่องจากเป็นเพียงการออกแบบสอบถาม) ดังนั้นจึงไม่มีความแตกต่าง แต่ถ้าผู้ใช้เคยใช้ DML มาก่อนมันจะถูกกำหนดโดยปริยายก่อนที่ DDL ใด ๆ จะถูกดำเนินการ
Jeffrey Kemp

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;

หวังว่านี่จะช่วยได้


หลังจากที่ฉันสร้างข้างต้น proc delobject ฉันพยายามเรียกมันว่า SQL ต่อไปนี้ แต่มันก็ไม่ได้ผล delobject ('MyTable', 'Table'); ฉันได้รับข้อผิดพลาดต่อไปนี้ -------------------------------- ข้อผิดพลาดเริ่มต้นที่บรรทัด 1 ในคำสั่ง: delobject ('MyTable ',' TABLE ') รายงานข้อผิดพลาด: คำสั่งที่ไม่รู้จัก
Shai

1
ใช้คำสั่ง EXECUTE - EXOUTE DelObject ('MyTable', 'Table');
idanuda

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
โดยส่วนตัวฉันจะวาง CREATE TABLE ในขั้นตอนแยกเนื่องจากไม่จำเป็นต้องทำแบบไดนามิกและไม่จำเป็นต้องมีตัวจัดการข้อยกเว้น
Jeffrey Kemp

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สคริปต์จะถูกยกเลิกในกรณีที่มีข้อผิดพลาด

ดูเพิ่มเติมที่: เมื่อใดก็ตามที่เอกสาร SQLERROR


3

ไม่มี 'DROP Table IF EXISTS' ใน oracle คุณจะต้องเลือกคำสั่ง

ลองนี้ (ฉันไม่ได้อยู่ในไวยากรณ์ของออราเคิลดังนั้นหากตัวแปรของฉันไม่แน่นอนโปรดยกโทษให้ฉันด้วย):

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

ฉันพยายามแปลสคริปต์เป็นไวยากรณ์ของ oracle
Tom

3
ประกาศจำนวนนับ เริ่มต้นเลือก count (*) เป็น count จาก all_tables โดยที่ table_name = 'x'; ถ้านับ> 0 ให้ดำเนินการทันที 'drop table x'; จบถ้า; จบ; คุณไม่สามารถเรียกใช้ DDL โดยตรงจากบล็อกธุรกรรมคุณต้องใช้ execute
Khb

ขอบคุณมาก ๆ! ฉันไม่ได้ตระหนักถึงไวยากรณ์ที่แตกต่างกัน ฉันรู้ว่าคุณจำเป็นต้องห่อสิ่งทั้งหมดไว้ในจุดเริ่มต้น / สิ้นสุด แต่ฉันคิดว่ามันถูกเรียกใช้ระหว่างสคริปต์อื่น ทอม: ฉันตัดสินใจที่จะออกจากเวอร์ชันของฉันและไม่คัดลอกของคุณดังนั้นฉันจึงไม่ได้รับการโหวตจากคุณซึ่งเห็นได้ชัดว่ามีคำตอบที่ถูกต้อง
Erich

ฉันไม่คิดว่าจะรวบรวม อาจเป็นสิ่งสำคัญที่จะรวมเจ้าของสคีมาที่นี่หรือคุณอาจได้รับ 'จริง' สำหรับตารางที่คุณไม่ได้ตั้งใจจะใช้ชื่อเดียวกัน
อัลเลน

คำตอบของคุณถูกแทนที่ด้วยไวยากรณ์ของ Oracle ที่ถูกต้อง 10 นาทีหลังจากโพสต์สิ่งนี้
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
แต่มันอาจไม่ใช่ชื่อของตาราง
Jeffrey Kemp

อาจมีตารางต่างๆโดยใช้ชื่อนั้นในสกีมาที่แตกต่างกัน
Hybris95

0

น่าเศร้าที่ไม่มีสิ่งใดที่จะลดลงหากมีอยู่หรือสร้างขึ้นหากไม่มีอยู่

คุณสามารถเขียนสคริปต์ plsql เพื่อรวมตรรกะที่นั่น

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

ฉันไม่ค่อยได้เข้ากับ Oracle Syntax แต่ฉันคิดว่าสคริปต์ของ @ Erich น่าจะเป็นอะไรแบบนี้

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
มันรวบรวมได้ไหม?
quillbreaker

0

คุณสามารถตรวจจับข้อผิดพลาดได้เสมอ

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

มันถือว่าเป็นการปฏิบัติที่ไม่ถูกต้องที่จะใช้สิ่งนี้มากเกินไปคล้ายกับเปล่า ๆ catch () ในภาษาอื่น

ขอแสดงความนับถือ
K


1
ไม่ไม่เคย "ยกเว้นเมื่อผู้อื่นเป็นโมฆะ"
miracle173

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
ฉันเชื่อว่านี่เป็นเพียงการสร้างตารางเมื่อเกิดข้อผิดพลาด
บิสกิต Fish

0

และถ้าคุณต้องการทำให้สามารถป้อนซ้ำได้และลดขนาดรอบ / ลดลงคุณสามารถแคช DDL โดยใช้ dbms_metadata.get_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; นี่เป็นเพียงตัวอย่างควรมีลูปภายในด้วย ชนิด DDL ชื่อและเจ้าของเป็นตัวแปร


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;  
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.