행위

오라클 프로시져

DB CAFE

thumb_up 추천메뉴 바로가기


1 ORACLE 프로시져[편집]

1.1 프로시져[편집]

1.1.1 프로시져 재 컴파일[편집]

ALTER PROCEDURE [PROCEDURE NAME] COMPILE;
SELECT 'ALTER PROCEDURE '||OBJECT_NAME||' COMPILE;' DSQL
  FROM USER_PROCEDURES
 WHERE OBJECT_TYPE = 'PROCEDURE'
 ORDER BY OBJECT_NAME
;


1.1.2 프로시져 소스 조회[편집]

SELECT *
  FROM DBA_SOURCE
 WHERE TYPE='PROCEDURE'
   AND TEXT LIKE '%PROD_ID%';

1.2 패키지에서 주석처리 되지 않은 항목 조회[편집]

오라클 사용자가 사용하는 패키지의 BODY 소스를 검색하여
주석이 없거나 패턴이 맞지 않는 항목을 조회 한다.


SELECT * FROM DBA_OBJECTS B 
 WHERE B.OWNER =:IN_OWNER 
   AND B.OBJECT_TYPE = 'PACKAGE BODY' 
   AND B.STATUS <> 'INVALID' -- VALID 상태만 조회, 만약 INVALID 된다고 해도 패키지를 수행하는 순간 컴파일 됨. 
   AND NOT EXISTS (SELECT 1
                     FROM DBA_SOURCE A
                    WHERE A.OWNER = B.OWNER
                      AND A.TYPE = B.OBJECT_TYPE
                      AND A.NAME = B.OBJECT_NAME
                      AND A.LINE <= 5
                      AND A.TEXT LIKE '%NAME%');

2 ORACLE 프로시져 추출쿼리[편집]

== 프로시져 추출쿼리 ==
  • 프로시져 추출
select  OWNER 
    ,  NAME 
    ,  TYPE 
    ,  LINE 
    ,  decode(line,1,'CREATE OR REPLACE ' 
        ||replace(text,'PACKAGE BODY ','PACKAGE BODY ' 
        ||owner 
        ||'.'),text) AS PKG_SOURCE 
from dba_source where type='PROCEDURE' and owner='$ownerName';

3 프로시져_샘플[편집]

3.1 기본 프로시져[편집]

DECLARE
--변수,상수 선언
BEGIN
--실행 가능 SQL문,PL/SQL문
EXCEPTION
--에러처리
END;

/* PL/SQL UPDATE예제(UPDATE,DELETE는 다중행처리 가능) */
Declare
  v_sale number := 2000;
begin
  update test set a = v_sale;
  delete from test where a = v_sale;
  commit;
end;

/* Procedure에서 Procedure를 호출하는 방법 */
A프로시져에서 "B프로시져명(변수, 변수2);"

PROCEDURE 리턴 여러개 
FUNCTION 리턴 한개

SELECT * FROM USER_OBJECTS WHERE OBJECT_TYPE='PROCEDURE';

/* 프로시저나 함수 조회 */
SELECT * FROM USER_OBJECTS WHERE OBJECT_TYPE='FUNCTION';

/* PROCEDURE & FUNCTION 삭제하기 */
DROP FUNCTION lee2;
DROP PROCEDURE lee2;

3.2 프로시져 실행 옵션[편집]

AUTHID DEFINER        실행 시 컴파일 할 때의 유저 사용(DEFAULT)
AUTHID CURRENT_USER   실행 시 현재 접속하고 있는 유저 사용.
  • PL/SQL내에서 EXECUTE IMEDIATE 실행 시 권한없음 에러가 발생하면 AUTHID CURRENT_USER 추가

3.3 커서 활용 샘플[편집]

CREATE OR REPLACE PROCEDURE RTIS_DBA.SP_OBJ_INVALID_TO_VALID
/*
*
*/
(
    IN_DATE IN VARCHAR2 DEFAULT TO_CHAR(SYSDATE,'YYYYMMDD')
)

AUTHID CURRENT_USER 

IS

    CURSOR INVALID_OBJECT IS                          
        
        SELECT A.GRANTEE, A.OWNER, A.TABLE_NAME, A.GRANTOR, A.PRIVILEGE,A.OBJECT_TYPE
          FROM TB_MGR_GRANT A                   
    ;  

                                                  
--    V_SQL   VARCHAR2(200);
    V_G_SQL  VARCHAR2(200);
--    V_L_SQL  VARCHAR2(2000);

    V_GRANTEE  VARCHAR2(100);
    V_OWNER  VARCHAR2(100);
    V_TABLE_NAME  VARCHAR2(100);
    V_GRANTOR  VARCHAR2(100);
    V_PRIVILEGE  VARCHAR2(100);
    V_OBJECT_TYPE  VARCHAR2(100);    
    V_GRANT_REVOKE_GBN VARCHAR2(100);    
    
    V_MSG       long;    
    
