오라클 : 테이블이 존재하는 경우


343

Oracle 데이터베이스에 대한 마이그레이션 스크립트를 작성 IF EXISTS중이며 Oracle이 MySQL의 구성 과 비슷한 것을 원했습니다 .

특히, MySQL에서 테이블을 삭제하려고 할 때마다 다음과 같은 작업을 수행합니다

DROP TABLE IF EXISTS `table_name`;

이렇게하면 테이블이 존재 DROP하지 않으면 오류가 발생하지 않으며 스크립트를 계속 진행할 수 있습니다.

오라클도 비슷한 메커니즘을 가지고 있습니까? 다음 쿼리를 사용하여 테이블이 있는지 여부를 확인할 수 있습니다.

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;

부록 참고로 다른 객체 유형에 해당하는 블록은 다음과 같습니다.

순서

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
절차를 작성해야합니까? 더 좋은 방법이 없습니까?
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 당신은 그렇게 생각하지 않을 것이지만, 나는 오라클이 모든 것을 어렵게 만드는 것을 몇 번이고 발견했습니다. 모호한 구문 오류 당 평균 1 시간을 소비하거나 다른 데이터베이스 (예 : 조건부로 요소 삭제)에서 명백하고 쉬운 작업을 수행하는 방법을 알아 내려고하면 이러한 종류의 문제가 매일 발생합니다. 빠른.
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해야 할 일을 이해하기 위해 예외 디코딩을 중계하지 않기 때문에 더 좋습니다. 코드는
다루기

4
@daitangio에 동의하십시오-성능은 일반적으로 한 번의 배포 스크립트로 유지 관리 성을 능가하지 않습니다.
pettys

1
암시 적 커밋이 여기에서 중요한 역할을하는지 이해하고 싶습니다. SELECT와 DROP이 같은 트랜잭션 내에 있어야합니다. [실행될 수있는 모든 후속 DDL을 무시합니다. ]
Mathew

2
@Matthew에서 DROP은 DDL 명령이므로 먼저 COMMIT를 발행하고 테이블을 삭제 한 다음 두 번째 COMMIT를 발행합니다. 물론,이 예제에서는 트랜잭션이 없기 때문에 (쿼리 만 발행되었으므로) 아무런 차이가 없습니다. 그러나 사용자가 이전에 일부 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 명령-EXECUTE DelObject ( 'MyTable', 'TABLE')를 사용하십시오.
idanuda

13

Jeffrey의 코드를 사용하여 이미 존재하는 경우 테이블을 생성하고 삭제하는 전체 코드를 게시하고 싶었습니다.

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'가 없으므로 select 문을 수행해야합니다.

이것을 시도하십시오 (오라클 구문에 의존하지 않으므로 변수가 ify이면 용서하십시오).

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
카운트 번호를 선언; all_tables에서 count (*) 선택을 시작하십시오. table_name = 'x'; count> 0이면 즉시 'drop table x'를 실행하십시오. 경우 종료; 종료; 트랜잭션 블록에서 직접 DDL을 실행할 수 없으므로 execute를 사용해야합니다.
Khb

매우 감사합니다! 구문이 그렇게 다르다는 것을 깨닫지 못했습니다. 나는 당신이 시작 / 종료로 모든 것을 포장해야한다는 것을 알고 있었지만 다른 스크립트의 중간에서 실행되고 있다고 생각했습니다. Tom : 나는 내 버전을 남기고 당신의 버전을 복사하지 않기로 결정했습니다.
Erich

나는 이것이 컴파일 될 것이라고 생각하지 않는다. 여기에 스키마 소유자를 포함시키는 것이 중요하거나 동일한 이름으로 사용하려는 테이블에 대해 'true'를 얻을 수도 있습니다.
Allen

귀하의 답변 은 게시 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 "이미 존재하지 않음""존재하지만 더 이상 존재하지 않음" :)
Jeffrey Kemp

2

한 가지 방법은 DBMS_ASSERT.SQL_OBJECT_NAME 을 사용하는 것입니다 .

이 함수는 입력 매개 변수 문자열이 기존 SQL 오브젝트의 규정 된 SQL ID인지 확인합니다.

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
아니오, 절대 "다른 사람들이 널 때 예외"
기적 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;  

그리고 TABLE 예제 :

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; 이는 샘플 일뿐입니다. 변수 인 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.