Notice
Recent Posts
Recent Comments
Link
«   2026/04   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30
Archives
Today
Total
관리 메뉴

Devvy-Is-Free

[Oracle] CH10 : 레코드와 컬렉션 본문

Programming/데이터베이스

[Oracle] CH10 : 레코드와 컬렉션

Devvy 2022. 11. 15. 00:32

레코드

레코드: 복수의 값을 저장할 수 있는 변수

레코드 종류  
%ROWTYPE 레코드 %ROWTYPE 속성으로 선언된 변수
사용자 정의 레코드 사용자 정의의 레코드 타입으로 선언된 변수

사용자 정의 레코드 정의

사용자가 타입을 정의할 때, 필드 개수와 필드명과 데이터 타입 정의

ㄴ 정의한 타입과 변수명 매핑

- 사용자 정의 레코드는 NUMBER 타입이나 %ROWTYPE 속성의 변수와는 달리 타입 정의와 변수 정의의 두 단계 필요

 

- 레코드 타입 정의

사용자 고유의 레코드 타입 정의

* 필드의 데이터 타입에 %TYPE, %ROWTYPE 속성 사용 가능

* 필드에 NOT NULL 제약 조건 지정 간으

* 필드에 디폴트 값을 지정하여 초기화 가능

TYPE <레코드 타입명> IS RECORD
  (<필드명> <데이터 타입>
   [NOT NULL { := | DEFAULT} <디폴트 값>]
  [,<필드명> <데이터 타입>
   [NOT NULL { := | DEFAULT} <디폴트 값>] ...]);

- 레코드 정의

앞서 정의한 레코드 타입과 레코드명 매핑

<레코드명> <레코드 타입명>;

 

사용자 정의 레코드 사용 방법

사용자 정의 레코드는 %ROWTYPE 속성의 변수와 동일하게 사용 가능

레코드명과 필드명을 지정하여 각 필드를 참조하고, 대입 연산자(:=)와 SELECT INTO문, FETCH INTO문을 사용하여 값을 대입

DECLARE
    TYPE rec IS RECORD
      (a NUMBER(2),  -- a, b라는 두 개의 필드가 있는 레코드 타입 rec 정의
       b VARCHAR2(6));
    record_type rec;  -- 레코드 타입 rec의 사용자 정의 레코드 record_type 정의
BEGIN
    record_type.a := 1;  -- 실행부에서 레코드의 각 필드에 값을 대입
    record_type.b := 'Part1';
    DBMS_OUTPUT.PUT_LINE(record_type.a||' '||record_type.b);
END;
/
Part1

PL/SQL 처리가 정상적으로 완료되었습니다.

동일한 레코드 타입으로 선언된 레코드가 있다면 필드명을 지정하지 않고 레코드에 값 대입 가능

DECLARE
    TYPE rec IS RECORD
      (a NUBMER(2),
       b VARCHAR2(6));
    record_type  rec; -- 두 개의 레코드 타입 rec의 레코드 정의
    record_type2 rec;
BEGIN
    record_type.a := 1;
    record_type.b := 'Part1';
    record_type2 := record_type; -- 같은 레코드 타입 rec이기 때문에 필드명을 지정하지 않고 값 대입 가능
    DBMS_OUTPUT.PUT_LINE(record_type2.a||' '||record_type2.b);
END;
/
Part1

PL/SQL 처리가 정상적으로 완료되었습니다.

 

 

파라미터와 함수의 반환 값으로 레코드 사용

레코드 타입을 프로시저의 파라미터와 함수의 반환 값으로 사용하는 것이 가능

ㄴ> 레코드 타입을 지정하면 복수의 값을 통합하여 다른 프로그램에 전달 가능