BEGIN

    DBMS_OUTPUT.ENABLE;

    FOR V_ROW IN INVALID_OBJECT
    LOOP    
        -- 블록 에러 발생시에도 계속 실행토록 
        BEGIN 
            V_GRANTEE:= V_ROW.GRANTEE;
            V_OWNER:= V_ROW.OWNER;
            V_TABLE_NAME:= V_ROW.TABLE_NAME;
            V_GRANTOR:= V_ROW.GRANTOR;
            V_PRIVILEGE:= V_ROW.PRIVILEGE;
            V_OBJECT_TYPE:= V_ROW.OBJECT_TYPE;
            V_GRANT_REVOKE_GBN := 'GRANT TO USER';            
          
          -- 권한 추가 
          V_G_SQL :=  'GRANT '||V_ROW.PRIVILEGE||' ON '||V_ROW.OWNER||'.'||V_ROW.TABLE_NAME||' TO '||V_ROW.GRANTEE;      
          EXECUTE IMMEDIATE V_G_SQL;
          
          -- LOG 기록 
          INSERT INTO TB_MGR_GRANT_LOG 
                      (GRANTEE      , OWNER     , TABLE_NAME     , GRANTOR, PRIVILEGE,OBJECT_TYPE,GRANT_REVOKE_GBN) 
               VALUES (V_ROW.GRANTEE,V_ROW.OWNER,V_ROW.TABLE_NAME,V_ROW.GRANTOR,V_ROW.PRIVILEGE,V_ROW.OBJECT_TYPE,V_GRANT_REVOKE_GBN);
          COMMIT;
        EXCEPTION
            WHEN OTHERS THEN
               V_MSG := 'ERROR : ['|| to_char(SQLCODE) ||']'|| substr(SQLERRM,1,500); 
              INSERT INTO TB_MGR_GRANT_LOG 
                          (GRANTEE  , OWNER , TABLE_NAME , GRANTOR , PRIVILEGE ,OBJECT_TYPE  ,GRANT_REVOKE_GBN,GRANT_REVOKE_SQL)
                   VALUES (V_GRANTEE,V_OWNER,V_TABLE_NAME,V_GRANTOR,V_PRIVILEGE,V_OBJECT_TYPE,V_GRANT_REVOKE_GBN,V_MSG);
              COMMIT;          
        END;
        

    END LOOP;


EXCEPTION
    WHEN OTHERS THEN
        DBMS_OUTPUT.PUT_LINE('ERR MESSAGE : ' || SQLERRM);

END;
/

3.4 Procedure 사용자 정의 예외 예제[편집]

입력한 부서의 사원이 5명보다 적으면 사용자 정의 예외가 발생하는 예제 입니다.

SQL>CREATE OR REPLACE PROCEDURE User_Exception 
        (v_deptno IN emp.deptno%type ) 
      IS

       -- 예외의 이름을 선언
       user_define_error EXCEPTION;     -- STEP 1
       cnt     NUMBER;

     BEGIN

       DBMS_OUTPUT.ENABLE;   

       SELECT COUNT(empno) 
       INTO cnt
       FROM emp
       WHERE deptno = v_deptno;

       IF cnt < 5 THEN
         -- RAISE문을 사용하여 직접적으로 예외를 발생시킨다
          RAISE user_define_error;         -- STEP 2
       END IF;

      EXCEPTION
        -- 예외가 발생할 경우 해당 예외를 참조한다.
       WHEN user_define_error THEN      -- STEP 3
           RAISE_APPLICATION_ERROR(-20001, '부서에 사원이 몇명 안되네요..');

   END; 
  /

4 오라클 프로시져 함수 목록[편집]

4.1 저장프로시저 명세[편집]

4.1.1 (1) 저장프로시저 정보[편집]

SELECT OBJECT_ID, OBJECT_TYPE, OBJECT_NAME, 
TO_CHAR(CREATED, 'YYYY-MM-DD HH24:MI:SS') AS CREATED,
TO_CHAR(LAST_DDL_TIME, 'YYYY-MM-DD HH24:MI:SS') AS LAST_DDL_TIME
FROM SYS.USER_OBJECTS
WHERE OBJECT_TYPE = 'PROCEDURE'
ORDER BY OBJECT_NAME ASC;

4.1.2 (2) 저장프로시저 파라미터 정보[편집]

SELECT a.OBJECT_ID, a.OBJECT_TYPE, a.OBJECT_NAME, b.SEQUENCE, b.ARGUMENT_NAME, b.DATA_TYPE
, (CASE b.DATA_TYPE WHEN 'NUMBER' THEN TO_CHAR(b.DATA_LENGTH) WHEN 'DATE' THEN ' ' ELSE
TO_CHAR(b.DATA_LENGTH) END) AS DATA_LENGTH
, b.DATA_TYPE ||
 (CASE b.DATA_TYPE 
  WHEN 'NUMBER' THEN '(' || TO_CHAR(b.DATA_LENGTH) || ',' || TO_CHAR(b.DATA_PRECISION) || ')'
  WHEN 'DATE' THEN ' ' 
  ELSE '(' || b.DATA_LENGTH || ')' 
  END) DATA_TYPE_DESC
, b.IN_OUT 
FROM SYS.USER_OBJECTS a, SYS.USER_ARGUMENTS b
WHERE a.OBJECT_TYPE = 'PROCEDURE' AND a.OBJECT_ID = b.OBJECT_ID
ORDER BY a.OBJECT_NAME ASC, b.SEQUENCE ASC;

4.1.3 (3) 저장프로시저 내용[편집]

SELECT TEXT FROM SYS.USER_SOURCE WHERE TYPE = 'PROCEDURE' AND NAME = '[저장프로시저명]';
5. 사용자정의함수 명세
(1) 사용자정의함수 정보 보기
SELECT OBJECT_ID, OBJECT_TYPE, OBJECT_NAME, 
TO_CHAR(CREATED, 'YYYY-MM-DD HH24:MI:SS') AS CREATED,
TO_CHAR(LAST_DDL_TIME, 'YYYY-MM-DD HH24:MI:SS') AS LAST_DDL_TIME
FROM SYS.USER_OBJECTS
WHERE OBJECT_TYPE = 'FUNCTION'
ORDER BY OBJECT_NAME ASC;


