Package(패키지) - 2

    패키지 서브프로그램

    패키지 서브프로그램은 패키지 내부에 선언된 함수와 프로시저를 말한다. 패키지 변수와 마찬가지로 패키지 서브프로그램은 공용과 전용으로 구분된다.

    차이점 공용 서브프로그램 전용 서브프로그램
    선언 위치 패키지 명세 패키지 본체
    패키지 외부 PL/SQL에서 참조 가능 불가능
    패키지 외부 SQL에서 참조 프로시저: 불가능
    함수: 가능
    불가능
    패키지 내부 서브프로그램에서 참조 가능 가능
    주 사용자 외부 프로그램 공용 또는 전용 서브프로그램
    -- 패키지 서브프로그램은 다른 PL/SQL 프로그램에서 참조 가능
    DECLARE
    	v_var NUMBER;
    BEGIN
    	v_var := pkg_emp.get_wage(7788);
        DBMS_OUTPUT_PUT_LINE('last wage = '||pkg_emp.v_last_wage);
    END;

    패키지 변수와 달리 패키지 함수는 SQL문에서도 참조될 수 있다.

    SCOTT> SELECT pkg_emp.get_wage(empno) FROM emp WHERE empno = 7788;
    
    PKG_EMP.GET_WAGE(EMPNO)
    -----------------------
    				  3000

     

    패키지 서브프로그램 작성 시 주의할 점은 서브프로그램이 사용되기 전에 반드시 먼저 선언되어야 한다는 점이다. 

    이 때문에 패키지 본체에서 저의되는 전용 서브프로그램의 순서에는 주의가 필요하다.

    CREATE OR REPLACE PACKAGE pkg_xyz
    IS
    	-- 빈 패키지 명세
    END;
    CREATE OR REPLACE PACKAGE BODY pkg_xyz
    IS
    	-- 전용 서브프로그램 정의
        -- 두 서브프로그램 x와 y는 패키지 명세에는 정의되지 않았다
        PROCEDURE y IS
        BEGIN
        	x();	-- x가 사용되기 전에 선언되지 않았으므로 오류 발생
        END;
        
        PROCEDURE x IS
        BEGIN
        	NULL;
        END;
       END;

    서브프로그램 y가 정의되는 위치에서 전용 프로시저 x가 전용 프로시저 y안에서 사용되었으나, 아직 프로시저 x에 대한 정보를 알지 못한다. 참고로 공용 서브프로그램은 이런 문제가 없다. 왜냐면 공용 서브프로그램은 패키지 명세에서 선언되었기 때문에 패키지 본체의 컴파일 시에는 모든 공용 서브프로그램의 정보를 이미 알고 있어서 오류가 발생하지 않는다. 

    CREATE OR REPLACE PACKAGE BODY pkg_xyz
    IS
    	-- 전용 서브프로그램 정의
        -- 두 서브프로그램 x와 y는 패키지 명세에는 정의되지 않았다.
        PROCEDURE x IS
        BEGIN
        	NULL;
        END;
        
        PROCEDURE y IS
        BEGIN
        	x();	-- 순서상 X가 먼저 선언되었으므로 정상
        END;
       END;

    전용 프로시저 x가 먼저 선언 및 정의되고 뒤에 오는 전용 프로시저 y에서 사용되었으므로 오류가 발생하지 않는다.

     

    패키지 커서

    패키지 명세에 커서를 선언할 수 있는데 일반적으로 명세에 커서를 선언하고 본체에 실제 커서를 정의한다.

    커서의 선언과 정의를 분리함으로써 커서 명세의 변경 없이 커서 정의를 변경할 수 있는 장점이 생긴다.

    커서를 선언만 하고 정의를 패키지 본체로 미룰 때에는 RETURN 키워드를 사용하여 커서의 반환타입을 지정해 주어야 한다. 패키지의 사용지는 해당 커서의 선언만 알더라도 사용에는 문제 없다.,

    CREATE OR REPLACE PACKAGE pkg_emp
    IS
    	CURSOR c_emp_cur RETURN emp%ROWTYPE;
    END;
    CREATE OR REPLACE PACKAGE BODY pkg_emp
    IS
    	CURSOR c_emp_cur RETURN emp%ROWTYPE IS
        	SELECT *
              FROM emp
             ORDER BY ename;
    END;
    -- 다른 프로그램에서 패키지 커서 사용
    BEGIN
    	FOR cur IN pkg_emp.c_emp_cur
        LOOP
        	DBMS_OUTPUT_PUT_LINE(cur.ename);
        END LOOP;
    END;

    패키지 명세에서 커서를 선언만 하고 정의를 본체로 미룰 경우 커서에 RETURN 타입을 명시해야 하는 데 패키지 커서의 RETURN 타입으로 지정할 수 있는 반환형은 다음 세 가지 중의 하나다.

     

    1. 테이블%ROWTYPE, 뷰%ROWTYPE, 커서%ROWTYPE, 커서변수%ROWTYPE과 같은 %ROWTYPE 사용

    CURSOR c_emp RETURN emp%ROWTYPE;

    2. 레코드변수%TYPE

    v_emp emp%ROWTYPE;
    CURSOR c_emp RETURN v_emp%TYPE;

    3. 레코드 타입

    TYPE emp_type IS RECORD
    	( EMPNO			NUMBER(4),
          ENAME			VARCHAR2(10));
    CURSOR c_emp RETURN emp_type;

    SERIALLY_REUSABLE 패키지

    일반적으로 패키지의 생명 주기는 세션과 밀접하게 연관되어 있다. 그 이유는 패키지가 서버 프로세스의 PGA 메모리 영역의 일부인 UGA(User Global Area)라는 영역에 생성되기 때문이다.(Dedicated 서버의 경우이며 Shared 서버의 경우는 SGA에 생성된다.) UGA는 세션별로 할당되며 세션의 생명 주기를 따르기 때문에 세션이 유지되는 동안에는 특별한 일이 없으면 패키지의 상태가 보존되는 것이다. 전체 데이터베이스 관점에서 봤을 때 이 방식에서의 문제점 중 하나는 세션별로 패키지가 메모리를 차지하기 때문에 메모리의 낭비가 크다는 것이다. 이 문제를 해결하기 위해서 패키지에 SERIALLY_REUSABLE 속성을 지정할 수 있다.

     

    SERIALLY_REUSABLE 패키지는 세션별로 사용되는 UGA가 아니라 공유 메모리인 SGA의 풀(pool)d에 생성된다.

    SERIALLY_REUSABLE 패키지가 사용될 때는 이 풀에서 패키지를 할당받아 실행하고, 실행이 끝나면 이 풀로 되돌려진다.

    항목 일반 패키지 SERIALLY_REUSABLE 패키지
    생성 장소 서버 프로세스의 UGA SGA
    생명 주기 세션의 생명주기를 따름 패키지 호출 단위
    초기화 시점 세션에서 최초 참조 시 패키지 호출 시마다 초기화
    패키지 변수 값 세션 종료나 패키지 Unload 시까지 유지 패키지 호출 시마다 초기화
    패키지 커서 상태 세션 종료나 패키지 Unload 시까지 유지 패키지 호출 종료 시 닫힘
    매모리 사용량 많음 적음
    -- 일반 패키지와 SERIALLY_REUSABLE 패키지의 차이
    SCOTT> REM 일반 패키지
    SCOTT> CREATE OR REPLACE PACKAGE normal_pkg IS
    		v_n NUMBER := 0;	-- 0으로 초기화
          END;
         /
         
     패키지가 생성되었습니다.
     
     SCOTT>
     SCOTT> REM SERIALLY_REUSABLE 패키지
     SCOTT> CREATE OR REPLACE PACKAGE sr_pkg IS
     		PRAGMA SERRIALLY_REUSABLE;
            v_n NUMBER := 0;	-- 0으로 초기화
           END;
          /
        
    패키지가 생성되었습니다.
    
    SCOTT>
    SCOTT> REM 두 패키지 변수의 값을 동일하게 10으로 변경
    SCOTT> BEGIN
    		 normal_pkg.v_n := 10;	-- 일반 패키지 변수 값을 10으로 변경
             sr_pkg.v_n	:= 10;		-- SERIALLY_REUSABLE 패키지 변수 값을 10으로 변경
           END;
          /
          
    PL/SQL 처리가 정상적으로 완료되었습니다.
    
    SCOTT>
    SCOTT> REM 재호출 시 패키지 변수의 값의 차이를 출력
    SCOTT> BEGIN
    		DBMS_OUTPUT_PUT_LINE('normal_pkg.v_n = ' || normal_pkg.v_n);
       		DBMS_OUTPUT_PUT_LINE('sr_pkg.v_n = ' || sr_pkg.v_n);
           END;
          /
     
     normal_pkg.v_n = 10
     sr_pkg.v_n = 0

    일반 패키지의 변수가 재참조될 때는 이전 참조 시에 변경한 값 10이 유지되었다. 반면에 SERIALLY_REUSABLE 패키지의 변수 값은 초기화되어 0으로 되돌아갔음을 알 수 있다. 이런 동작 방식은 패키지 변수 뿐만 아니라 패키지 커서에서도 마찬가지다. 두 패키지 유형의 특성을 잘 알고 사용해야 부작용이 없을 것이다.

     

    SERIALLY_REUSABLE 패키지는 다음의 제약 사항을 가진다.

    • 데이터베이스 트리거에는 SERIALLY_REUSABLE 패키지를 사용할 수 없다.
    • SQL문에서 호출되는 서브함수를 가진 패키지는 SERIALLY_REUSABLE 속성을 사용할 수 없다.

    'DATABASE > SQL, PL-SQL' 카테고리의 다른 글

    Trigger(트리거)  (0) 2021.04.14
    서브프로그램의 다양한 기능들 - 매개변수  (0) 2021.04.01
    Package(패키지) - 1  (0) 2021.03.24
    저장 프로시저  (0) 2021.03.16
    Stored Function(저장 함수)  (0) 2021.03.09

    댓글