Oracle: Nếu Bảng tồn tại


343

Tôi đang viết một số tập lệnh di chuyển cho cơ sở dữ liệu Oracle và hy vọng Oracle có cái gì đó tương tự như của MySQL IF EXISTS cấu trúc .

Cụ thể, bất cứ khi nào tôi muốn bỏ một bảng trong MySQL, tôi sẽ làm một cái gì đó như

DROP TABLE IF EXISTS `table_name`;

Bằng cách này, nếu bảng không tồn tại, DROP thì không tạo ra lỗi và tập lệnh có thể tiếp tục.

Liệu Oracle có một cơ chế tương tự? Tôi nhận ra rằng tôi có thể sử dụng truy vấn sau để kiểm tra xem bảng có tồn tại hay không

SELECT * FROM dba_tables where table_name = 'table_name';

nhưng cú pháp buộc nó cùng với a DROPđang thoát khỏi tôi.

Câu trả lời:


585

Cách tốt nhất và hiệu quả nhất là bắt ngoại lệ "không tìm thấy bảng": điều này tránh được chi phí kiểm tra nếu bảng tồn tại hai lần; và không gặp phải vấn đề là nếu DROP thất bại vì một số lý do khác (điều đó có thể quan trọng) thì ngoại lệ vẫn được đưa ra cho người gọi:

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

ĐỊA CHỈ Để tham khảo, đây là các khối tương đương cho các loại đối tượng khác:

Sự nối tiếp

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

Lượt xem

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

Kích hoạt

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

Mục lục

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

Cột

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;

Liên kết cơ sở dữ liệu

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

Chế độ xem cụ thể

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

Kiểu

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

Hạn chế

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;

Công việc lên lịch

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

Người dùng / Lược đồ

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;

Gói

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

Thủ tục

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

Chức năng

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

Không gian bảng

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

Từ đồng nghĩa

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

13
Và để loại bỏ NGƯỜI DÙNG, SQLCODE bỏ qua là -1918.
Andrew Swan

14
Một người cần phải viết một thủ tục làm điều đó? Không có cách nào tốt hơn để làm điều đó?
Wilson Freitas

8
Nếu tôi thêm nhiều EXECUTE IMMEDIATE 'DROP TABLE mytable';câu (một câu cho mỗi bảng trong tập lệnh), tôi có phải đặt một trình xử lý ngoại lệ cho mỗi câu không, hoặc nó có đủ để bọc tất cả các câu trong một BEGIN ... EXCEPTION ... END;khối không?
Throoze

8
@ jpmc26: Tương đương với MS SQL là IF OBJECT_ID('TblName') IS NOT NULL DROP TABLE TblName. Có vẻ như độ dài của ngôn ngữ SQL tỷ lệ thuận với giá cả.

6
@JeffreyKemp Bạn sẽ không nghĩ như vậy, nhưng tôi đã tìm thấy hết lần này đến lần khác rằng Oracle làm mọi thứ trở nên khó khăn. Khi bạn dành trung bình một giờ cho mỗi lỗi cú pháp tối nghĩa hoặc cố gắng tìm ra cách thực hiện điều gì đó rõ ràng và dễ dàng trong cơ sở dữ liệu khác (như có điều kiện thả một phần tử) và các loại vấn đề đó xuất hiện hàng ngày, nó sẽ xuất hiện. Nhanh.
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;

Đó là để kiểm tra xem một bảng trong lược đồ hiện tại có tồn tại hay không. Để kiểm tra xem một bảng đã cho có tồn tại trong một lược đồ khác hay không, bạn phải sử dụng all_tablesthay vì user_tablesvà thêm điều kiệnall_tables.owner = upper('schema_name')


34
+1 Điều này tốt hơn vì không chuyển tiếp vào giải mã ngoại lệ để hiểu phải làm gì. Mã sẽ dễ dàng hơn để hiểu và hiểu
daitangio

4
Đồng ý với @daitangio - hiệu suất thường không thể duy trì được với các kịch bản triển khai một lần.
pettys

1
Tôi sẽ quan tâm để hiểu nếu cam kết ngầm đóng một phần ở đây. Bạn sẽ muốn CHỌN và DROP ở trong cùng một giao dịch. [Rõ ràng bỏ qua mọi DDL tiếp theo có thể được thực thi. ]
Mathew

2
@Matthew, DROP là lệnh DDL, vì vậy trước tiên, nó sẽ phát hành một CAM KẾT, bỏ bảng, sau đó phát hành một CAM KẾT thứ 2. Tất nhiên, trong ví dụ này không có giao dịch (vì nó chỉ đưa ra một truy vấn) nên không có gì khác biệt; nhưng nếu trước đây người dùng đã ban hành một số DML, thì nó sẽ được cam kết ngầm trước khi bất kỳ DDL nào được thực thi.
Jeffrey Kemp

28

Tôi đã tìm kiếm tương tự nhưng cuối cùng tôi đã viết một thủ tục để giúp tôi:

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;

Hi vọng điêu nay co ich


Sau khi tôi tạo ở trên Proc. delobject, tôi đã cố gắng gọi nó ban hành SQL sau. Nhưng nó không hoạt động. delobject ('MyTable', 'BẢNG'); Tôi đang gặp lỗi sau -------------------------------- Lỗi bắt đầu từ dòng 1 trong lệnh: delobject ('MyTable ',' BẢNG ') Báo cáo lỗi: Lệnh không xác định
Shai

1
sử dụng lệnh EXECUTE - EXECUTE DelObject ('MyTable', 'TABLE');
idanuda

13