4.2 사용자정의함수 명세[편집]

4.2.1 (1) 사용자정의함수 파라미터[편집]

SELECT a.OBJECT_ID, a.OBJECT_TYPE, a.OBJECT_NAME, b.SEQUENCE, b.ARGUMENT_NAME, b.DATA_TYPE
     , (CASE b.DATA_TYPE WHEN 'NUMBER' THEN TO_CHAR(b.DATA_LENGTH) 
                         WHEN 'DATE' THEN ' ' 
                         ELSE TO_CHAR(b.DATA_LENGTH) END
       ) AS DATA_LENGTH
     , b.DATA_TYPE || (CASE b.DATA_TYPE WHEN 'NUMBER' THEN '(' || TO_CHAR(b.DATA_LENGTH) || ',' || TO_CHAR(b.DATA_PRECISION) || ')'
                                        WHEN 'DATE' THEN ' ' 
                                        ELSE '(' || b.DATA_LENGTH || ')' 
                        END ) DATA_TYPE_DESC
     , b.IN_OUT 
  FROM SYS.USER_OBJECTS   a
     , SYS.USER_ARGUMENTS b
 WHERE a.OBJECT_TYPE = 'FUNCTION' 
   AND a.OBJECT_ID = b.OBJECT_ID
 ORDER BY a.OBJECT_NAME ASC, b.SEQUENCE ASC;

4.2.2 (3) 사용자정의함수 내용[편집]

SELECT TEXT 
  FROM SYS.USER_SOURCE 
 WHERE TYPE = 'FUNCTION' 
   AND NAME = '[사용자정의함수명]';

5 오라클 테이블 복사 프로시져[편집]

5.1 오라클 테이블 간 데이터 복사 프로시져[편집]

5.1.1 sp_dbm_copy[편집]

CREATE OR REPLACE PROCEDURE sp_dbm_copy 
    (  p_tname    in varchar2 default null
     , p_dblink   in varchar2 default null 
     , p_option   in varchar2 default 'I' -- I:insert, R:replace(truncate), A:append
     , p_exec     in number default 0 
     , p_owner    in varchar2 default ''
     , p_srcowner in varchar2 default null 
     , p_srctname in varchar2 default null 
    ) authid current_user
is 
/* =============================================================================
XXX_DBA 계정에 select any table 권한 필요: 없을 경우 다계정에 대해 NO_COLUMN_LIST 발생
create table XXX_DBA.tb_dbm_copy_log 
(
    logdt      date not null,             -- 작업일자
    owner      varchar2 (100) not null,   -- 테이블 소유자
    tname      varchar2 (100) not null,   -- 테이블명
    dblink     varchar2 (100),            -- DB LINK
    sowner     varchar2 (100),            -- 원본 테이블 소유자
    stname     varchar2 (100),            -- 원본 테이블
    coption    varchar2 (10),             -- 옵션(I:Insert, A:Append, R:Truncate and Insert)
    result     varchar2 (5),              -- 결과(S:Sucess, F:Fail)
    rowcount   number,                    -- 건수
    errmsg     varchar2 (300),            -- 에러메시지
    constraint pk_dbm_copy_log primary key (logdt, owner, tname)
);
============================================================================= */
    NO_COLUMN_LIST EXCEPTION; 
    NO_PRIMARYKEY  EXCEPTION; 
    v_tgt_tname varchar2(50) default null; 
    v_tgt_owner varchar2(30) default null; 
    v_src_owner varchar2(30) default null; 
    v_src_tname varchar2(50) default null; 
    v_option    varchar2(2);
     
    type typ_rec_cols is record 
        (colid user_tab_columns.column_id%TYPE, colname all_tab_columns.column_name%TYPE); 
    type typ_tab_cols is table of typ_rec_cols; 
    tab_cols  typ_tab_cols; 
 
    v_dblink    varchar2(100) default ' '; 
    v_sqltxt    long; 
    v_sqlwhere  varchar2(1000);
    v_colist    long; 
    v_msg       long; 
    v_rowcnt    number(10) default 0; 
    V_NL        char(1) default chr(10); 
    V_DELIM       varchar(10) default ','; 
