① 다차원 컬렉션
지금까지 봤던 컬렉션 예제에서는 컬렉션의 요소 타입으로 VARCHAR2, NUMBER 같은 빌트인 SQL 타입을 사용했다. 이때 컬렉션 모양은 앞에서 얘기했던 것처럼 같은 유형의 객실이 연결되어 있는 기차 모양과 비슷한 1차원 형태다. 하지만 이러한 1차원 형태를 다차원으로 만들 수 있는 방법이 있는데, 컬렉션의 요소 타입 자체를 다른 컬렉션이나 OBJECT 타입으로 선언하는 것이다. 예를 들어, 요소의 타입이 VARRAY 타입인 중첩 테이블, 중첩 테이블 타입의 요소를 가진 VARRAY 타입, 요소의 타입이 OBJECT 타입인 중첩 테이블도 만들 수 있고, 심지어는 레코드 타입을 요소로 가진 컬렉션 타입도 생성이 가능하다. 먼저 VARRAY 타입인 요소로 구성된 VARRAY 타입의 컬렉션을 생성해 보자.
입력
DECLARE
-- 첫 번째 VARRAY 타입 선언 (구구단 중 각 ‘단X5’ 값을 가진 요소를 갖는 VARRAY)
TYPE va_type1 IS VARRAY(5) OF NUMBER;
-- 위에서 선언한 va_type1을 요소 타입으로 하는 VARRAY 타입 선언(구구단 중 1~3단까지 요소를 갖는 VARRAY)
TYPE va_type11 IS VARRAY(3) OF va_type1;
-- 두 번째 va_type11 타입의 변수 선언
va_test va_type11;
BEGIN
-- 생성자를 이용해 값 초기화,
va_test := va_type11( va_type1(1, 2, 3, 4, 5),
va_type1(2, 4, 6, 8, 10),
va_type1(3, 6, 9, 12, 15));
-- 구구단 출력
DBMS_OUTPUT.PUT_LINE('2곱하기 3은 ' || va_test(2)(3));
DBMS_OUTPUT.PUT_LINE('3곱하기 5는 ' || va_test(3)(5));
END;
결과
2곱하기 3은 6
3곱하기 5는 15
위 예제는 두 개의 VARRAY를 선언해 구구단 중 1~3단까지 값을 생성자를 사용해 넣은 후 출력한 것이다. VARRAY인 va_type11 타입은 또 다른 VARRAY인 va_type1을 요소의 타입으로 선언해서, 최종적으로는 5×3 형태의 2차원 배열이 되었다. 초기화하는 부분을 보면 내부는 va_type1로, 외부는 va_type11 생성자를 사용한 점을 눈여겨 보자. VARRAY의 VARRAY 타입이므로 2차원이지만 더 중첩시키면 3차원 이상도 가능하다. 만약 3차원 형태라면 생성자는 “생성자1(생성자2(생성자3)))” 형태로, 요소값 참조는 “변수명(n)(n)(n)” 형태가 될 것이다.
또 다른 예를 살펴 보자. 이번에는 레코드 타입을 요소로 가진 중첩 테이블을 만들어 보자.
입력
DECLARE
-- 요소 타입을 employees%ROWTYPE로 선언, 즉 테이블형 레코드를 요소 타입으로 한 중첩 테이블
TYPE nt_type IS TABLE OF employees%ROWTYPE;
-- 중첩 테이블 변수 선언
vnt_test nt_type;
BEGIN
-- 빈 생성자로 초기화
vnt_test := nt_type();
-- 중첩 테이블에 요소 1개 추가
vnt_test.EXTEND;
-- 사원 테이블에서 100번 사원의 정보를 가져옴
SELECT *
INTO vnt_test(1) -- 위에서 빈 생성자로 초기화해서 요소 1개를 추가했으므로 인덱스는 1
FROM employees
WHERE employee_id = 100;
-- 100번 사원의 사번과 성명 출력
DBMS_OUTPUT.PUT_LINE(vnt_test(1).employee_id);
DBMS_OUTPUT.PUT_LINE(vnt_test(1).emp_name);
END;
결과
100
Steven King
주석을 보면 예제소스를 어렵지 않게 이해할 수 있을 것이다. 먼저 테이블형 레코드는 ‘테이블명%ROWTYPE’으로만 선언할 수 있으므로 nt_type 중첩 테이블의 요소 타입으로 사용했다. 그리고 나서 중첩 테이블의 변수를 선언하고 빈 생성자로 초기화했다. 그 다음 EXTEND 컬렉션 메소드를 사용해 요소를 1개 추가한 뒤 SELECT…INTO 문을 사용해 100번 사원의 정보를 vnt_test 변수에담았다. 만약 또 다른 사원정보를 넣고 싶다면 EXTEND~SELECT INTO 과정을 되풀이하면 된다. 즉 다음과 같이 처리하면 위 중첩 테이블은 사원 테이블과 완벽히 같은 구조와 데이터를 갖게 된다.
입력
DECLARE
-- 요소 타입을 employees%ROWTYPE으로 선언,즉 테이블형 레코드를 요소 타입으로 한 중첩 테이블
TYPE nt_type IS TABLE OF employees%ROWTYPE;
-- 중첩 테이블 변수 선언
vnt_test nt_type;
BEGIN
-- 빈 생성자로 초기화
vnt_test := nt_type();
-- FOR 루프와 커서를 사용해 사원 테이블 전체를 중첩 테이블에 담기
FOR rec IN ( SELECT * FROM employees)
LOOP
-- 요소 1개 추가
vnt_test.EXTEND;
-- LAST 메소드를 사용하면 항상 위에서 추가한 요소의 인덱스를 가져 옴
vnt_test ( vnt_test.LAST) := rec;
END LOOP;
-- 출력
FOR i IN vnt_test.FIRST..vnt_test.LAST
LOOP
DBMS_OUTPUT.PUT_LINE(vnt_test(i).employee_id || ' - ' || vnt_test(i).emp_name);
END LOOP;
END;
결과
171 - William Smith
172 - Elizabeth Bates
173 - Sundita Kumar
174 - Ellen Abel
...
...
FOR 루프와 커서를 사용해 루프를 돌면서 EXTEND와 LAST 메소드를 이용해 사원 테이블의 모든 정보를 컬렉션 변수에 담아둔 뒤, 다시 루프를 돌며 출력하였다. 이 예제에서는 중첩 테이블을 사용했지만, 사번을 인덱스로 하는 연관 배열을 사용했다면 사번을 인덱스로 활용해 나머지 정보를 컬렉션에서 뽑아 올 수 있을 것이다. 여기서는 두 가지 예만 다루었지만 용도에 따라 다양한 타입의 조합을 통해 다양한 형태의 데이터 구조를 만들어 사용할 수 있다.