chỉ muốn đăng một mã đầy đủ sẽ tạo một bảng và thả nó nếu nó đã tồn tại bằng cách sử dụng mã của Jeffrey (kudos cho anh ta, không phải tôi!).

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
Cá nhân, tôi đã đặt BẢNG TẠO trong một bước riêng biệt, vì nó không cần phải được thực hiện một cách linh hoạt và không cần một trình xử lý ngoại lệ.
Jeffrey Kemp

11

Với SQL * PLUS, bạn cũng có thể sử dụng lệnh WHENEVER SQLERROR:

WHENEVER SQLERROR CONTINUE NONE
DROP TABLE TABLE_NAME;

WHENEVER SQLERROR EXIT SQL.SQLCODE
DROP TABLE TABLE_NAME;

Với CONTINUE NONEmột lỗi được báo cáo, nhưng kịch bản sẽ tiếp tục. Với EXIT SQL.SQLCODEkịch bản sẽ bị chấm dứt trong trường hợp có lỗi.

xem thêm: Tài liệu SQLERROR KHI NÀO


3

Không có 'DROP TABLE NẾU EXISTS' trong orory, bạn sẽ phải thực hiện câu lệnh chọn.

hãy thử điều này (tôi không theo cú pháp tiên tri, vì vậy nếu các biến của tôi là ify, xin vui lòng tha thứ cho tôi):

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

Tôi đã cố gắng dịch tập lệnh sang cú pháp tiên tri.
Tom

3
khai số đếm; bắt đầu chọn đếm (*) thành đếm từ all_tables trong đó tên_bảng = 'x'; nếu đếm> 0 thì thực hiện ngay lập tức 'thả bảng x'; kết thúc nếu; kết thúc; Bạn không thể chạy DDL trực tiếp từ một khối giao dịch, bạn cần sử dụng thực thi.
Khb

Cảm ơn rất nhiều! Tôi đã không nhận ra cú pháp là khác nhau. Tôi biết bạn cần phải bao quát toàn bộ mọi thứ trong một khởi đầu / kết thúc, nhưng tôi đoán rằng nó đang được chạy ở giữa một kịch bản khác. Tom: Tôi đã quyết định rời khỏi phiên bản của mình và không sao chép phiên bản của bạn, vì vậy tôi không nhận được bất kỳ phiếu bầu nào từ bạn, người rõ ràng có quyền trả lời đúng.
Erich

Tôi không nghĩ rằng điều này sẽ biên dịch. Nó cũng có thể quan trọng để bao gồm chủ sở hữu lược đồ ở đây hoặc bạn có thể nhận được 'true' cho một bảng mà bạn không có nghĩa là có cùng tên.
Allen

Câu trả lời của bạn đã được thay thế bằng cú pháp chính xác của Oracle 10 phút sau khi bài này được đăng.
jpmc26

3

Tôi thích giải pháp kinh tế

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

Một phương pháp khác là xác định một ngoại lệ và sau đó chỉ bắt ngoại lệ đó để cho tất cả những người khác tuyên truyền.

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 "đã không tồn tại" so với "đã tồn tại, nhưng không còn tồn tại" :)
Jeffrey Kemp

2

Một cách là sử dụng DBMS_ASSERT.SQL_OBJECT_NAME :

Hàm này xác minh rằng chuỗi tham số đầu vào là một định danh SQL đủ điều kiện của một đối tượng SQL hiện có.

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

Trình diễn DBFiddle


2
Nhưng nó có thể không phải là tên của một bảng.
Jeffrey Kemp

Cũng có thể có nhiều bảng sử dụng tên đó trong các lược đồ khác nhau.
Hybris95

0

Đáng buồn thay, không có thứ gọi là thả nếu tồn tại, hoặc TẠO NẾU KHÔNG HIỆN TẠI

Bạn có thể viết một kịch bản Pleaseql để bao gồm logic ở đó.

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

Tôi không biết nhiều về Oracle Syntax, nhưng tôi nghĩ kịch bản của @ Erich sẽ giống như thế này.

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
Điều này thậm chí còn biên dịch?
quillbreaker

0

Bạn luôn có thể tự bắt lỗi.

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

Nó được coi là thực hành xấu để lạm dụng điều này, tương tự như việc bắt trống () trong các ngôn ngữ khác.

Lời chúc mừng
K


1
Không, không bao giờ "ngoại lệ khi người khác vô hiệu"
miracle173

0

Tôi thích chỉ định bảng và chủ sở hữu lược đồ.

Xem ra cho trường hợp nhạy cảm là tốt. (xem mệnh đề "trên" bên dưới).

Tôi đã ném một vài đối tượng khác nhau để cho thấy rằng có thể được sử dụng ở những nơi ngoài BẢNG.

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

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;  

Và một ví dụ BẢNG:

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;

// Thực hiện mã này, kiểm tra nếu bảng tồn tại và sau đó nó tạo bảng tối đa. điều này chỉ đơn giản là làm việc trong biên dịch đơn


2
Tôi tin rằng điều này chỉ tạo bảng khi lỗi được ném.
Bánh quy cá

0

Và nếu bạn muốn làm cho nó có thể nhập lại và giảm thiểu các chu kỳ thả / tạo, bạn có thể lưu trữ DDL bằng cách sử dụng dbms_metadata.get_ddl và tạo lại mọi thứ bằng cách sử dụng một cấu trúc như thế này: 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; Đây chỉ là một mẫu, bên trong phải có một vòng lặp Loại DDL, tên và chủ sở hữu là các biến.


0

Một khối như thế này có thể hữu ích cho bạn.

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;  
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.