begin 
    if p_tname is null then 
        dbms_output.enable(100); 
        dbms_output.put_line('usage: sp_dbm_copy(          ');
        dbms_output.put_line('          p_tname    => null ');
        dbms_output.put_line('        , p_dblink   => null ');
        dbms_output.put_line('        , p_option   => ''i'' -- I:insert, R:replace(truncate), A:append');
        dbms_output.put_line('        , p_exec     => 0    ');
        dbms_output.put_line('        , p_owner    => null ');
        dbms_output.put_line('        , p_srcowner => null ');
        dbms_output.put_line('        , p_srctname => null)');        
        dbms_output.put_line(' p_option=I:Insert, R:Replace(truncate and insert), A:Append'); 
        return; 
    end if; 

    v_tgt_owner := upper(p_owner); 
    v_tgt_tname := upper(p_tname); 
    v_option    := upper(p_option);
    if (p_srcowner is null) then v_src_owner := v_tgt_owner; else v_src_owner := upper(p_srcowner); end if; 
    if (p_srctname is null) then v_src_tname := v_tgt_tname; else v_src_tname := upper(p_srctname); end if; 
    if (p_dblink is not null) then v_dblink := '@'||p_dblink; end if; 
    ------------------------------------------------------------------
    v_sqltxt := 'INSERT INTO '||v_tgt_owner||'.'||v_tgt_tname ||'('; 
    if p_exec <> 1 then  
        dbms_output.enable(1000);  
        if v_option = 'R' then dbms_output.put_line('truncate table '||v_tgt_owner||'.'||v_tgt_tname||';'); end if; 
        dbms_output.put_line(v_sqltxt);  
    end if; 
    -- column list
    execute immediate  
        'select t.column_id, t.column_name 
         from all_tab_columns t, all_tab_columns'||v_dblink|| ' s '|| 
        ' where t.owner = :1 
            and t.table_name = :2 
            and s.owner = :3 
            and s.table_name = :4 
            and t.column_name=s.column_name 
           order by t.column_id'  
        bulk collect into tab_cols  
        using v_tgt_owner, v_tgt_tname, v_src_owner, v_src_tname; 
    if SQL%ROWCOUNT<1 then raise NO_COLUMN_LIST; end if; 
     
    for i in tab_cols.first..tab_cols.last 
    loop 
        if p_exec=1 then  
            if i=tab_cols.first then 
                v_colist := trim(tab_cols(i).colname); 
            else 
                v_colist := v_colist ||V_NL||V_DELIM|| trim(tab_cols(i).colname); 
            end if; 
        else 
            if i=tab_cols.first then 
                dbms_output.enable(1000); dbms_output.put_line('     '||tab_cols(i).colname);  
            else 
                dbms_output.enable(1000); dbms_output.put_line('    ,'||tab_cols(i).colname);  
            end if; 
        end if; 
    end loop; 
    
    if v_option = 'A' then
        for icur in (select l.column_name, l.position from dba_cons_columns l, dba_constraints c 
                    where l.owner=c.owner and l.constraint_name=c.constraint_name
                      and c.constraint_type='P' 
                      and c.owner=v_tgt_owner and c.table_name=v_tgt_tname
                    order by position)
        loop
            if icur.position = 1 then
                -- v_sqlwhere := icur.column_name;
                v_sqlwhere := 'B.'||icur.column_name||' = A.'||icur.column_name;
            else
                --v_sqlwhere := v_sqlwhere||', '||icur.column_name;
                v_sqlwhere := v_sqlwhere||' AND B.'||icur.column_name||' = A.'||icur.column_name;
            end if;
        end loop;
        if v_sqlwhere is null then raise NO_PRIMARYKEY; end if;
        -- v_sqlwhere := 'WHERE ('||v_sqlwhere||') NOT IN ( SELECT '||v_sqlwhere||' FROM '||v_tgt_owner||'.'||v_tgt_tname||')';
        v_sqlwhere := 'WHERE NOT EXISTS ( SELECT 1 FROM '||v_tgt_owner||'.'||v_tgt_tname||' B WHERE '||v_sqlwhere||')';
    end if;
 
if p_exec=1 then 
       if v_option = 'R' then execute immediate 'truncate table '||v_tgt_owner||'.'||v_tgt_tname; end if; 
 
        v_sqltxt := 'INSERT INTO '||v_tgt_owner||'.'||v_tgt_tname ||'('|| v_colist ||')'||V_NL 
                    ||'SELECT '||v_colist||V_NL||'FROM '||v_src_owner||'.'||v_src_tname||v_dblink||' A '; 
        if v_option = 'A' then  v_sqltxt := v_sqltxt ||chr(10)||v_sqlwhere; end if;

        begin
            execute immediate v_sqltxt; 
            v_rowcnt := to_char(SQL%ROWCOUNT); 
            v_msg := v_tgt_owner||'.'||v_tgt_tname||':(option='||v_option||'):'||lpad(v_rowcnt,10,'.')||' rows from '||v_src_owner||'.'||v_src_tname||v_dblink;
            commit;
            --(--------------------------------------------------------------------------------------------------------
            execute immediate 'insert into XXX_DBA.tb_dbm_copy_log(logdt, owner, tname, dblink, sowner, stname, coption, result, rowcount)'
                      ||' values( sysdate, :1, :2, :3, :4, :5, :6, :7, :8)'
                      using v_tgt_owner, v_tgt_tname, v_dblink, v_src_owner, v_src_tname, v_option, 'S', to_number(v_rowcnt);
            commit;                      
            --)--------------------------------------------------------------------------------------------------------
        exception
            when others then
            rollback;
            --(--------------------------------------------------------------------------------------------------------
            execute immediate 'insert into XXX_DBA.tb_dbm_copy_log(logdt, owner, tname, dblink, sowner, stname, coption, result, errmsg)'
                      ||' values( sysdate, :1, :2, :3, :4, :5, :6, :7, :8)'
                      using v_tgt_owner, v_tgt_tname, v_dblink, v_src_owner, v_src_tname, v_option, 'F', substr(SQLERRM,1,250) ;
            commit;
            --)--------------------------------------------------------------------------------------------------------
            v_msg := 'ERROR : ['|| to_char(SQLCODE) ||']'|| substr(SQLERRM,1,500); 
            dbms_output.enable(100); 
            dbms_output.put_line(v_msg); 
        end;
        dbms_output.enable(2000); 
        dbms_output.put_line(v_msg); 
    else 
        dbms_output.enable(1000); 
        dbms_output.put_line('    )'); 
        dbms_output.put_line('  SELECT '); 
        for i in tab_cols.first..tab_cols.last 
        loop 
            if i=tab_cols.first then 
                dbms_output.enable(1000); dbms_output.put_line('     '||tab_cols(i).colname);  
            else 
                dbms_output.enable(1000); dbms_output.put_line('    ,'||tab_cols(i).colname);  
            end if; 
        end loop; 
        dbms_output.put_line('  FROM '||v_src_owner||'.'||v_src_tname||v_dblink||' A '); 
        if v_option = 'A' then dbms_output.put_line(v_sqlwhere); end if;
        dbms_output.put_line(';'); 
        
    end if; 
     
