현재 사용하는 패키지에서 사용하는 함수 중 리턴값이 쿼리 문자열인 함수가 있다. 이 함수의 리턴값을 받아서 동적으로 쿼리를 실행시키는데, 이러한 동적쿼리를 이전까지 사용한 경험이 없어 이번기회에 정리하고자한다.
패키지에서는 프로시저 내부에서 함수를 호출하고, 커서를 이용하여 쿼리 수행 결과 개수 만큼 로직을 반복하는식으로 구성되어있다.
즉, 쿼리 수행 결과의 값이 집합인 동적 쿼리를 OPEN FOR 구문을 통해 처리한다.
예를 들자면 아래와 같다.
-- F_DYN_QUERY 함수 내부
DECLARE
str_sql VARCHAR2(4000);
BEGIN
SELECT QUERY
INTO str_sql
FROM TABLE_QUERY -- 동적 쿼리 관리 테이블
WHERE ID = '1';
RETURN str_sql;
END;
-- P_PROC_MAIN 프로시저 내부
DECLARE
TYPE DYN_CURSOR_TYPE IS REF CURSOR; -- 커서타입선언
dyn_cur DYN_CURSOR_TYPE; -- 커서변수선언
str_sql VARCHAR2(4000);
col VARCHAR2(10);
BEGIN
str_sql:= F_DYN_QUERY(...);
OPEN dyn_curFOR str_sql;
LOOP dyn_cur
FETCH dyn_curINTO col;
EXIT WHEN dyn_cur%NOTFOUND;
BEGIN
DBMS_OUTPUT.PUT_LINE('data: '|| col);
END;
END;
위 예시에서는 OPEN FOR 구문을 이용하여 커서를 선언하여 동적함수를 실행하며 아래의 순서로 진행한다.
그런데, OPEN FOR 구문처럼 루프를 돌면서 결과를 패치하는 식의 처리는 쿼리 결과로 반환되는 행의 수가 많은 경우 시간도 오래 걸리고, 성능면에서 좋지 않다.
따라서 일일이 한 행씩 읽으며 결과를 받는 대신 BULK COLLECT INTO 구문을 사용하여 배열로 받도록 프로시저 내부를 개선 시키도록 하겠다.
BULK COLLECT INTO 구문은 쿼리 수행 결과를 배열로 읽어들인다.
이 방법은 루프를 사용해서 한번에 한 행씩 패치하는 것보다 방식도 간편하며, 성능면에서도 우수하다.
따라서 P_PROC_MAIN 프로시저 내부를 다음과 같이 변경해 볼 수 있겠다.
-- 향상된 P_PROC_MAIN 프로시저 내부
DECLARE
str_sql VARCHAR2(4000);
-- 레코드 선언
TYPE REC_COL IS RECORD (
col VARCHAR2(10);
);
-- 레코드를 항목으로 하는 중첩 테이블 선언
TYPE NT_DATA IS TABLE OF REC_COL;
-- 중첩테이블 변수 선언
vr_data NT_DATA;
BEGIN
str_sql:= F_DYN_QUERY(...);
EXECUTE IMMEDIATE str_sql BULK COLLECT INTO vr_data;
FOR cnt IN 1..vr_data.COUNT
LOOP
DBMS_OUTPUT.PUT_LINE('data: '|| vr_data(cnt).data1);
END LOOP;
END;
향상된 프로시저는 BULK COLLECT INTO 구문을 이용하여 동적함수를 실행결과를 컬렉션 변수에 담아 처리하였고, 아래의 순서로 진행한다.