Package(패키지) - 1

    PL/SQL 저장 패키지(Stored Package)는 다양한 PL/SQL 요소들을 묶어 모듈화하여 하나의 프로그램 레벨로 제공할 수 있게 한다. 저장 함수나 저장 프로시저와 마찬가지로 패키지도 고유한 이름을 가지고 데이터베이스에 저장되며, 이름을 식별자로 사용하여 반복적으로 재사용될 수 있다.

    패키지는 내부에 타입, 상수, 변수, 커서. 예외, 서브프로그램(함수와 프로시저) 등 PL/SQL에서 지원하는 대부분의 요소들을 포함할 수 있으며 패키지의 사용자가 이들을 참조할 수 있도록 한다.

    패키지는 상속과 기본 생성자를 사용할 수 없어서 완전한 객체 지향 프로그래밍을 지원하지는 않는다.

    완전한 객체 지향 프로그래밍은 객체 타입에서 지원한다.

    패키지 구조

    패키지는 패키지 명세(Specification)와 패키지 본체(Body)로 나뉘어 작성된다. 패키지 명세는 패키지 사용자에게 공개되는 타입, 상수, 변수, 커서, 예외, 서브프로그램 등을 선언하는 오브젝트로 패키지를 사용하기 위해서는 반드시 작성되어야 한다.

    패키지 명세의 구조

    CREATE OR REPLACE PACKAGE 패키지명
    IS -- IS 대신 AS를 사용해도 동일
    	-- 패키지 명세에는 아래와 같은 공용(public) 항목들이 선언될 수 있다.
        -- 항목들의 순서는 관계가 없으며 서로 섞여도 무관하다.
        공용타입 선언
        
        공용 상수 선언
        
        공용 변수 선언
        
        공용 커서 선언
        
        공용 예외 선언
        
        공용 서브프로그램 선언
    END;
    

    패키지 본체는 패키지 명세에서 선언만 하고 실체를 정의하지 않은 서브프로그램과 커서를 실제로 정의한다. 그리고 패키지의 외부에 노출시키지 않고 내부적으로만 사용할 목적(private)의 타입, 상수, 변수, 예외, 커서, 서브프로그램을 추가적으로 선언하고 정의할 수 있다. 패키지 명세에 서브프로그램이나 커서가 선언되지 않았다면 패키지 본체는 불필요할 수도 있다. 즉 패키지 명세는 필수이고, 본체는 선택이다.

    패키지 본체의 구조는 패키지 명세와 달리 패키지 본체에서는 지켜야 할 배치 순서가 있다.

    전용이건 공용이건 상관없이 서브프로그램 정의가 패키지 본체 선언부의 마지막에 놓여야 한다. 서브프로그램 정의가 나타난 후에 서브프로그램이 아닌 다른 요소가 나타나면 컴파일 시 오류가 나타난다. 전용 서브프로그램과 공용 서브프로그램의 순서는 상관없다. 서브프로그램을 제외한 다른 항목들의 순서도 제한이 없다.

    CREATE OR REPLACE PACKAGE BODY 패키지명
    IS 	-- IS 대신 AS를 사용해도 동일
    /* 패키지 본체 선언부 */
    -- 패키지 본체에는 내부적으로만 사용할 아래와 같은 전용 항목들의 선언과
    -- 공용 커서의 정의부가 나타남
    -- 다음 일곱가지 유형의 항목들의 순서는 관계가 없으며 서로 섞여도 무관
    전용 타입 선언
    
    전용 상수 선언
    
    전용 변수 선언
    
    전용 커서 선언 및 정의
    
    전용 예외 선언
    
    전용 서브프로그램 선언
    
    공용 커서 정의
    
    -- 서브프로그램 정의는 패키지 초기화부 바로 위에 나타나야 한다
    -- 전용 서브프로그램과 공용 서브프로그램 정의의 순서는 상관없다.
    전용 서브프로그램 정의
    
    공용 서브프로그램 정의
    
    -- 패키지 본체의 마지막에는 패키지 인스턴스가 생성될 때(처음 참조될 떄) 실행되어 
    -- 패키지 변수를 초기화할 수 있는 패키지 초기화부가 나타날 수 있다.
    BEGIN
    	패키지초기화부
    END;

    패키지 본체에 나타나는 모든 항목은 필수가 아니라 옵션이다. 따라서 필요한 경우에만 선언하고 정의하면 된다.

    패키지초기화부는 패키지가 초기화될 때 단 한 번만 실행된다. 이때문에 패키지초기화부를 일회성 프로시저라고 부르기도 한다. 패키지초기화부는 변수의 초기화나 특정 기능을 실행하는 등의 용도로 사용된다.

    --패키지 명세
    CREATE OR REPLACE PACKAGE pkg_emp
    IS
    	-- 공용 타입 선언
        TYPE emp_type IS TABLE OF emp%ROWTYPE;
        
        -- 공용 상수 선언
        c_deptno_accounting CONSTANT NUMBER := 10;
        c_deptno_research CONSTANT NUMBER := 20;
        c_deptno_sales CONSTANT NUMBER := 30;
        c_deptno_operations CONSTANT NUMBER := 40;
        
        -- 공용 변수 선언
        v_last_wage NUMBER;
        
        -- 공용 서브프로그램 선언
        FUNCTION get_wage(a_empno NUMBER) RETURN NUMBER;
        PROCEDURE raise_bonus(a_empno NUMBER, a_amt NUMBER);
    END;
    -- 패키지 본체
    CREATE OR REPLACE PACKAGE BODY pkg_emp
    IS 
    	--전용 상수 선언
        c_null_commission		CONSTANT NUMBER := 0;
        c_failed_return_wage	CONSTANT NUMBER := -1;
        
        -- 전용 커서 선언
        CURSOR emp_cursor(a_empno NUMBER) IS
        	-- 사번이 a_empno인 사원의 급여와 커미션의 합을 조회
            SELECT sal + NVL(comm, c_null_commission) comm
              FROM emp
             WHERE empno = a_empno;
             
        -- 공용 서브프로그램 정의
        FUNCTION get_wage(a_empno NUMBER) RETURN NUMBER
        	-- 사원의 급여와 커미션의 합을 반환하는 함수
        IS
        BEGIN
        	FOR rec IN emp_cursor(a_empno)
            LOOP
            	-- 사원이 존재하는 경우에는 급여를 반환
                v_last_wage := rec.comm;
                return rec.comm;
                end loop;
                -- 사원이 존재하지 않을 경우는 -1을 반환한다.
                RETURN c_failed_return_wage;
         END;
            
         PROCEDURE raise_bonus(a_empno NUMBER, a_amt NUMBER)
         	-- 테이블 bonus에 사원의 커미션 값을 인상하는 프로시저
         IS
         	v_ename emp.ename%TYPE;
         BEGIN
         	-- 사원의 이름을 얻는다
            BEGIN
              SELECT ename
                INTO v_ename
                FROM emp
               WHERE empno = a_empno;
            EXCEPTION
              WHEN NO_DATA_FOUND THEN
              	-- 사원이 존재하지 않을 경우 수행을 중단하고 복귀
                -- 반환값이 없는 것이 함수와 다른 점이다.
                RETURN;
             END;
             
             == 보너스를 인상한다.
             IF a_amt IS NOT NULL
             THEN
             	MERGE INTO bonus
                USING DUAL
                   ON (bonus.ename = v_ename)
                 WHEN MATCHED THEN -- 기존 보너스가 있는 경우는 인상할 값을 더한다.
                 	UPDATE SET comm = comm + a_amt
                 WHEN NOT MATCHED THEN -- 기존 보너스가 없는 경우 새 로우를 추가
                 	INSERT (ename, comm)
                    VALUES (v_ename, a_amt);
              END IF;
         END;
         
     BEGIN -- 패키지 초기화부
     	v_last_wage := -1;
     END;

    패키지 변수

    패키지 변수는 패키지 명세 또는 본체에 선언되는 변수를 말한다. (패키지 서브프로그램 내부에서 사용하는 변수는 여기에 해당되지 않는다.)  패키지 변수는 공용 패키지 변수와 전용 패키지 변수로 구분할 수 있다.

    차이점 공용 패키지 변수 전용 패키지 변수
    변수 선언 위치 패키지 명세 패키지 본체
    패키지 외부 PL/SQL에서 참조 가능 불가능
    패키지 외부 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;

    패키지에 대해 한 가지 알아두어야 하는 것은 패키지 변수가 언제 생성되고 언제 제거되는가다.

    패키지 변수는 패키지가 초기화될 때 생성되고, 패키지가 제거될 때 같이 제거된다. 즉 패키지 변수의 생명 주기는 패키지의 생명 주기와 동일하다는 것이다. 패키지는 세션에서 패키지가 최초로 참조될 때 초기화된다. 패키지가 제거되는 시점은 여러 가지인데, 세션이 종료될 때, 패키지가 재컴파일될 때, 패키지가 무효화될 때 등이다.

     

    패키지 변수는 특정 값으로 초기화가 필요한 경우가 많다. 패키지 변수의 초기화 방법은 두가지인데, 첫째는 패키지 변수 선언시 바로 초깃값을 부여하는 방법이고, 둘째는 패키지 초기화부에서 초기값을 부여하는 방법이다.

    -- 패키지 변수를 선언과 동시에 초기값 부여
    CREATE OR REPLACE PACKAGE pkg_xyz
    IS
    	v_pkg_var_1 NUMBER := 1; -- 공용 패키지 변수. 선언 시 초기화
        v_pkg_var_2 NUMBER;		 -- 공용 패키지 변수. 미초기화
    END;
    -- 패키지 변수를 패키지 본체의 초기화부에서 초기화
    CREATE OR REPLACE PACKAGE BODY pkg_xyz
    IS
    	v_pkg_var_3 NUMBER := 3;	-- 전용 패키지 변수. 선언 시 초기화
        v_pkg_var_4 NUMBER;			-- 전용 패키지 변수. 미초기화
    BEGIN
    	-- 초기화부에서 패키지 변수 초기화
        v_pkg_var_2 := 2;	-- 공용 패키지 변수를 초기화
        v_pkg_var_4 := 4;   -- 전용 패키지 변수를 초기화
    END;

    패키지 변수의 사용 시에 주의할 사항이 몇 가지 있다.

    1.  생명 주기: 패키지 변수는 패키지가 초기화된 후로부터 패키지가 제거되는 시점 사이에만 존재한다. 패키지 변수의 값은 데이터베이스에 영구적으로 저장되는 값이 아니다. 따라서 패키지 변수는 데이터를 저장하는 용도로 사용할 수 없다. 데이터를 저장하는 용도로 사용할 수 있는 데이터베이스 오브젝트는 테이블 뿐이다.
    2. 세션 의존성: 패키지 변수는 특정 세션에 의존하고 그 세션과 생명을 같이 한다. 패키지가 메모리에 적재되어 생성되는 특정 패키지 인스턴스는 오로지 하나의 세션만을 위해 존재한다. 예를 들어 100개의 세션이 동일한 패키지를 사용한다고 하더라도 100개의 세션은 각자의 별도 패키지 인스턴스를 가진다. 인스턴스별로 가지는 패키지 변수의 값은 완전히 독립적이다. 따라서 패키지 변수의 값을 여러 세션이 공유할 수 없다.
    3. 재초기화: 패키지 변수는 세션 사용 중에도 다시 초기화될 수 있다. 패키지가 재컴파일되거나 무효화되는 경우에는  다시 초기화가 일어나서 모든 변수가 최초의 값으로 되돌아간다.
    4. 병렬 처리: 패키지 변수를 참조하는 쿼리는 병렬 처리가 불가능 하다. 병렬 처리에 대한 설명은 생략한다.
    -- 패키지 변수 사용 시 주의 사항 예제를 위한 패키지 명세
    CREATE OR REPLACE PACKAGE pkg_seq
    IS
    	v_seq NUMBER;	-- 공용 패키지 변수 선언
        
        FUNCTION get_nextval RETURN NUMBER;	-- 패키지 변수의 값을 1 증가시키고 이 값을 반환
    END;
    -- 패키지 변수 사용 시 주의 사항 예제를 위한 패키지 본체
    CREATE OR REPLACE PACKAGE BODY pkg_seq
    IS
    	FUNCTION get_nextval RETURN NUMBER IS
        BEGIN
        	v_seq := v_seq + 1;
            RETURN v_seq;
        END;
    BEGIN
    	v_seq := 0;		-- 공용 패키지 변수 초기화
    END;
    -- 패키지 변수의 생명 주기, 세션 의존성, 재초기화
    SCOTT> REM 다음 문장이 실행될 때 패키지 변수가 초기화된다.
    SCOTT> SELECT pkg_seq.get_nextval FROM dual;
    
    GET_NEXTVAL
    -----------
    		 1
             
    SCOTT> SELECT pkg_seq.get_nextval FROM dual;
    
    GET_NEXTVAL
    -----------
    		 2
             
    SCOTT> SELECT pkg_seq.get_nextval FROM dual;
    
    GET_NEXTVAL
    -----------
    		 3
             
    SCOTT> REM 패키지가 재컴파일되면 패키지는 메모리에서 제거된다.
    SCOTT> ALTER PACKAGE pkg_seq COMPILE;
    
    패키지가 변경되었습니다.
    
    SCOTT> REM 다음 문장이 실행될 때 패키지 변수가 다시 초기화된다.
    SCOTT> SELECT pkg_seq.get_nextval FROM dual;
    
    GET_NEXTVAL
    -----------
    		  1
              
    SCOTT> SELECT pkg_seq.get_nextval FROM dual;
    
    GET_NEXTVAL
    -----------
    		 2
             
    SCOTT> exit
    
    % sqlplus scott/tiger
    SCOTT> REM 재접속하면 패키지는 메모리에 존재하지 않고 다음 줄에서 다시 초기화된다.
    SCOTT> SELECT pkg_seq.get_nextval FROM dual;
    
    GET_NEXTVAL
    -----------
    		  1

    패키지 pkg_seq의 패키지 변수 v_seq는 3번 줄에서 최초로 참조되면서 초기화된다. 이후 패키지 함수 get_nextval이 수행되면서 패키지 변수 v_seq는 값이 계속 증가하지만 22번 줄에서 패키지가 재컴파일되면서 메모리에서 제거되었다가 패키지가 다시 참조되는 27번 줄에서 다시 초기화 된다. SQL*Plus를 종료하면 패키지는 메모리에서 제거된다. 다시 접속하면 패키지는 아직 초기화되지 않은 상태이다. 44번 줄에서 패키지를 다시 참조하면 패키지 변수는 비로소 다시 초기화된다. 

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

    서브프로그램의 다양한 기능들 - 매개변수  (0) 2021.04.01
    Package(패키지) - 2  (0) 2021.03.25
    저장 프로시저  (0) 2021.03.16
    Stored Function(저장 함수)  (0) 2021.03.09
    저장 서브프로그램 개요  (0) 2021.03.03

    댓글