레코드(Record)

    레코드를 사용하면 데이터 타입이나 길이가 다른 여러 변수들을 논리적으로 하나의 그룹으로 묶을 수 있다.

    로우 앵커(%ROWTYPE)를 사용하면 기존의 테이블 또는 뷰와 돌일한 구조의 레코드를 쉽게 정의하고 사용할 수 있다.

    또한 레코드를 사용하면 SQL문을 훨씬 간단하게 작성할 수 있다.

    레코드 사용

    레코드를 사용하기 위해서는 컬렉션과 마찬가지로 먼저 레코드 타입을 선언하고 이 타입을 이용해서 레코드 변수(인스턴스)를 선언해야 한다.

    TYPE 타입명 IS RECORD (필드목록);
    레코드변수명 타입명;

    여기에서 필드목록은 레코드의 필드 목록을 선언하는 부분으로 형식은 CREATE TABLE문의 컬럼선언과 유사한 형태를 가진다.

    DECLARE
    	TYPE emp_type IS RECORD(	--세 개의 필드를 가지는 레코드 선언
        	empno NUMBER(4) NOT NULL := 0,  -- NOT NULL 필드는 반드시 초기값을 선언해야 함
            ename emp.ename%TYPE,			-- 컬럼 앵커를 사용한 필드 선언
            job VARCHAR2(9)					-- 필드의 데이터 타입을 직접 지정
       );
       v_emp	emp_type;				-- 타입을 사용하여 변수(인스턴스) 선언
    BEGIN
    	-- 레코드의 필드에 값을 할당
        v_emp.empno := 9000;
        v_emp.ename := '홍길동';
        v_emp.job  := '의적';
        
        DBMS_OUTPUT_PUT_LINE('EMPNO = '||v_emp_empno);
        DBMS_OUTPUT_PUT_LINE('ENAME = '||v_emp_ename);
        DBMS_OUTPUT_PUT_LINE('JOB = '||v_emp_job);
    END;     

    레코드 선언 시 각 필드에는 반드시 데이터 타입을 지정해야 한다. 데이터 타입은 직접 지정할 수도 있고, 앵커를 사용하여 지정할 수도 있다. 필드에는 옵션으로 초기값을 지정할 수 있는데, 위 프로그램에서와 같이 empno의 초기값을 0으로 주면 모든 emp_type 레코드 인스턴스의 empno 컬럼은 최초에 항상 초기값 0을 가지게 된다. NOT NULL 필드의 경우에는 반드시 초기값을 지정해야 오류가 나지 않는다.

     

    DECLARE
    	v_emp emp%ROWTYPE;		-- 로우 앵커를 사용한 레코드 변수 선언
    BEGIN
    	-- 레코드의 필드에 값을 할당
        v_emp.empno := 9000;
        v_emp.ename := '홍길동';
        v_emp.job  := '의적';
        
        DBMS_OUTPUT_PUT_LINE('EMPNO = '||v_emp_empno);
        DBMS_OUTPUT_PUT_LINE('ENAME = '||v_emp_ename);
        DBMS_OUTPUT_PUT_LINE('JOB = '||v_emp_job);
    END;     

    주의할 것은 %ROWTYPE을 사용하여 선언한 레코드 변수 v_emp에는 예제에서 사용된 세 개의 필드 empno, ename, job뿐만 아니라 테이블 emp에 정의되어 있는 다른 다섯 개의 컬럼까지 레코드의 필드로 선언된다는 사실이다.

    레코드를 SQL에 사용하기

    레코드는 관련을 가지는 변수들을 하나의 자료구조로 묶어서 프로그램을 보다 구조적으로 만들어 줄 뿐만 아니라 레코드 단위의 연산을 가능하게 해준다. 레코드를 사용하면 SELECT, INSERT, UPDATE문이 훨씬 간단해진다.

    DECLARE
    	TYPE emp_type IS RECORD(
          empno NUMBER(4) NOT NULL := 0,  -- NOT NULL필드는 반드시 초기값 지정
          ename emp.ename%TYPE,
          job	VARCHAR2(9)
       );   
    BEGIN
        -- INTO절에 세 개의 필드를 지정하는 대신 레코드 변수를 지정할 수 있다.
        SELECT empno, ename, job
        	INTO v_emp
            -- INTO v_emp.empno, v_emp.ename, v_emp.job -- 세 개의 변수를 윗 줄의 레코드 하나로 대체
            FROM emp
         WHERE empno = 7788;
         
        DBMS_OUTPUT_PUT_LINE('EMPNO = '||v_emp_empno);
        DBMS_OUTPUT_PUT_LINE('ENAME = '||v_emp_ename);
        DBMS_OUTPUT_PUT_LINE('JOB = '||v_emp_job);
    END;     

    레코드를 SQL에 사용할 때 필드의 순서는 중요한데 필드와 순번이 동일한 SELECT 컬럼은 동일하거나 호환되는 데이터 타입을 가져야 한다. 위 프로그램에서는 레코드의 필드 이름을 SELECT될 컬럼 이름과 동일하게 사용했지만 이름이 동일할 필요는 없다. 마찬가지로 데이터 타입도 정확히 일치해야할 필요는 없지만 묵시적 변환에 문제가 되지는 않아야 한다.

     

    SQL문에 레코드 변수를 사용할 때 주의할 점은 일단 레코드 변수를 사용하게 되면 오로지 레코드 변수 하나만 지정 가능하며 다른 레코드 변수나 스칼라 변수, 상수, 리터럴등을 같이 사용할 수 없다는 점이다.

     

    DECLARE
    	-- 테이블 emp의 일부 컬럼을 레코드로 선언
        TYPE emp_basic_info_type IS RECORD (
          empno		emp.empno	%TYPE,
          ename 	emp.ename	%TYPE,
          job		emp.job		%TYPE,
          mgr		emp.mgr		%TYPE,
          hiredate  emp.hiredate%TYPE,
          deptno	emp.deptno	%TYPE
        );
        -- 테이블 emp의 나머지 칼럼을 레코드로 선언
        TYPE emp_salary_info_type IS RECORD (
        	sal		emp.sal		%TYPE,
            comm	emp.comm	%TYPE
        );
        
        -- 레코드 변수
        v_emp_basic		emp_basic_info_type;
        v_emp_salary	emp_salary_info_type;
        
        -- 개별 스칼라 변수
        v_sal	emp.sal		%TYPE;
        v_comm	emp.comm	%TYPE;
    BEGIN	
    	-- 두 개의 레코드 변수를 INTO절에 사용할 수는 없다.
        -- 파싱 단계에서 오류 발생
        SELECT empno, ename, job, mgr, hiredate, deptno, sal, comm
          INTO v_emp_basic, v_emp_salary
          FROM emp
         WHERE empno = 7788;
        
        -- 레코드 변수와 스칼라 변수를 혼합하여 INTO절에 사용할 수도 없다
        -- 파싱 단계에서 오류 발생
        SELECT empno, ename, job, mgr, hiredate, deptno, sal, comm
          INTO v_emp_basic, v_sal, v_comm
          FROM emp
         WHERE empno = 7788;
    END;

    레코드 변수에 값 할당

    레코드 변수의 각 필드에 값을 할당할 때에는 각 필드별로 값을 할당하거나 할당 연산자 :=를 사용하여 다른 레코드를 복사하거나 쿼리를 실행한 결과를 한 번에 레코드 변수의 여러 필드에 담는 방법을 사용할 수 있다.

    DECLARE
    	TYPE emp_rec IS RECORD (
          empno emp.empno%TYPE;
          ename emp.ename%TYPE;
        );
        
        v_emp1 emp_rec;
        v_emp2 emp_rec;
        v_emp3 emp_rec;
    BEGIN
    	v_emp1.empno := 9000;  v_emp1.ename := '홍길동';	-- 1. 필드별로 값을 할당
        v_emp2 := v_emp1;								   -- 2. 다른 레코드를 복사
        SELECT empno, ename INTO v_emp3					   -- 3. 쿼리 결과를 레코드에 저장
          FROM emp
         WHERE empno = 7788;
    END;

     컬렉션에서 동일 구조의 변수이더라도 타입명이 다르면 할당 연산자를 사용할 수 없었던 것처럼 레코드에서도 동일한 구조이더라도, 타입명이 다르면 할당 연산자를 사용할 수 없다.

     

    컬렉션에서의 생성자와 같이 한 문장으로 레코드의 모든 필드에 값읗 할당하는 방법은 기본적으로 지원하지 않는다.

    유사한 기능을 원한다면 생성자와 유사한 함수를 만들어 해결하는 방법밖에 없다.

    DECLARE
    	TYPE emp_rec IS RECORD (
          empno emp.empno%TYPE;
          ename emp.ename%TYPE;
        );
        
        v_emp emp_rec;
        
        -- 생성자 역할을 하는 함수를 만든다.
        FUNCTION make_emp_rec(a_empno emp.empno%TYPE,
        					  a_ename emp.ename%TYPE) RETURN emp_rec
        IS
        	v_rec emp_rec;
        BEGIN
        	v_rec.empno := a_empno;
            v_rec.ename := a_ename;
            RETURN v_rec;
        END;
        
    BEGIN
    	v_emp := make_emp_rec('9000', '아무개');	-- 생성자 역할의 함수를 사용
    END;

    함수 make_emp_rec는 레코드의 두 필드에 저장될 값을 매개변수로 받아서 emp_rec레코드를 구성한 후 이를 반환하고, 사용 시에는 함수의 결과를 레코드 변수에 할당한다.

     

    레코드와 컬렉션의 혼합

    레코드와 컬렉션은 혼합하여 동시에 사용할 수 있다. 둘을 동시에 사용한다는 것은 레코드 컬렉션(레코드의 컬렉션)이나 컬렉션 레코드(컬렉션의 레코드), 또는 레코드의 필드로 레코드를 사용할 수 있다는 것을 뜻한다. 

    레코드의 컬렉션을 사용하면 여러 개의 로우를 반환하는 SELECT문의 결과를 배열에다 한 번에 담을 수 있고, FORALL문을 사용하여 레코드 컬렉션을 사용한 INSERT, UPDATEM문을 실행할 수 있다.

    DECLARE
    	TYPE city_tab_type IS TABLE OF VARCHAR2(64) INDEX BY PLS_INTEGER;	-- 컬렉션
        TYPE name_rec IS RECORD									-- 레코드
          first_name	VARCHAR2(30),
          last_name		VARCHAR2(30)
       );
       TYPE emp_rec IS RECORD ( 	-- 컬렉션과 레코드의 혼합
       	 empno	emp.empno%TYPE DEFAULT 1000,
         ename name_rec,		-- 레코드가 레코드의 필드가 될 수 있다.
         city  city_tab_type	-- 컬렉션이 레코드의 필드가 될 수 있다.
       );
       TYPE people_type IS VARRAY(10) OF name_rec;	--레코드의 컬렉션이 가능
       TYPE emp_type	IS VARRAY(10) OF emp_rec;	--레코드의 컬렉션
    BEGIN
    	NULL;
    END;

     

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

    Cursor(커서) - 2  (0) 2021.02.18
    Cursor(커서) - 1  (0) 2021.02.17
    컬렉션 - 2  (0) 2021.02.04
    컬렉션(Collection) - 1  (0) 2021.02.03
    제어문 - 2  (0) 2021.02.02

    댓글