오라클 PL/SQL은 컴포지트 테이터 타입으로 컬렉션(Collection)과 레코드(Record) 두 가지를 지원한다.
컬렉션은 동일한 데이터 타입이 반복되는 데이터를 저장하는 자료구조이고, 레코드는 서로 다른 데이터 타입의 데이터를 모아 놓은 자료구조이다. 여기서는 컬렉션을 알아보자. 컬렉션의 대표적인 예는 배열이다.
컬렉션 타입 종류
-컬렉션 타입 특성 비교
비교항목 | Associative Array | VARRAY | Nested Table |
컬렉션 항목 개수 | 미지정 | 지정 | 미지정 |
인덱스 유형 | 문자열 또는 정수 | 정수 | 정수 |
초기화되지 않은 상태 | Empty | NULL | NULL |
ADT(Abstract Data Type) 정의 | 불가능 | 가능 | 가능 |
컬렉션 항목의 개수가 미지정이면 항목의 개수에 한계가 없음을 의미한다.
VARRAY의 경우는 선언 시 항목의 개수가 지정되며 한 번 지정된 개수는 변경 불가능하다.
비어있는(Empty) 컬렉션은 존재하지만 항목을 가지지 않은 항목의 개수가 0개인 컬렉션이며 NULL 컬렉션과는 다르다.
비어 있는 컬렉션은 원소를 가지지 않은 빈 집합을 생각하면 되고, NULL 컬렉션은 집합 자체가 없는 것을 말한다.
비어있는 컬렉션에 항목을 추가하려면 EXTEND 메소드를 호출해야 한다.
NULL 컬렉션은 정말 존재하지 않는 컬렉션이므로 NULL 컬렉션에 EXTEND 메소드를 사용하는 것은 불가능하며, NULL컬렉션은 Empty 컬렉션이나 항목을 가지는 컬렉션으로 바꿔야 사용할 수 있다.
Associative Array
컬렉션 항목의 개수를 사전에 지정하지 않는다.
Associative Array의 개별항목을 유일하게 식별하는 인덱스로는 정수와 문자열 둘 중 하나를 사용할 수 있다.
정수 인덱스로는 어떤 값이라도 사용가능한데, 음수까지 사용 가능하다.
컬렉션 항목의 개수에는 제한이 없으며 따라서 인덱스의 최대 한계 값이 존재하지 않는다.
다른 종류의 컬렉션 타입과 달리 초기화를 필요로 하지 않는다.
Associative Array 변수는 NULL이 될 수 없고, NULL을 할당하려고 하면 오류가 발생한다.
-- Associative Array 선언
TYPE 타입명 IS TABLE OF 데이터타입 INDEX BY 인덱스데이터타입;
변수명 타입명;
DECLARE
-- 정수를 인덱스로 하는 Associative Array 타입 선언
TYPE city IS TABLE OF VARCHAR2(64) INDEX BY PLS_INTEGER;
-- 문자열을 인덱스로 하는 Associative Array 타입 선언
TYPE population IS TABLE OF NUMBER INDEX BY VARCHAR2(64);
v_city city; -- 컬렉션 변수 선언
v_Population population -- population 타입의 컬렉션 변수
BEGIN
--정수 인덱스를 사용하는 Associative Array의 값 할당
--특정 인덱스에 값을 지정하면, 이후 이 인덱스로 값의 접근이 가능하다.
v_city(-1) := '서울';
v_city(0) := '부산';
v_city(1) := '대전';
-- 인덱스 -1, 0, 1을 제외한 인덱스의 항목은 값을 가지지 않는다.
-- 다른 인덱스를 사용하여 v_city에 접근하면 ORA-01403 오류가 발생
-- 문자열 인덱스를 사용하는 Associative Array의 값 지정
v_Population('서울') := 10373234; -- 서울 인구
v_Population('부산') := 3812322; -- 부산 인구
v_Population('대전') := 1346630; -- 대전 인구
-- v_city에 들어 있는 각 도시의 인구 출력
DBMS_OUTPUT_PUT_LINE('도시별 인구(2000년 기준)');
DBMS_OUTPUT_PUT_LINE('=============================');
DBMS_OUTPUT_PUT_LINE(v_city(-1) || ' :' || TO_CHAR(v_Population(v_city(-1)), '99,999,999'));
DBMS_OUTPUT_PUT_LINE(v_city(0) || ' :' || TO_CHAR(v_Population(v_city(0)), '99,999,999'));
DBMS_OUTPUT_PUT_LINE(v_city(1) || ' :' || TO_CHAR(v_Population(v_city(1)), '99,999,999'));
END;
Associative Array에 값을 위 예제처럼 하나하나 할당해주는 방법만 있는 것이 아니다.
배열 처리를 사용해 DB에서 조회한 결과를 한 번에 컬렉션에 적재할 수도 있다.
SELECT문에 단순 INTO를 사용하면 조회된 한 건의 로우로부터의 결과를 변수에 넣어 주지만, BULK COLLECT INTO를 사용하면 조회된 여러 건의 로우로부터의 결과를 로우 건수만큼의 항목을 가지는 배열에 넣어준다.
DECLARE
TYPE string_array IS TABLE OF VARCHAR2(100) INDEX BY PLS_INTEGER;
v_arr string_array;
BEGIN
-- 테이블 emp의 모든 로우의 ename을 Associative Array 컬렉션에 한번에 적재
SELECT ename
BULK COLLECT INTO v_arr
FROM emp;
DBMS_OUTPUT_PUT_LINE('Associative Array 컬렉션 건수 = '||v_arr.COUNT);
END;
Associative Array는 뒤에 설명할 PL/SQL함수나 프로시저의 매개변수 또는 함수의 반환형으로도 사용할 수 있다.
Associative Array는 서브프로그램 내애서 사용될 소규모의 참조 테이블에서 사용하거나 컬렉션을 서버로 전달 또는 서버로부터 전달받을 때 유용하게 사용할 수 있다.
VARRAY(Variable-Size Array)
VARRAY변수 선언 시 배열의 크기를 지정해야 되는데 이 크기는 배열이 가질 수 있는 최대 크기를 의미한다.
VARRAY 배열의 실제 크기는 최소 0부터 타입 선언 시 지정된 최대 크기 사이에서 동적으로 할당된다.
VARRAY의 인덱스는 1부터 시작하는 자연수이며, 저장된 값은 순서가 바뀌지 않는 것이 보장된다.
초기화되지 않은 VARRAY 변수는 NULL이며 사용 전에 반드시 초기화해주어야 오류가 발생하지 않는다.
--VARRAY 선언
TYPE 타입명 IS VARRAY(크기) OF 데이터타입;
변수명 타입명;
DECLARE
-- VARRAY 타입 선언
TYPE language IS VARRAY(10) OF VARCHAR2(64);
v_lang language; -- VARRAY 변수 선언, v_lang = NULL임
v_lang2 language := language('한국어', '중국어', '영어'); -- 변수 선언시 생성자를 사용하여 초기화
BEGIN
v_lang := language(); -- 컬렉션 생성자를 사용하여 Empty(크기가 0)로 초기화
v_lang := language('한국어'); -- 컬렉션 생성자를 사용하여 크기가 1인 VARRAY로 재초기화
v_lang := language('한국어', '중국어'); -- 컬렉션 생성자를 사용하여 크기가 2인 VARRAY로 재초기화
v_lang.EXTEND(2); -- 크기 두 개 증가
v_lang(3) := '일본어';
v_lang(4) := '영어';
-- v_lang에 들어 있는 언어 출력
DBMS_OUTPUT_PUT_LINE(' ');
DBMS_OUTPUT_PUT_LINE('언어 목록');
DBMS_OUTPUT_PUT_LINE('============');
FOR i IN v_lang.FIRST .. v_lang.LAST
LOOP
DBMS_OUTPUT_PUT_LINE(TO_CHAR(i) || ' :' || v_lang(i));
END LOOP;
END;
VARRAY도 Associative Array와 마찬가지로 배열 처리를 위해 BULK COLLECT INTO를 사용하여 DB에서 조회한 결과를 컬렉션에 한 번에 적재할 수 있다.
VARRAY를 사용한 타입 선언 시 명시하는 크기는 최대 크기를 명시할 뿐이며, 처음부터 해당 크기가 할당되는 것이 아니다.
Associative Array와 마찬가지로 VARRAY도 할당되지 않은 인덱스의 참조는 허용되지 않는다.
VARRAY는 배열의 최대 개수를 사전에 알고 있는 경우 또는 배열의 항목을 항상 동일한 순서로 접근하는 경우에 유용하게 사용할 수 있다.
Nested Table
순서가 고정되어 있지 않고 크기도 고정되지 않은 데이터의 집합을 저장하는 데 적합한 컬렉션.
컬렉션의 인덱스는 1부터 시작하여 순차적으로 증가한다.
Nested Table은 값을 저장하거나 조회할 때 저장된 항목의 순서가 항상 동일함을 보장하지 않는다.
초기화되지 않은 Nested Table 변수는 NULL이며 VARRAY와 마찬가지로 사용 전에 반드시 초기화해 주어야 오류가 발생하지 않는다.
개념적으로 Nested Table은 임의의 개수의 항목을 가지는 일차원 배열이다. 하지만 배열과는 차이가 있다.
- 배열은 크기가 사전에 선언되지만 Nested Table은 그렇지 않다. Nested Table의 크기는 동적으로 증가될 수 있다.
- 배열은 항상 밀집형이다. Nested Table은 처음에는 밀집형이지만 항목 중 일부를 삭제하면 희소형(Sparse, 일부 항목이 비어있는 형태)으로 바뀔 수 있다.
TYPE 타입형 IS TABLE OF 데이터타입;
변수명 타입명;
DECLARE
-- 정수를 인덱스로 하는 Nested Table
TYPE city IS TABLE OF VARCHAR2(64);
v_city city; -- 빈 컬렉션 변수 선언
-- 컬렉션 변수 선언과 동시에 컬렉션 생성자를 사용하여 값을 초기화
v_city2 city := city('서울', '부산', '대전');
BEGIN
-- 실행 중에도 컬렉션 생성자를 사용해 초기화 가능
v_city := city('서울', '부산', '대전', '광주', '인천');
v_city := city(); -- 크기 0(Empty 컬렉션)으로 초기화
-- 크기를 증가시키고 값을 지정한다.
v_city.EXTEND; v_city(1) := '서울';
v_city.EXTEND; v_city(2) := '부산';
v_city.EXTEND; v_city(3) := '대전';
v_city.EXTEND; v_city(4) := '광주';
DBMS_OUTPUT_PUT_LINE('도시 개수 : '||v_city.COUNT||'개');
-- 유효한 컬렉션 값을 출력
FOR i IN v_city.FIRST .. v_city.LAST
LOOP
IF v_city.EXISTS(i) THEN
DBMS_OUTPUT_PUT_LINE(CHR(9) || 'v_city(' || TO_CHAR(i) || ') : '||v_city(i));
END IF;
END LOOP;
-- 3번 인덱스 삭제. 삭제된 인덱스의 항목은 더 이상 접근이 불가능
v_city.DELETE(3);
DBMS_OUTPUT_PUT_LINE('도시 개수 : ' ||v_city.COUNT||'개');
-- 유효한 컬렉션 값을 출력
FOR i IN v_city.FIRST .. v_city.LAST
LOOP
IF v_city.EXISTS(i) THEN
DBMS_OUTPUT_PUT_LINE(CHR(9) || 'v_city(' || TO_CHAR(i) || ') : '||v_city(i));
END IF;
END LOOP;
END;
Nested Table도 마찬가지로 배열 처리를 하기 위해서는 BULK COLLECT INTO를 사용해야 한다.
Nested Table은 개수가 확정적이지 않거나 인덱스가 연속적이지 않거나, 컬렉션의 일부 항목을 삭제 또는 변경할 필요가 있는 경우에 유용하게 사용할 수 있다.
컬렉션 생성자
세가지 컬렉션 중 VARRAY와 Nested Table로 선언된 변수는 초기화하지 않으면 NULL이다.
Associative Array는 초기화가 필요하지 않다.
컬렉션 변수의 초기화를 위해서는 생성자를 사용해야 하는데 생성자는 컬렉션 타입과 동일한 이름을 가지는 시스템이 자동으로 생성해 주는 함수이다.
컬렉션타입 ( [ 값 [, 값 ...] ] )
컬렉션 생성자에서 값 항목이 없을 수도 있는데 없는 경우에는 항목의 개수가 0개인 빈 컬렉션을 반환한다.
생성자를 사용하여 초기화하기 전의 컬렉션 변수는 NULL이고 값 항목이 없는 생성자로 초기화하면 빈(Empty) 변수가 된다는 것을 잊지 말아야 한다. NULL 컬렉션 변수는 컬렉션으로서의 기능을 할 수 없고 비어있는 컬렉션 변수는 컬렉션으로써 사용 가능하다.
컬렉션의 초기화는 DECLARE정에서 변수 선언 시에도 가능하고 프로그램의 실행 중에도 가능하다.
DECLARE
TYPE string_array IS TABLE OF VARCHAR2(100);
v_arr1 string_array := string_array(); -- 변수 선언 시 빈 컬렉션으로 초기화
v_arr2 string_array;
BEGIN
-- 실행 시 네 개의 항목을 가지는 컬렉션으로 초기화
v_arr2 := string_array('사과', '배', '포도', '귤');
DBMS_OUTPUT_PUT_LINE('v_arr1.COUNT = '||v_arr1.COUNT);
DBMS_OUTPUT_PUT_LINE('v_arr2.COUNT = '||v_arr2.COUNT);
END;
Associative Array는 초기화 없이도 컬렉션으로 사용할 수 있어서 생성자가 필요하지도 않고, 생성자가 존재하지도 않다.
'DATABASE > SQL, PL-SQL' 카테고리의 다른 글
레코드(Record) (0) | 2021.02.06 |
---|---|
컬렉션 - 2 (0) | 2021.02.04 |
제어문 - 2 (0) | 2021.02.02 |
제어문 - 1 (0) | 2021.02.02 |
CLOB (0) | 2021.01.27 |
댓글