CREATE OR REPLACE PROCEDURE get_record(rec dept%ROWTYPE) -- 파라미터의 데이터 타입에 %ROWTYPE 속성 변수 지정
IS
BEGIN
    DBMS_OUTPUT.PUT_LINE(rec.deptno||'
                       '||rec.dname||'
                       '||rec.loc);
END;
/

프로시저가 생성되었습니다.


-- 레코드를 전달받은 프로그램
DECLARE
  dept_row dept%ROWTYPE;
BEGIN
  SELECT * INTO dept_row FROM dept WHERE deptno = 20;
  get_record(dept_row); -- 프로시저 get_record 실행(SELECT INTO문에서 추출한 변수를 실제 파라미터에 지정)
END;
/
RESEARCH DALLAS -- 파라미터에서 한 번에 세 개의 값을 받은 것을 확인 가능

PL/SQL 처리가 정상적으로 완료되었습니다.

 

 

레코드를 사용한 데이터 삽입 및 변경

레코드를 사용하면 테이블에 값을 삽입, 업데이트하는 작업을 한꺼번에 수행 가능

 

- 레코드 삽입

ㄴ INSERT 문의 VALUES 절에 레코드 지정 가능

ㄴ 필드의 개수를 테이블 정의 또는 INTO절 컬럼 목록과 동일하게 해야 함

ㄴ 필드의 데이터 타입은 적재 대상 컬럼의 데이터 타입과 호환성을 가져야 함

INSERT INTO <테이블명> VALUES <레코드명>;
DECLARE
  rec dept%ROWTYPE;
BEGIN
  rec.deptno := 50;   -- 레코드에는 삽입할 세 개의 값을 미리 대입
  rec.dname  := 'SE';
  rec.loc    := 'DETROIT';
  
  INSERT INTO dept VALUES rec; -- 레코드를 사용하여 결정된 값 삽입
END;
/

PL/SQL 처리가 정상적으로 완료되었습니다.

SELECT * FROM dept WHERE deptno = 50;

DEPTNO   DNAME    LOC
------   ------   -------
50       SE       DETROIT

 

 

레코드를 사용한 행 데이터 변경

통상적으로 UPDATE문에서는 특정 컬럼 단위로 업데이트하지만, 레코드를 사용하면 값을 1행(row)단위로 한꺼번에 업데이트 가능. -> ROW 키워드 사용

UPDATE <테이블명> SET ROW = <레코드명>;

ROW

- ROW 키워드와 서브 쿼리를 함께 사용할 수 없음

- ROW 키워드를 사용하는 경우, 허용되는 SET절은 한 개뿐임

 

레코드를 사용하여 행을 업데이트하는 경우, 레코드를 테이블의 행 구성에 따라 지정할 필요가 있기 때문에 SET절에는 %ROWTYPE 속성을 사용하는 것이 가장 효율적.

DECLARE
  rec dept%ROWTYPE;
BEGIN
  rec.deptno := 60; -- 업데이트할 세 개의 값을 레코드에 할당하여
  rec.dname  := 'PERSONNEL'; -- DEPT 컬럼의 모든 컬럼을 한꺼번에 업데이트
  rec.loc    := 'CHICAGO';
  
  UPDATE dept SET ROW = rec WHERE deptno = 50;
END;
/

PL/SQL 처리가 정상적으로 완료되었습니다.


SELECT * FROM dept WHERE deptno = 60;

DEPTNO  DNAME       LOC
------  ---------  ------------
60	    PERSONNEL   CHICAGO

컬렉션

컬렉션 : 동일한 데이터 타입의 복수의 값을 받는 변수

컬렉션을 레코드와 달리 요소 및 인덱스로 구성되며, 번호를 사용하여 각 요소에 액세스

변수를 배열로 처리하는 것이 가능

 

세 종류의 컬렉션

- 연관 배열(Associative Array)

- 중첩 테이블(NESTED TABLE)

- VAARAY

 

PL/SQL에서 컬렉션을 조작할때는 일반적으로 연관 배열을 가장 많이 사용


연관 배열(Associative Array)

연관 배열을 정의하려면 먼저 TABLE 타입을 정의할 필요가 있음

ㄴ 타입 정의 시에 인덱스와 요소의 데이터 타입을 정의. 레코드와 같이 타입 정의와 변수 정의의 두 단계 필요

 

* 연관 배열 정의

- 타입 정의 

: 연관 배열을 사용하려면 먼저 TABLE 타입 정의

ㄴ TABLE OF 뒤에 TABLE 타입 요소의 데이터 타입 지정

ㄴ INDEX BY 절 뒤에 지정한 데이터 타입이 인덱스의 데이터 타입

TYPE <테이블 타입> IS TABLE OF <데이터 타입> [NOT NULL]
  INDEX BY {
    BINARY_INTEGER | PLS_INTEGER | <문자 데이터 타입> (<상한 사이즈>)
};

 

- 연관 배열 정의 : 앞서 정의한 테이블 타입과 연관 배열명 매핑

<연관 배열명> <테이블 타입명>;

 

 

*연관 배열 사용 방법

연관 배열의 요소를 참조하려면 인덱스 번호 사용

요소에 값을 대입하는 경우는 대입 연산자(:=), SELECT INTO문, FETCH INTO문 사용

-- 연관 배열 참조
<연관 배열명>(<인덱스 번호>);
DECLARE
  TYPE tab IS TABLE OF VARCHAR2(10) INDEX BY PLS_INTEGER; -- 문자 데이터를 저장할 TABLE 타입 tab 정의
  col_table tab; -- 연관 배열 col_table 정의
BEGIN
  col_table(1) := 'Part1'; -- 실행부에서 인덱스 번호 각각에 값 대입
  col_table(2) := 'Part2';
  col_table(3) := 'Part3';
  
  DBMS_OUTPUT.PUT_LINE(col_table(1)); -- 각 요소에 저장된 값은 인덱스 번호를 사용하여 참조
  DBMS_OUTPUT.PUT_LINE(col_table(1));
  DBMS_OUTPUT.PUT_LINE(col_table(1));
END;
/
Part1
Part2
Part3

PL/SQL 처리가 정상적으로 완료되었습니다.

연관 배열 사용시 주의점

- 이미 사용되고 있는 인덱스 번호에 값을 대입하는 경우, 요소는 덮어쓰게 됨

- 인덱스 번호는 연속해서 지정할 필요 없음

- 미사용 인덱스 번호를 지정하여 요소를 참조하는 경우 오류 발생


컬렉션 메소드

컬렉션 메소드(Collection method)를 사용하면 컬렉션에 대한 정보 확인 및 요소 삭제, 컬렉션 조작 등 가능

메소드명 기능
COUNT 컬렉션에 현재 포함되어 있는 요소의 수를 리턴
DELETE 컬렉션의 모든 요소를 삭제
DELETE(n) 컬렉션의 n번째 요소를 삭제
DELETE(n, m) 컬렉션의 n번째 부터 m번째까지의 요소 삭제
FIRST 컬렉션의 첫 번째 인덱스 번호 리턴
LAST 컬렉션의 마지막 인덱스 번호 리턴
PRIOR(n) 컬렉션 n 앞의 인덱스 번호 리턴
NEXT(n) 컬렉션 n 뒤의 인덱스 번호 리턴
EXISTS(n) 컬렉션 n번째 요소가 존재하는 경우에 TRUE 리턴

 

컬렉션 메소드 조회

-- 컬렉션 메소드 조회 시 컬렉션 명 뒤에 메소드 이름 지정
<컬렉션명>.<메소드명>
DECLARE
  TYPE tab IS TABLE OF VARCHAR2(10) INDEX BY PLS_INTEGER;
  col_table tab;
BEGIN
  col_table(1) := 'Part1';
  col_table(100) := 'Part2';
  DBMS_OUTPUT.PUT_LINE(col_table.COUNT); -- 요소 개수
  DBMS_OUTPUT.PUT_LINE(col_table.FIRST); -- 첫번째 인덱스 번호
  DBMS_OUTPUT.PUT_LINE(col_table.LAST); -- 마지막 인덱스 번호
  DBMS_OUTPUT.PUT_LINE(col_table.PRIOR(100)); -- 100보다 한 개 이전의 인덱스 번호
  DBMS_OUTPUT.PUT_LINE(col_table.NEXT(1)); -- 한 개 이후의 인덱스 번호
END;
/
2
1
100
1
100

PL/SQL 처리가 정상적으로 완료되었습니다.

레코드의 연관 배열

요소의 데이터 타입에 레코드를 지정한 연관 배열 작성 가능

ㄴ TABLE OF 의 데이터 타입에 레코드 타입을 지정함

TYPE <테이블 타입> IS TABLE OF <레코드명>
  INDEX BY {
    BINARY_INTEGER | PLS_INTEGER | <문자 데이터 타입>(<상한 사이즈>)
};

레코드는 사용자 정의 레코드%ROWTYPE 속성 변수 사용 가능

각 필드에 액세스 하려면 인덱스 번호뿐만 아니라 필드명을 지정할 필요가 있음

<연관 배열명>(<인덱스 번호>).<필드명>

레코드 연관 배열을 사용하면 테이블 내의 복수 행 데이터를 하나의 변수로 통합하여 대입 가능

DECLARE
  TYPE tab IS TABLE OF dept%ROWTYPE INDEX BY PLS_INTEGER; -- 레코드의 요소에 %ROWTYPE 속성 변수를 사용한 TABLE 타입 tab 정의
  table_rec tab; -- 연관 배열 table_rec 선언
  CURSOR dept_cl IS SELECT * FROM dept;
  n NUMBER := 0;
BEGIN
  FOR r IN dept_cl LOOP
    n := n + 1;
    table_rec(n) := r; -- 실행부의 커서 FOR 루프 내에서 변수 n을 인덱스 번호로 하여 DEPT 테이블의 한 행의 데이터 대입
    DBMS_OUTPUT.PUT_LINE(table_rec(n).deptno||' '|| -- LOOP문이 종료된 지점에서 연관 배열 table_rec에는 DEPT 테이블의 모든 데이터가 저장되어 있음
                         table_rec(n).dname||' '|| -- 각 필드에는 인덱스 번호와 필드 이름을 지정하여 액세스
                         table_rec(n).loc);
  END LOOP;
END;
/

10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON

PL/SQL 처리가 정상적으로 완료되었습니다.

 

임시 테이블(TEMPORARY TABLE)과 연관 배열

Oracle에는 임시 테이블(Temporary table) 오브젝트 존재.

일시적으로 데이터를 저장하는 데 사용되며, 세션 또는 트랜잭션이 종료되면 데이터 삭제

- 데이터베이스 오브젝트로 작성되기 때문에 사용 시 타입 정의 불필요

- 값을 저장하려면 INSERT 문 사용

- 값을 조회하려면 SELECT 문 사용

- 세션이나 트랜잭션 사이에서만 값 참조 가능

CREATE GLOBAL TEMPORARY TABLE <테이블명>
  (<컬럼명> <데이터 타입> [, <컬럼명> <데이터 타입>]...)
ON COMMIT {DELETE|PRESERVE} ROWS];
파라미터 개요
DELETE ROWS 트랜잭션 내에서 값을 보유( Default )
PRESERVE ROWS 세션 내에서 값을 보유
CREATE GLOBAL TEMPORARY TABLE temp1(col1 NUMBER(3), col2 VARCHAR2(10)) ON COMMIT DELETE ROWS;
-- 임시 테이블 temp1 작성
테이블이 생성되었습니다.