exception 
    when NO_COLUMN_LIST then 
        v_msg := 'ERROR : [ NO_COLUMN_LIST ] '||v_tgt_owner||'.'||v_tgt_tname; 
        dbms_output.enable(100); 
        dbms_output.put_line(v_msg); 
    when NO_PRIMARYKEY then 
        v_msg := 'ERROR : [ NO_PRIMARYKEY ] '||v_tgt_owner||'.'||v_tgt_tname; 
        dbms_output.enable(100); 
        dbms_output.put_line(v_msg); 
    when others then 
        v_msg := 'ERROR : ['|| to_char(SQLCODE) ||']'|| substr(SQLERRM,1,500); 
        dbms_output.enable(100); 
        dbms_output.put_line(v_msg); 
end;

5.1.2 sp_dbw_copy[편집]

CREATE OR REPLACE PROCEDURE sp_dbw_copy
    (  p_tname    in varchar2 default null
     , p_dblink   in varchar2 default null 
     , p_option   in varchar2 default 'I' -- I,R,DI,M,AI,AX
     , p_exec     in number default 0 
     , p_owner    in varchar2 default null
     , p_srcowner in varchar2 default null 
     , p_srctname in varchar2 default null 
    ) 
is 
    NO_COLUMN_LIST EXCEPTION; 
    NO_PRIMARYKEY  EXCEPTION; 
    v_tgt_tname varchar2(50) default null; 
    v_tgt_owner varchar2(30) default null; 
    v_src_owner varchar2(30) default null; 
    v_src_tname varchar2(50) default null; 
    v_option    varchar2(2);
     
    type typ_rec_cols is record 
        (colid user_tab_columns.column_id%TYPE, colname all_tab_cols.column_name%TYPE, pno number); 
    type typ_tab_cols is table of typ_rec_cols; 
    
    tab_cols          typ_tab_cols; 
    v_dblink          varchar2(100)  default ' ';
    v_sql_select      varchar2(1000) default ' ';
    v_sql_dataRemove  varchar2(1000) default ' ';
    v_sqlwhere        varchar2(4000);
    v_sql_copyData    long; 
    v_colist          long; 
    v_keycols         varchar2(4000);
    v_merge_join      varchar2(4000);
    v_merge_update    varchar2(4000);
    v_merge_insert    varchar2(4000);
    v_msg             varchar(4000); 
    v_rowcnt          number(15)  default 0; 
    v_start_dtm       date; 
    v_secs            varchar2(10);
    V_NL              char(1)     default chr(10); /* new line */
    V_TAB             char(4)     default '    ';  /* tab      */
    V_DELIM           varchar2(10) default ',';     /* delimter */
