개요
Oracle 21c에서 FOR LOOP 문의 기능이 대폭 개선되었습니다.
테스트를 위해 아래와 같이 테이블을 생성하겠습니다.
-- 1 DROP TABLE t1 PURGE; CREATE TABLE t1 (c1 NUMBER, c2 VARCHAR2(1)); INSERT INTO t1 VALUES (2, 'A'); INSERT INTO t1 VALUES (4, 'B'); INSERT INTO t1 VALUES (6, 'C'); COMMIT;
Iterand
FOR LOOP 문의 iterand에 MUTABLE 키워드와 constrained type을 사용할 수 있습니다.
pls_identifier [ MUTABLE | IMMUTABLE ] [constrained_type]
아래 예제는 iterand에 MUTABLE 키워드를 사용하여 루프 내에서 iterand를 변경합니다. MUTABLE 키워드를 사용하지 않으면 "PLS-00363: 'I' 식은 피할당자로 사용될 수 없습니다." 에러가 발생합니다. 참고로 루프 내에서 iterand를 변경하면 성능 저하나 무한 루프가 발생할 수 있습니다.
-- 2 BEGIN FOR i MUTABLE IN 1 .. 10 LOOP DBMS_OUTPUT.PUT_LINE (i); i := i * 2; END LOOP; END; / 1 3 7 PL/SQL 처리가 정상적으로 완료되었습니다.
아래 예제는 하한 값과 상한 값에 소수 값, iterand에 constrained type을 사용합니다. 결과가 소수 값으로 표시됩니다. iterand에 constrained type을 사용하지 않으면 반올림된 정수 값이 표시됩니다.
-- 3 BEGIN FOR i NUMBER(2,1) IN 1.5 .. 3.5 LOOP DBMS_OUTPUT.PUT_LINE (i); END LOOP; END; / 1.5 2.5 3.5 PL/SQL 처리가 정상적으로 완료되었습니다.
Iteration Control
PL/SQL Language Reference는 FOR LOOP 문의 Iteration Control를 아래와 같이 구분합니다. 쉼표(,)로 구분해 다수의 Iteration Control을 사용할 수 있습니다.
iteration_control stepped_control single_expression_control collection_iteration_control values_of_control indices_of_control pairs_of_control cursor_iteration_control sql_statement cursor_object cursor_variable dynamic_sql
Stepped Control
Stepped Control은 하한 값과 상한 값을 지정합니다. 21c부터 하한 값과 상한 값을 소수 값으로 지정할 수 있으며, BY 키워드로 증분 값을 지정할 수 있습니다.
[REVERSE] lower_bound .. upper_bound [BY step]
아래 예제는 BY 키워드를 사용하여 값을 2씩 증가시킵니다.
-- 4 BEGIN FOR i IN 1 .. 10 BY 2 LOOP DBMS_OUTPUT.PUT_LINE (i); END LOOP; END; / 1 3 5 7 9 PL/SQL 처리가 정상적으로 완료되었습니다.
아래 예제는 constrained type와 BY 키워드를 사용하여 값을 0.2씩 증가시킵니다.
-- 5 BEGIN FOR i NUMBER(2,1) IN 1.0 .. 2.0 BY 0.2 LOOP DBMS_OUTPUT.PUT_LINE (i); END LOOP; END; / 1 1.2 1.4 1.6 1.8 2 PL/SQL 처리가 정상적으로 완료되었습니다.
아래 예제는 FOR LOOP 문에 2개의 Iteration Control을 사용합니다.
-- 6 BEGIN FOR i IN 1 .. 3, REVERSE 2 .. 6 BY 2 LOOP DBMS_OUTPUT.PUT_LINE (i); END LOOP; END; / 1 2 3 2 4 6 PL/SQL 처리가 정상적으로 완료되었습니다.
아래 예제는 두 번째 Iteration Control의 하한 값과 상한 값에 iterand에 대한 산술 표현식을 사용합니다. 참고로 첫 번째 Iteration Control은 iterand를 참조할 수 없습니다.
-- 7 BEGIN FOR i IN 1 .. 3, i + 4 .. i + 6 LOOP DBMS_OUTPUT.PUT_LINE (i); END LOOP; END; / 1 2 3 7 8 9 PL/SQL 처리가 정상적으로 완료되었습니다.
Single Expression Control
Single Expression Control은 단일 표현식과 반복 표현식으로 사용할 수 있습니다.
[REPEAT] expr
아래 예제는 다수의 단일 표현식을 사용합니다.
-- 8 BEGIN FOR i IN 1, 3, 5, 7, 9 LOOP DBMS_OUTPUT.PUT_LINE (i); END LOOP; END; / 1 3 5 7 9 PL/SQL 처리가 정상적으로 완료되었습니다.
아래 예제는 두 번째 Iteration Control에 반복 표현식을 사용합니다. 반복 표현식은 WHILE 절로 시작하는 Stopping 조건을 함께 사용해야 합니다.
-- 9 BEGIN FOR i IN 1, REPEAT i * 2 WHILE i <= 16 LOOP DBMS_OUTPUT.PUT_LINE (i); END LOOP; END; / 1 2 4 8 16 PL/SQL 처리가 정상적으로 완료되었습니다.
아래 예제는 WHEN 절로 시작하는 Skipping 조건을 사용합니다. 참고로 Stopping 조건과 Skipping 조건은 모든 Iteration Control에 사용할 수 있습니다.
-- 10 BEGIN FOR i IN 1, REPEAT i * 2 WHILE i <= 16 WHEN i >= 8 LOOP DBMS_OUTPUT.PUT_LINE (i); END LOOP; END; / 1 8 16 PL/SQL 처리가 정상적으로 완료되었습니다.
Collection Iteration Control
아래 예제는 Values of Control로 연관 배열의 값을 조회합니다.
-- 11: values of control DECLARE TYPE taa IS TABLE OF VARCHAR2(1) INDEX BY PLS_INTEGER; v1 taa := taa (2 => 'A', 4 => 'B', 6 => 'C'); BEGIN FOR v IN VALUES OF v1 LOOP DBMS_OUTPUT.PUT_LINE (v); END LOOP; END; / A B C PL/SQL 처리가 정상적으로 완료되었습니다.
아래 예제는 Indices of Control로 연관 배열의 인덱스를 조회합니다.
-- 12: indices of control DECLARE TYPE taa IS TABLE OF VARCHAR2(1) INDEX BY PLS_INTEGER; v1 taa := taa (2 => 'A', 4 => 'B', 6 => 'C'); BEGIN FOR i IN INDICES OF v1 LOOP DBMS_OUTPUT.PUT_LINE (i); END LOOP; END; / 2 4 6 PL/SQL 처리가 정상적으로 완료되었습니다.
아래 예제는 Pairs of Control로 중첩 테이블의 인덱스(i)와 값(v)을 조회합니다.
-- 13: pairs of control DECLARE TYPE taa IS TABLE OF VARCHAR2(1) INDEX BY PLS_INTEGER; v1 taa := taa (2 => 'A', 4 => 'B', 6 => 'C'); BEGIN FOR i, v IN PAIRS OF v1 LOOP DBMS_OUTPUT.PUT_LINE (i || ',' || v); END LOOP; END; / 2,A 4,B 6,C PL/SQL 처리가 정상적으로 완료되었습니다.
Cursor Iteration Control
아래 예제는 Iteration Control에 SQL 문을 사용합니다. 참고로 Cursor Iteration Control은 Collection Iteration Control과 동일하게 Values of, Indices of, Pairs of Coltrol을 사용할 수 있습니다.
-- 14: sql statement BEGIN FOR v IN (SELECT * FROM t1) LOOP -- FOR v IN VALUES OF (SELECT * FROM t1) LOOP DBMS_OUTPUT.PUT_LINE (v.c1 || ',' || v.c2); END LOOP; END; / 2,A 4,B 6,C PL/SQL 처리가 정상적으로 완료되었습니다.
아래 예제는 Iteration Control에 커서 오브젝트를 사용합니다.
-- 15: cursor object DECLARE CURSOR cur1 IS SELECT * FROM t1; BEGIN FOR i, v IN PAIRS OF cur1 LOOP DBMS_OUTPUT.PUT_LINE (i || ',' || v.c1 || ',' || v.c2); END LOOP; END; / 1,2,A 2,4,B 3,6,C PL/SQL 처리가 정상적으로 완료되었습니다.
아래 예제는 Iteration Control에 커서 변수를 사용합니다.
-- 16: cursor variable DECLARE v1 SYS_REFCURSOR; BEGIN OPEN v1 FOR SELECT * FROM t1; FOR i, v t1%ROWTYPE IN PAIRS OF v1 LOOP DBMS_OUTPUT.PUT_LINE (i || ',' || v.c1 || ',' || v.c2); END LOOP; CLOSE v1; END; / 1,2,A 2,4,B 3,6,C PL/SQL 처리가 정상적으로 완료되었습니다.
아래 예제는 Iteration Control에 동적 SQL 문을 사용합니다.
-- 17: dynamic sql DECLARE v_sql_text VARCHAR2(4000) := 'SELECT * FROM T1'; BEGIN FOR i, v t1%ROWTYPE IN PAIRS OF (EXECUTE IMMEDIATE v_sql_text) LOOP DBMS_OUTPUT.PUT_LINE (i || ',' || v.c1 || ',' || v.c2); END LOOP; END; / 1,2,A 2,4,B 3,6,C PL/SQL 처리가 정상적으로 완료되었습니다.
관련 링크
- PL/SQL Language Reference - FOR LOOP Statement Overview
- PL/SQL Language Reference - FOR LOOP Statement
- ORABLE-BASE - FOR LOOP Iteration Enhancements in Oracle Database 21c