INSERT INTO temp1 VALUES(10, 'Part1'); -- 임시테이블에 데이터를 대입하는 경우에는 INSERT문 실행하고

1개의 행이 만들어졌습니다.

INSERT INTO temp1 VALUES(20, 'Part2');
1 개의 행이 만들어졌습니다.

SELECT * FROM temp1; -- 조회하는 경우에는 SELECT문 실행

COL1  COL2
----  -----
10    Part1
20    Part2
COMMIT -- 트랜잭션을 COMMIT문으로 종료
-- 임시 테이블 temp1에는 DELETE ROWS가 지정되어 있기 때문에 트랜잭션의 종료와 동시에 삽입 값이 삭제
커밋이 완료되었습니다.

SELECT * FROM temp1;

선택된 레코드가 없습니다.

 

 

파라미터와 함수의 반환 값으로 연관 배열 사용

프로시저의 파라미터와 함수의 반환 값에 TABLE 타입을 사용하면 다른 프로그램에 배열을 그대로 전달 가능

CRETE OR REPLACE PACKAGE test_para
IS
  TYPE tab IS TABLE OF dept%ROWTYPE INDEX BY BINARY_INTEGER;
  table_rec tab; -- 레코드를 요소로 가지는 TABLE 타입 tab의 연관 배열 table_rec 정의
  PROCEDURE get_table(para OUT NOCOPY tab); -- 패키지 tset_para 내에 연관 배열을 OUT 모드의 파라미터로 전달할 프로시저 get_table