begin 
    v_tgt_owner := upper(p_owner); 
    v_tgt_tname := upper(p_tname); 
    v_option    := upper(p_option);

    if p_tname is null or v_option not in ('I','R','DI','M','AI','AX')
    then 
        v_msg := 
             V_NL||'usage: '
           ||V_NL||' exec sp_dbw_copy('
           ||V_NL||'          p_tname    => ''table_name'' '
           ||V_NL||'        , p_dblink   => null '
           ||V_NL||'        , p_option   => ''i'' -- I/R/DI/M/AI/AX '
           ||V_NL||'        , p_exec     => 0 '
           ||V_NL||'        , p_owner    => '''' '
           ||V_NL||'        , p_srcowner => null '
           ||V_NL||'        , p_srctname => null); '
           ||V_NL||'   p_option=> I:Insert, R:Replace(truncate and insert), DI:Delete Insert '
           ||V_NL||'      , M:Merge, AI:Append(not In), AX:Append(not eXists) '
        ;

        dbms_output.enable(500); 
        dbms_output.put_line(v_msg);
        return; 
    end if; 

    if (p_srcowner is null) then v_src_owner := v_tgt_owner; else v_src_owner := upper(p_srcowner); end if; 
    if (p_srctname is null) then v_src_tname := v_tgt_tname; else v_src_tname := upper(p_srctname); end if; 
    if (p_dblink is not null) then v_dblink := '@'||p_dblink; end if; 
    if v_option = 'R'  then
        v_sql_dataRemove := 'truncate table '||v_tgt_owner||'.'||v_tgt_tname ;
    elsif v_option = 'DI' then 
        v_sql_dataRemove := 'delete from '||v_tgt_owner||'.'||v_tgt_tname; 
    end if; 
    -- column list
    v_sql_select := 'select tgt.column_id, tgt.column_name, p.position pno '
          ||V_NL||'  from all_tab_cols tgt '
          ||V_NL||'       join all_tab_cols'||v_dblink||' src '
          ||V_NL||'         on (src.column_name = tgt.column_name) '
          ||V_NL||'       left join all_constraints c  '
          ||V_NL||'         on (c.owner = tgt.owner and c.table_name = tgt.table_name and c.constraint_type = ''P'') '
          ||V_NL||'       left join all_cons_columns p  '
          ||V_NL||'         on (p.owner = c.owner and p.table_name = c.table_name and p.constraint_name = c.constraint_name '
          ||V_NL||'        and p.owner = tgt.owner and p.table_name = tgt.table_name and p.column_name = tgt.column_name) '
          ||V_NL||' where tgt.owner = :1 '
          ||V_NL||'   and tgt.table_name = :2 '
          ||V_NL||'   and src.owner = :3 '
          ||V_NL||'   and src.table_name = :4 '
          ||V_NL||'   and tgt.virtual_column <> ''YES'' '
          ||V_NL||' order by tgt.column_id  '
    ;

    execute immediate v_sql_select         
      bulk collect into tab_cols  
      using v_tgt_owner, v_tgt_tname, v_src_owner, v_src_tname; 
    if SQL%ROWCOUNT < 1 then 
      dbms_output.enable(10000);
      dbms_output.put_line(v_sql_select);
      raise NO_COLUMN_LIST; 
    end if; 
     
    for i in tab_cols.first..tab_cols.last 
    loop 
        /* insert column list */
        if i=tab_cols.first then 
            v_colist := V_TAB||'  SRC.'||trim(tab_cols(i).colname);
        else 
            v_colist := v_colist ||V_NL||V_TAB||V_DELIM||' SRC.'|| trim(tab_cols(i).colname); 
        end if; 
        
        /* merge on clause, update set clause */
        if tab_cols(i).pno is not null then
            v_merge_join := v_merge_join||' AND SRC.'||tab_cols(i).colname||' = TGT.'||tab_cols(i).colname;
            v_keycols := v_keycols ||', '||tab_cols(i).colname;
        else 
            v_merge_update := v_merge_update||', TGT.'||tab_cols(i).colname||' = SRC.'||tab_cols(i).colname||V_NL||V_TAB;
        end if;
    end loop;

    v_merge_join := substr(v_merge_join, 5, 10000);
    v_keycols := substr(v_keycols, 2,10000);
    v_merge_update := substr(v_merge_update, 2, 10000);
    
    if v_option in ('M','AI', 'AX') then
        if v_merge_join is null then raise NO_PRIMARYKEY; end if;
        if v_option = 'AI' then 
            v_sqlwhere := 'WHERE ('||v_keycols||') NOT IN ( SELECT '||v_keycols||' FROM '||v_tgt_owner||'.'||v_tgt_tname||')';
        elsif v_option = 'AX' then
            v_sqlwhere := 'WHERE NOT EXISTS ( SELECT 1 FROM '||v_tgt_owner||'.'||v_tgt_tname||' TGT WHERE '||v_merge_join||')';
        end if;
        
    end if;

    if v_option = 'M' then
        v_sql_copyData := 'MERGE INTO '||v_tgt_owner||'.'||v_tgt_tname||' TGT '
                  ||V_NL||'USING '||v_src_owner||'.'||v_src_tname||v_dblink||' SRC '
                  ||V_NL||'   ON ('||v_merge_join||' ) '
                  ||V_NL||'WHEN MATCHED THEN UPDATE '
                  ||V_NL||'     SET '
                  ||V_NL||'     '||v_merge_update
                  ||V_NL||'WHEN NOT MATCHED THEN '
                  ||V_NL||'     INSERT ('
                  ||V_NL||      replace(replace(v_colist, ' SRC.',' '), V_TAB, V_TAB||V_TAB)||') '
                  ||V_NL||'     VALUES ('
                  ||V_NL||replace(v_colist, V_TAB, V_TAB||V_TAB)||')'
        ;
    else
        v_sql_copyData := 'INSERT '|| case when v_option in ('R','AI','AX') then '/*+ APPEND */' end 
                  ||V_NL||'  INTO '||v_tgt_owner||'.'||v_tgt_tname ||'('
                  ||V_NL|| replace(v_colist , ' SRC.', ' ')
                  ||V_NL||')'
                  ||V_NL||'SELECT /*+ PARALLEL(SRC 1) */'
                  ||V_NL||v_colist
                  ||V_NL||'FROM '||v_src_owner||'.'||v_src_tname||v_dblink||' SRC '; 
        if v_option IN ('AI', 'AX') then  v_sql_copyData := v_sql_copyData ||chr(10)||v_sqlwhere; end if;
    end if;
 

    if p_exec=1 then 
        begin
            select sysdate into v_start_dtm from dual;
            if v_option in ('R','DI') then execute immediate v_sql_dataRemove; end if; 
            execute immediate v_sql_copyData; 
            v_rowcnt := SQL%ROWCOUNT; 
            v_msg := v_tgt_owner||'.'||v_tgt_tname||':(option='||v_option||'):'||lpad(to_char(v_rowcnt, 'fm999,999,999,999'),15,'.')||' rows from '||v_src_owner||'.'||v_src_tname||v_dblink;
            commit;
            --(--------------------------------------------------------------------------------------------------------
            select to_char(trunc(tm /3600), 'fm00')
                   ||':'||to_char(mod(trunc(tm/60),60),'fm00')
                   ||':'||to_char(mod(tm,60), 'fm00')
            into v_secs 
            from (select (sysdate - v_start_dtm) * 24 * 60 * 60 tm from dual)
            ;

            insert into XXX_DBA.tb_dbw_copy_log
                   (logid, owner, table_name, result, rowcount
                  , src_owner, src_table_name, dblink, copyopt, start_time, end_time, errmsg)
             values(sq_dbw_copy_log.nextval, v_tgt_owner, v_tgt_tname, 'S', to_number(v_rowcnt)
                  , v_src_owner, v_src_tname, v_dblink, v_option, v_start_dtm, sysdate, v_secs);
            commit;                      
            --)--------------------------------------------------------------------------------------------------------
        exception when others then
            v_msg := SQLERRM;
            rollback;
            --(--------------------------------------------------------------------------------------------------------
            insert into XXX_DBA.tb_dbw_copy_log
                   (logid, owner, table_name, result, errmsg
                  , src_owner, src_table_name, dblink, copyopt, start_time, end_time)
             values(sq_dbw_copy_log.nextval, v_tgt_owner, v_tgt_tname, 'S', substr(v_msg, 1, 250)
                  , v_src_owner, v_src_tname, v_dblink, v_option, v_start_dtm, sysdate);
            commit;   
            --)--------------------------------------------------------------------------------------------------------
            v_msg := 'ERROR : '|| substr(v_msg,1,500); 
            dbms_output.enable(1000); 
            dbms_output.put_line(v_msg); 
        end;
        dbms_output.enable(1000); 
        dbms_output.put_line(v_msg); 
    else 
        dbms_output.enable(10000); 
        dbms_output.put_line(''); 
        if v_option in ('R','DI') then dbms_output.put_line(v_sql_dataRemove||';'); end if; 
        dbms_output.put_line(v_sql_copyData||';');
    end if; 
     
exception 
    when NO_COLUMN_LIST then 
        v_msg := 'ERROR : [ NO_COLUMN_LIST ] '||v_tgt_owner||'.'||v_tgt_tname; 
        dbms_output.enable(100); 
        dbms_output.put_line(v_msg); 
    when NO_PRIMARYKEY then 
        v_msg := 'ERROR : [ NO_PRIMARYKEY ] '||v_tgt_owner||'.'||v_tgt_tname; 
        dbms_output.enable(100); 
        dbms_output.put_line(v_msg); 
    when others then 
        v_msg := 'ERROR : ['|| to_char(SQLCODE) ||']'|| substr(SQLERRM,1,500); 
        dbms_output.enable(100); 
        dbms_output.put_line(v_msg); 
end;
/

6 오라클 FK 생성 프로시져[편집]

== 사용법 ==

6.1 FK 생성[편집]

BEGIN 
    SP_DBM_FK_RECOVER(p_owner => 'XXX',p_exec => 1);
END;

6.2 실패한 FK 생성[편집]

BEGIN 
    SP_DBM_FK_RECOVER(p_owner => 'XXX',p_exec => 1, p_status=>'DISABLE');
END;

6.3 FK 생성 결과 확인[편집]

-- 성공

SELECT * 
   -- 'ALTER TABLE '||OWNER||'.'||TABLE_NAME|| ' ENABLE CONSTRAINT '||CONSTRAINT_NAME||';' 
  FROM DBA_CONSTRAINTS A
 WHERE OWNER = 'XXX'
  AND A.CONSTRAINT_TYPE ='R'
  AND STATUS <> 'ENABLED'
  ;

6.4 실패한 FK 재생성[편집]

-- 실패

SELECT 'ALTER TABLE '||OWNER||'.'||TABLE_NAME|| ' ENABLE CONSTRAINT '||CONSTRAINT_NAME||';' 
  FROM DBA_CONSTRAINTS A
 WHERE OWNER = 'XXX'
  AND A.CONSTRAINT_TYPE ='R'
  AND STATUS <> 'ENABLED'
  ;

6.5 FK 생성 프로시져[편집]

CREATE OR REPLACE PROCEDURE XXX_MIG.sp_dbm_fk_recover 
    (  p_owner in varchar2 default null
     , p_tname in varchar2 default '%' 
     , p_fkname in varchar2 default '%' 
     , p_status in varchar2 default null 
     , p_exec in number default 0) 
/*==============================================================================*
  SP NAME : SP_DBM_FK_RECOVER
  SP DESC.: 
         - 테이블에 저장된 정보를 사용하여 FK를 생성한다.
         - set serverout on이 설정되어야 실행 결과 또는 스크립트 출력을 확인할 수 있다.
    PARAM :
         p_owner : 테이블 소유자
         p_tname : 테이블 명칭. like 조건으로 검색되며 기본값은 %로 소유자의 모든 테이블이다.
         p_fkname: FK 명칭. like 조건으로 검색되며 기본값은 %로 해당 테이블의 모든 FK이다.
         p_status: 생성시 FK 옵션을 입력한다. ENABLE/DISABLE/ENABLE NOVALIDATE
         p_exec  : 1 : FK를 생성한다.
                   0 : FK를 생성하는 스크립트를 출력한다.
*==============================================================================*/
is 
    v_sqltxt    varchar2(4000); 
    v_owner     varchar2(100); 
    v_tname     varchar2(100); 
    v_fkname    varchar2(100); 
    v_cols      varchar2(2000); 
    v_rcols     varchar2(2000); 
    v_status    varchar2(20); 
begin 
    v_owner  := upper(p_owner); 
    v_tname  := upper(p_tname); 
    v_fkname := upper(p_fkname); 
    v_status := upper(p_status); 
 
    if v_owner is null then 
        dbms_output.enable(100); 
        dbms_output.put_line('usage: sp_dbm_fk_recover(p_owner=>, p_tname=>''%'', p_fkname=>''%'', p_status=>null, p_exec=>0)'); 
        dbms_output.put_line('       p_status=> enable, disable, enable novalidate'); 
        return; 
    end if; 
     
    for icur in (select a.owner, a.tname, a.fkname, a.cols, a.rowner, a.rtname, a.rcols 
                      , a.drule, nvl(v_status, a.status) status 
                 from XXX_MIG.dbm_fk a 
                 where a.owner=v_owner and a.tname like v_tname and a.fkname like v_fkname 
                   and not exists(select 1 from dba_constraints b  -- all_constraints b  
                                  where a.owner=b.owner and b.table_name=a.tname and b.constraint_name=a.fkname) 
                 ) 
    loop 
        begin
            v_sqltxt := 'ALTER TABLE '||icur.owner||'.'||icur.tname||' ADD CONSTRAINT '||icur.fkname 
                        ||' FOREIGN KEY ('||icur.cols||') REFERENCES '||icur.rowner||'.'||icur.rtname 
                        ||'('||icur.rcols||') '||icur.drule||' '||icur.status; 
     
            dbms_output.enable(500); 
            if p_exec = 1 then 
                execute immediate v_sqltxt; 
                dbms_output.put_line(v_sqltxt||';  -- done'); 
            else 
                dbms_output.put_line(v_sqltxt||';'); 
            end if; 
        exception when others then
            dbms_output.enable(500); 
            dbms_output.put_line('Error at: '||v_sqltxt);
            dbms_output.put_line('-->'||SQLERRM); 
        end;
    end loop; 
end;
/

6.6 관련 테이블 생성[편집]

CREATE TABLE XXX_MIG.DBM_FK
(
  OWNER   VARCHAR2(100 BYTE)                    NOT NULL,
  TNAME   VARCHAR2(100 BYTE)                    NOT NULL,
  FKNAME  VARCHAR2(100 BYTE)                    NOT NULL,
  COLS    VARCHAR2(2000 BYTE)                   NOT NULL,
  ROWNER  VARCHAR2(100 BYTE)                    NOT NULL,
  RTNAME  VARCHAR2(100 BYTE)                    NOT NULL,
  RCOLS   VARCHAR2(2000 BYTE)                   NOT NULL,
  DRULE   VARCHAR2(20 BYTE),
  STATUS  VARCHAR2(20 BYTE)                     NOT NULL,
  TMPCHG  VARCHAR2(20 BYTE),
  LOGDT   DATE                                  DEFAULT sysdate
)

CREATE UNIQUE INDEX XXX_MIG.PK_DBM_FK ON XXX_MIG.DBM_FK
(OWNER, TNAME, FKNAME)

ALTER TABLE XXX_MIG.DBM_FK ADD (
  CONSTRAINT PK_DBM_FK
  PRIMARY KEY
  (OWNER, TNAME, FKNAME)
  USING INDEX XXX_MIG.PK_DBM_FK
  ENABLE VALIDATE);

6.7 관련 트리거[편집]

CREATE OR REPLACE TRIGGER XXX_MIG.TR_DBM_FK_DEL
    after delete on XXX_MIG.dbm_fk
    for each row
declare
    v_nval number(1) default null;
begin
    select 1 into v_nval from dbm_fk_hist
    where owner = :old.owner and tname = :old.tname and fkname = :old.fkname
      and cols = :old.cols and rowner = :old.rowner and rtname = :old.rtname and rcols = :old.rcols
      and rownum=1;
exception
    when NO_DATA_FOUND then
        insert into dbm_fk_hist
        values(:old.owner, :old.tname, :old.fkname, :old.cols, :old.rowner, :old.rtname, :old.rcols
             , :old.drule, :old.status, :old.tmpchg, :old.logdt);
    when others then
        --raise; --SQLCODE, SQLERRM
        RAISE_APPLICATION_ERROR(-20001, '('||to_char(SQLCODE)||':'||SQLERRM||')');
end;
/

7 오라클 프로시져 패러럴 실행[편집]

SET SERVEROUTPUT ON
DECLARE
  l_task_name user_parallel_execute_tasks.task_name%TYPE;
  l_sql_stmt user_parallel_execute_tasks.sql_stmt%TYPE;
  
BEGIN
  l_task_name := 'px_update';
  l_sql_stmt := 'UPDATE t SET col = expr WHERE rowid BETWEEN :start_id AND :end_id';

  dbms_parallel_execute.create_task(task_name => l_task_name);

  dbms_parallel_execute.create_chunks_by_rowid(
    task_name   => l_task_name,
    table_owner => user,
    table_name  => 'T',
    by_row      => FALSE,
    chunk_size  => 128
  );

  dbms_parallel_execute.run_task(
    task_name      => l_task_name,
    sql_stmt       => l_sql_stmt,
    language_flag  => dbms_sql.native,
    parallel_level => 4
  );

  WHILE (dbms_parallel_execute.task_status(task_name => l_task_name)
           NOT IN (
             dbms_parallel_execute.chunking_failed,
             dbms_parallel_execute.finished,
             dbms_parallel_execute.finished_with_error,
             dbms_parallel_execute.crashed
           ))
  LOOP
    dbms_lock.sleep(1);
  END LOOP;

  CASE dbms_parallel_execute.task_status(task_name => l_task_name)
    WHEN dbms_parallel_execute.chunking_failed THEN dbms_output.put_line('chunking_failed');
    WHEN dbms_parallel_execute.finished THEN dbms_output.put_line('finished');
    WHEN dbms_parallel_execute.finished_with_error THEN dbms_output.put_line('finished_with_error');
    WHEN dbms_parallel_execute.crashed THEN dbms_output.put_line('crashed');
  END CASE;

  dbms_parallel_execute.drop_task(task_name => l_task_name);
END;
/