END;                           -- 파라미터의 모드 지정 후에는 NOCOPY 옵션 지정
/

패키지가 생성되었습니다.


CREATE OR REPLACE PACKAGE BODY test_para
IS
  PROCEDURE get_table(para OUT NOCOPY tab)
  IS
    n NUMBER := 0;
    CURSOR dept_cl IS SELECT * FROM dept;
  BEGIN
    FOR r IN dept_cl LOOP
      n := n + 1;
      table_rec(n) := r; -- 패키지 본체에는 연관 배열 table_rec에 DEPT 테이블의 데이터 대입
    END LOOP;
    para := table_rec; -- OUT 모드의 파라미터 para에 전달(연관 배열 전달 가능)
  END get_table;
END;
/

패키지 본문이 생성되었습니다.


BEGIN
  test_para.get_table(test_para.table_rec); -- 패키지 내의 연관 배열 table_rec을 OUT모드 파라미터 위치에 지정한 프로시저 get_table 실행
  FOR r IN 1.. test_para.table_rec.COUNT LOOP
    DBMS_OUTPUT.PUT_LINE(test_para.table_rec(r).deptno||' '||
                         test_para.table_rec(r).dname||' '||
                         test_para.table_rec(r).loc);
  END LOOP;
END;
/

10 ACCOUNTING NEW YORK -- 프로시저에서 테이블 데이터가 한꺼번에 전달된 것을 확인 가능
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON

PL/SQL 처리가 정상적으로 완료되었습니다.

 

 

NOCOPY 옵션

OUT, IN OUT 모드 파라미터에 NOCOPY 옵션 지정 가능.

반환 값이 존재하는 주소만 호출한 쪽에 참조되도록 하는 것이 가능

ㄴ 컬렉션과 같이 사이즈가 큰 반환 값을 전달하는 경우에, 컬렉션을 복사할 필요가 없어져서 메모리 공간 절약 가능

 

 

 

 

 

 

참조 : 책 '전문가를 위한 오라클 PL/SQL 입문'

'Programming > 데이터베이스' 카테고리의 다른 글

[Oracle] CH9 : 트리거  (1) 2022.11.14
[Oracle] CH8 : 패키지  (0) 2022.11.14
[Oracle] CH7 : 프로시저와 함수  (1) 2022.11.14
[Oracle] CH6 : Stored 서브 프로그램의 개요  (1) 2022.11.12
[Oracle] CH5 : 예외 처리  (0) 2022.11.08