4. 흐름 제어#
개요#
이 장은 저장 프로시저 바디 내에서의 흐름 제어 방법을 설명한다.
구문#
저장 프로시저에서 사용할 수 있는 흐름 제어문은 다음과 같다.
-
조건 분기문인 IF 문과 CASE 문
-
조건을 만족할 때 반복 수행하는 LOOP 문, WHILE 문과 FOR 문
-
반복 수행문의 흐름을 제어하는 EXIT 문와 CONTINUE 문
-
아무것도 수행하지 않음을 명시적으로 나타낼 수 있는 NULL 문
-
특정 위치로 이동할 수 있는 GOTO 문
제약 사항#
부질의(subquery)가 포함된 조건은 IF 문 또는 CASE 문의 조건으로 사용될 수 없다. 단, 아래의 조건은 예외이다:
-
EXIST (subquery)
-
NOT EXIST (subquery)
IF#
구문#
기능#
조건을 만족하는 경우와 그렇지 않은 경우에 따라 처리 흐름을 분기하는 조건 분기문이다. IF절은 조건은 검사해서 true이면 THEN절로 제어를 이동하고, false이거나 NULL이면 ELSE 절로 제어를 이동한다.
condition#
조건절에는 SQL문의 WHERE 절에서 사용 가능한 모든 술어(predicate)들을 사용할 수 있다. 지원하는 술어들에 대한 상세 내용은 SQL Reference의 SELECT 구문을 참조한다.
ELS(E)IF#
ELS(E)IF절의 경우 이전의 IF 문의 조건과는 다른 조건을 명시할 수 있다.
ELSIF는 한 단어이며 하나의 IF 문 내에 여러 개의 ELS(E)IF절을 사용할 수 있다.
ELSE#
앞서IF와 ELS(E)IF에서 열거된 모든 조건을 만족하지 않는 경우 ELSE 절의 문장이 수행된다. ELSE 절은 명시하거나, 하나의 IF 문 내에 한번만 기술 가능하다.
중첩 IF 문#
IF 문은 다른 IF 문 내에 중첩되어 기술할 수 있다. 즉, IF 문은 다른 IF, ELS(E)IF 또는 ELSE 문의 결과로 수행되는 일련의 작업 내에 포함될 수 있다. 각 IF 문은 END IF로 종료해야 한다.
예제#
예제1#
CREATE OR REPLACE PROCEDURE proc1
AS
CURSOR c1 IS SELECT eno, emp_job, salary FROM employees;
emp_id employee.eno%TYPE;
e_job employee.emp_job%TYPE;
e_salary employee.salary%TYPE;
BEGIN
OPEN c1;
LOOP
FETCH c1 INTO emp_id, e_job, e_salary;
EXIT WHEN c1%NOTFOUND;
IF e_salary IS NULL THEN
IF e_job = 'CEO' THEN
e_salary := 5000;
ELSIF e_job = 'MANAGER' THEN
e_salary := 4500;
ELSIF e_job = 'ENGINEER' THEN
e_salary := 4300;
ELSIF e_job = 'PROGRAMMER' THEN
e_salary := 4100;
ELSE
e_salary := 4000;
END IF;
UPDATE employees SET salary = e_salary WHERE eno = emp_id;
END IF;
END LOOP;
CLOSE c1;
END;
/
iSQL> SELECT eno, emp_job FROM employees WHERE salary IS NULL;
ENO EMP_JOB
--------------------------------
1 CEO
8 manager
20 sales rep
3 rows selected.
iSQL> EXEC proc1;
Execute success.
iSQL> SELECT eno, emp_job, salary FROM employees
WHERE eno=1 OR eno=8 OR eno=20;
ENO EMP_JOB SALARY
---------------------------------------------
1 CEO 5000
8 manager 4500
20 sales rep 4000
3 rows selected.
예제2#
CREATE TABLE t1 (i1 VARCHAR(20), i2 NUMBER, i3 DATE);
CREATE TABLE t2 (i1 VARCHAR(20), i2 NUMBER, i3 DATE);
INSERT INTO t1 VALUES ('21-JUL-2001', 2, '01-JUL-2000');
INSERT INTO t2 VALUES (NULL,NULL,'01-FEB-1990');
INSERT INTO t2 VALUES (NULL,NULL,'02-FEB-1990');
CREATE OR REPLACE FUNCTION func2
(p1 IN DATE, p2 IN CHAR(30))
RETURN NUMBER
AS
BEGIN
RETURN (TO_NUMBER(TO_CHAR(p1, 'dd')) + TO_NUMBER(p2));
END;
/
CREATE OR REPLACE FUNCTION func1
(p1 IN DATE, p2 IN DATE)
RETURN DATE
AS
BEGIN
IF p1 >= p2 THEN
RETURN add_months(p1, 3);
ELSE
RETURN add_months(p1, 4);
END IF;
END;
/
CREATE OR REPLACE PROCEDURE proc1
AS
v1 VARCHAR(20);
v2 NUMBER;
v3 DATE;
BEGIN
SELECT i1, func2(TO_DATE(i1), TO_CHAR(i3, 'yyyy')), i3
INTO v1,v2,v3 FROM t1 WHERE i2 = 2;
INSERT INTO t2 VALUES (v1,v2,v3);
IF v2 not in (2001, 2002, 2003) AND v1 = '21-JUL-2001' THEN
UPDATE t2
SET i1 = func1(v1, '17-JUL-2001'),
i2 = nvl(i2, 10)
WHERE i3 = '01-FEB-1990';
UPDATE t2
SET i1 = func1(v1, '27-JUL-2001'),
i2 = nvl(i2, 10*2)
WHERE i3 = '02-FEB-1990';
END IF;
END;
/
iSQL> EXEC proc1;
Execute success.
iSQL> SELECT * FROM t2;
T2.I1 T2.I2 T2.I3
-----------------------------------------------
21-JUL-2001 2021 2000/07/01 00:00:00
21-OCT-01 10 1990/02/01 00:00:00
21-NOV-01 20 1990/02/02 00:00:00
3 rows selected.
예제3#
CREATE TABLE payroll(
eno INTEGER,
bonus NUMBER(10, 2));
CREATE OR REPLACE PROCEDURE proc1
AS
BEGIN
DECLARe
CURSOR c1 IS
SELECT DISTINCT(eno), SUM(qty) FROM orders GROUP BY eno;
emp_id orders.eno%TYPE;
sum_qty orders.qty%TYPE;
bonus NUMBER(10, 2);
BEGIN
OPEN c1;
IF c1%ISOPEN THEN
LOOP
FETCH c1 INTO emp_id, sum_qty;
EXIT WHEN c1%NOTFOUND;
IF sum_qty > 25000 THEN
bonus := 1000;
ELSIF sum_qty > 15000 THEN
bonus := 500;
ELSE
bonus := 200;
END IF;
INSERT INTO payroll VALUES(emp_id, bonus);
END LOOP;
END IF;
END;
END;
/
iSQL> EXEC proc1;
Execute success.
iSQL> SELECT DISTINCT(eno), SUM(qty) sum FROM orders GROUP BY eno;
ENO SUM
------------------------------------
12 17870
19 25350
20 13210
3 rows selected.
iSQL> SELECT * FROM payroll;
PAYROLL.ENO PAYROLL.BONUS
-----------------------------
12 500
19 1000
20 200
3 rows selected.
CASE#
구문#
기능#
특정 변수의 값에 따라서 처리 경로를 결정하는 조건 분기문이다.
IF 문과 동일한 기능이지만 CASE 문을 사용하면 프로그램의 가독성을 높일 수 있다. CASE 문은 위의 다이어그램에서 보여 주 듯이 다음 두 가지가 있다.
-
case_statement_1: 조건식이 참일 때에 특정한 문장을 수행하는 방식
-
case_statemen_2: 하나의 변수가 특정한 값이 되었을 때에 특정한 문장을 수행하는 방식
단, 하나의 CASE 문에서 이 두 가지 방식을 혼용할 수 없다.
CASE 절을 모두 만족하지 못하면 ELSE 절의 문장을 수행하게 되며, ELSE 절이 없는 경우에는 어떠한 문장도 수행하지 않는다.
condition#
검사할 조건을 기술한다. SELECT 구문의 WHERE 절과 같이 술어 문으로 명시한다.
case_variable#
저장 프로지저의 처리를 분기시키는 기준이 되는 변수명을 기술한다.
when_value#
case_variable 변수와 비교할 실제 상수 값을 기술한다.
ELSE#
CASE 조건식이 모두 거짓이 되었을 때 수행해야 할 처리 구문을 ELSE 절에 기술한다. ELSE 절은 없어도 상관 없으며 하나의 CASE 문에 한번 사용 가능하다.
조건식이 모두 거짓이 되었음에도 ELSE 절이 없을 경우 CASE 문은 어떠한 문장도 수행하지 않고 지나간다.
예제#
예제1#
CREATE OR REPLACE PROCEDURE proc1
AS
CURSOR c1 IS SELECT eno, emp_job, salary FROM employees;
emp_id employees.eno%TYPE;
e_job employees.emp_job%TYPE;
e_salary employees.salary%TYPE;
BEGIN
OPEN c1;
LOOP
FETCH c1 INTO emp_id, e_job, e_salary;
EXIT WHEN c1%NOTFOUND;
IF e_salary IS NULL THEN
CASE
WHEN e_job = 'CEO' THEN e_salary := 5000;
WHEN e_job = 'MANAGER' THEN e_salary := 4500;
WHEN e_job = 'ENGINEER' THEN e_salary := 4300;
WHEN e_job = 'PROGRAMMER' THEN e_salary := 4100;
ELSE e_salary := 4000;
END CASE;
UPDATE employees SET salary = e_salary WHERE eno = emp_id;
END IF;
END LOOP;
CLOSE c1;
END;
/
iSQL> EXEC proc1;
Execute success.
iSQL> SELECT eno, emp_job, salary FROM employees
WHERE eno=1 OR eno=8 OR eno=20;
ENO EMP_JOB SALARY
---------------------------------------------
1 CEO 5000
8 manager 4500
20 sales rep 4000
3 rows selected.
예제2#
@SCHEMA.SQL
CREATE OR REPLACE PROCEDURE PROC1
AS
CURSOR C1 IS SELECT ENO, EMP_JOB, SALARY FROM EMPLOYEES;
EMP_ID EMPLOYEES.ENO%TYPE;
E_JOB EMPLOYEES.EMP_JOB%TYPE;
E_SALARY EMPLOYEES.SALARY%TYPE;
BEGIN
OPEN C1;
LOOP
FETCH C1 INTO EMP_ID, E_JOB, E_SALARY;
EXIT WHEN C1%NOTFOUND;
IF E_SALARY IS NULL THEN
CASE E_JOB
WHEN 'CEO' THEN E_SALARY := 5000;
WHEN 'MANAGER' THEN E_SALARY := 4500;
WHEN 'ENGINEER' THEN E_SALARY := 4300;
WHEN 'PROGRAMMER' THEN E_SALARY := 4100;
ELSE E_SALARY := 4000;
END CASE;
UPDATE EMPLOYEES SET SALARY = E_SALARY WHERE ENO = EMP_ID;
END IF;
END LOOP;
CLOSE C1;
END;
/
ISQL> SELECT ENO, EMP_JOB FROM EMPLOYEES WHERE SALARY IS NULL;
ENO EMP_JOB
--------------------------------
1 CEO
8 MANAGER
20 SALES REP
3 ROWS SELECTED.
ISQL> EXEC PROC1;
EXECUTE SUCCESS.
ISQL> SELECT ENO, EMP_JOB, SALARY FROM EMPLOYEES WHERE ENO=1 OR ENO=8 OR ENO=20;
ENO EMP_JOB SALARY
---------------------------------------------
1 CEO 5000
8 MANAGER 4500
20 SALES REP 4000
3 ROWS SELECTED.
LOOP#
구문#
기능#
LOOP 구문은 조건을 따로 지정하지 않고 반복적으로 구문(들)을 수행하고자 하는 경우에 사용된다.
그러나 LOOP 구문에서 EXIT 문을 사용하지 않게 되면 무한 LOOP에 빠져서 시스템에 문제를 일으킬 수 있으므로 주의한다.
예제#
CREATE TABLE item(id INTEGER, counter NUMBER(2));
CREATE OR REPLACE PROCEDURE proc1
AS
BEGIN
DECLARE
v_id item.id%TYPE := 501;
v_counter NUMBER(2) := 1;
BEGIN
LOOP
INSERT INTO item VALUES(v_id, v_counter);
v_counter := v_counter + 1;
EXIT WHEN v_counter > 10;
END LOOP;
END;
END;
/
iSQL> EXEC proc1;
Execute success.
iSQL> SELECT * FROM item;
ITEM.ID ITEM.COUNTER
----------------------------
501 1
501 2
…
501 9
501 10
10 rows selected.
WHILE LOOP#
구문#
기능#
조건이 참인 경우만 LOOP을 수행하고자 할 때 사용하는 반복문이다. 만약 처음부터 이 조건이 참이 아니면, WHILE 문은 한 번도 수행되지 않는다.
condition#
LOOP을 수행할 지의 여부를 결정하는 조건절을 명시한다. 조건절에는 SQL문의 WHERE 절에서 사용 가능한 모든 술어(predicate)들을 사용할 수 있다.
예제#
CREATE TABLE t1(i1 INTEGER, i2 INTEGER, i3 INTEGER);
CREATE OR REPLACE PROCEDURE proc1
AS
v1 INTEGER;
BEGIN
v1 := 1;
WHILE v1 < 3 LOOP
v1 := v1 + 1;
INSERT INTO t1 VALUES (v1, v1, v1);
IF v1 = 2 THEN
CONTINUE;
END IF;
END LOOP;
END;
/
iSQL> EXEC proc1;
Execute success.
iSQL> SELECT * FROM t1;
T1.I1 T1.I2 T1.I3
----------------------------------------
2 2 2
3 3 3
2 rows selected.
FOR LOOP#
구문#
기능#
FOR LOOP 문은 일정 횟수만큼 반복해서 구문(들)을 수행하고자 할 때 사용된다. 두 개의 점 ("..")을 사용해서 범위를 지정할 수 있는데 이는 FOR loop에 진입하기 전에 한 번만 체크된다. 범위의 작은 값과 큰 값을 같은 값으로 지정하면, loop내부는 한 번만 실행된다.
counter_name#
일정한 값만큼 증가, 혹은 감소시킬 변수를 하나 기술한다. 이 변수는 블록의 선언부에 선언할 필요가 없다. 또한 이 변수의 범위는 LOOP과 END LOOP으로 둘러싸여 있는 문장들에만 한정된다. FOR LOOP내에서 이 변수에 새로운 값을 할당할 수 없다.
REVERSE#
REVERSE 모드를 지정하면 counter_name 변수의 값을 upper_bound에서 lower_bound까지 감소시키면서 FOR 문을 수행한다.
lower_bound#
counter_name 변수가 가질 수 있는 값 중 가장 작은 값이다. 정수형 또는 정수형과 호환성이 있는 표현식을 지정하여야 한다.
여기에는 지역변수를 사용할 수도 있으나, 이 값은 FOR 문이 맨 처음으로 실행될 때에 딱 한번 그 값을 계산하여 저장해 두고 사용하므로, 추후 해당 지역변수를 변경하여도 FOR 문의 동작에는 영향을 미치지 않는다.
여기에 실수형을 명시하면 반올림된 정수값으로 변환된다.
upper_bound#
counter_name 변수가 가질 수 있는 값 중 가장 큰 값이다. lower_bound와 마찬가지로 정수형 또는 정수형과 호환성이 있는 표현식을 지정하여야 한다. 정수형이 아닌 수를 명시하면 반올림된 정수값으로 변환된다.
만약 FOR 문이 맨 처음으로 수행될 때 upper_bound가 lower_bound보다 작으면 어떠한 에러도 내지 않고 단지 FOR 문을 건너 뛰고 다음 문장을 수행한다.
lower_bound 처럼, 여기에는 지역변수를 사용할 수도 있으나, 이 값은 FOR 문이 맨 처음으로 실행될 때에 딱 한번 그 값을 계산하여 저장해 두고 사용하므로, 추후 해당 지역변수를 변경하여도 FOR 문의 동작에는 영향을 미치지 않는다.
step_size#
증가 또는 감소시킬 값을 지정한다. 생략하면 기본적으로 FOR 문은 counter_name 변수를 1씩 증, 혹은 감소 시킨다.
단, step size는 1보다 작을 수 없다. 정수형이 아닌 수를 명시한 경우에는 반올림된 정수값으로 변환된다.
예제#
예제1#
CREATE TABLE t6(i1 INTEGER, sum INTEGER);
CREATE OR REPLACE PROCEDURE proc1
AS
v1 INTEGER;
sum INTEGER := 0;
BEGIN
FOR i IN 1 .. 50 LOOP
v1 := 2 * i - 1;
sum := sum + v1;
INSERT INTO t6 VALUES(v1, sum);
END LOOP;
END;
/
iSQL> EXEC proc1;
Execute success.
iSQL> SELECT * FROM t6;
T6.I1 T6.SUM
---------------------------
1 1
3 4
5 9
…
97 2401
99 2500
50 rows selected.
예제2#
CREATE OR REPLACE PROCEDURE proc1
AS
eno_count INTEGER;
BEGIN
SELECT COUNT(eno) INTO eno_count FROM employees;
FOR i IN 1 .. eno_count LOOP
UPDATE employees SET salary = salary * 1.2 WHERE eno = i;
END LOOP;
END;
/
iSQL> SELECT eno, salary FROM employees WHERE eno in (11,12,13);
ENO SALARY
---------------------------
11 2750
12 1890
13 980
3 rows selected.
iSQL> EXEC proc1;
Execute success.
iSQL> SELECT eno, salary FROM employees WHERE eno IN (11,12,13);
ENO SALARY
---------------------------
11 3300
12 2268
13 1176
3 rows selected.
예제3#
CREATE TABLE t1(i1 INTEGER, i2 INTEGER, i3 INTEGER);
CREATE OR REPLACE PROCEDURE proc1
AS
BEGIN
<<a>>
INSERT INTO t1 VALUES (1,1,1);
IF 1 = 1 THEN
NULL;
END IF;
<<b>>
FOR v1 IN 1 .. 3 LOOP
<<c>>
FOR v1 IN 1 .. 3 LOOP
INSERT INTO t1 VALUES (b.v1, b.v1, c.v1);
END LOOP;
END LOOP;
END;
/
iSQL> EXEC proc1;
Execute success.
iSQL> SELECT * FROM t1;
T1.I1 T1.I2 T1.I3
----------------------------------------
1 1 1
1 1 1
1 1 2
1 1 3
2 2 1
2 2 2
2 2 3
3 3 1
3 3 2
3 3 3
10 rows selected.
--#####################
-- reverse
--#####################
CREATE TABLE t6(i1 INTEGER, sum INTEGER);
CREATE OR REPLACE PROCEDURE proc1
AS
sum INTEGER := 0;
BEGIN
FOR i IN reverse 1 .. 100 LOOP
sum := sum + i;
INSERT INTO t6 VALUES(i, sum);
END LOOP;
END;
/
iSQL> EXEC proc1;
Execute success.
iSQL> SELECT * FROM t6;
T6.I1 T6.SUM
---------------------------
100 100
99 199
98 297
…
3 5047
2 5049
1 5050
100 rows selected.
--#####################
-- step
--#####################
CREATE TABLE t6(i1 INTEGER, sum INTEGER);
CREATE OR REPLACE PROCEDURE proc1
AS
sum INTEGER := 0;
BEGIN
FOR i IN 1 .. 100 STEP 2 LOOP
sum := sum + i;
INSERT INTO t6 VALUES(i, sum);
END LOOP;
END;
/
iSQL> EXEC proc1;
Execute success.
iSQL> SELECT * FROM t6;
T6.I1 T6.SUM
---------------------------
1 1
3 4
5 9
…
97 2401
99 2500
50 rows selected.
EXIT#
구문#
기능#
EXIT 문을 감싸고 있는 가장 가까운 LOOP 문을 빠져나간다. 그러나 label_name이 명시적으로 주어진 경우에는 label_name이 정의된 LOOP을 빠져나간다.
LOOP 문 내부가 아닌 다른 블록에서 EXIT 문을 사용하면 오류가 발생한다.
<<outer>>
LOOP
...
LOOP
...
EXIT outer WHEN ... -- EXIT both LOOPs
END LOOP;
...
END LOOP outer;
EXIT WHEN count > 100;
IF count > 100 THEN
EXIT;
END IF;
EXIT 문은 아래의 LOOP 문 내에서 사용 가능하다.
-
LOOP
-
WHILE LOOP
-
FOR LOOP
-
CURSOR FOR LOOP
label_name#
EXIT 문을 감싸고 있는 가장 가까운 LOOP가 아닌 그보다 더 바깥의 LOOP으로 빠져나가야 하는 경우, 해당 LOOP의 바로 앞에 LABEL을 정의하고 그 이름을 여기에 명시한다.
WHEN condition#
특정 조건이 참인 경우 루프를 빠져나갈 수 있도록 WHEN 절에 조건식을 지정할 수 있다. 조건절에는 SELECT 문의 WHERE 절에서 사용 가능한 모든 술어들을 사용할 수 있다.
EXIT 문을 만나서 WHEN 절에 명시된 조건이 참이면 저장 프로시저는 가장 가까운 루프나 지정된 LABEL 블록문을 빠져 나가서 그 다음 문장을 수행한다.
EXIT WHEN은 간단한 IF 구문과 유사하다. 다음의 두 문장들은 동일한 기능을 한다.
EXIT WHEN count > 100;
IF count > 100 THEN
EXIT;
END IF;
예제#
CREATE TABLE stock(
gno BYTE(5) primary key,
stock INTEGER,
price numeric(10,2));
CREATE OR REPLACE PROCEDURE proc1
AS
CURSOR c1 IS SELECT gno, stock, price FROM goods;
rec1 c1%ROWTYPE;
BEGIN
OPEN c1;
LOOP
FETCH c1 INTO rec1;
IF c1%found THEN
IF rec1.stock > 0 AND rec1.stock < 1000 THEN
INSERT INTO stock VALUES(rec1.gno, rec1.stock, rec1.price);
END IF;
ELSIF c1%NOTFOUND THEN
EXIT;
END IF;
END LOOP;
CLOSE c1;
END;
/
iSQL> EXEC proc1;
Execute success.
iSQL> SELECT * FROM stock;
STOCK.GNO STOCK.STOCK STOCK.PRICE
----------------------------------------
A111100002 100 98000
B111100001 780 35800
D111100003 650 45100
E111100001 900 2290.54
E111100006 900 2338.62
5 rows selected.
--#####################
-- EXIT WHEN
--#####################
CREATE OR REPLACE PROCEDURE proc1
AS
CURSOR c1 IS SELECT gno, stock, price FROM goods;
rec1 c1%ROWTYPE;
BEGIN
OPEN c1;
IF c1%ISOPEN THEN
LOOP
FETCH c1 INTO rec1;
EXIT WHEN c1%NOTFOUND;
IF rec1.stock > 0 AND rec1.stock < 1000 THEN
INSERT INTO stock VALUES(rec1.gno, rec1.stock, rec1.price);
END IF;
END LOOP;
END IF;
CLOSE c1;
END;
/
iSQL> EXEC proc1;
Execute success.
iSQL> SELECT * FROM stock;
STOCK.GNO STOCK.STOCK STOCK.PRICE
----------------------------------------
A111100002 100 98000
B111100001 780 35800
D111100003 650 45100
E111100001 900 2290.54
E111100006 900 2338.62
5 rows selected.
CONTINUE#
구문#
기능#
현재 CONTINUE 문을 감싸고 있는 LOOP에서 CONTINUE 문 이후의 문장들을 전부 무시하고 LOOP의 맨 처음으로 되돌아간다.
CONTINUE 문을 사용할 수 있는 LOOP 문은 다음과 같다.
-
WHILE
-
FOR
-
CURSOR FOR
LOOP 문 내부가 아닌 다른 곳에서 CONTINUE 문을 사용하면 오류가 발생한다.
예제#
CREATE TABLE t8(i1 INTEGER, mathpower INTEGER default 0);
INSERT INTO t8(i1) VALUES(7);
INSERT INTO t8(i1) VALUES(3);
INSERT INTO t8(i1) VALUES(20);
INSERT INTO t8(i1) VALUES(15);
INSERT INTO t8(i1) VALUES(6);
INSERT INTO t8(i1) VALUES(1);
INSERT INTO t8(i1) VALUES(9);
CREATE OR REPLACE PROCEDURE proc1
AS
BEGIN
DECLARE
CURSOR c1 IS SELECT i1 FROM t8;
rec c1%ROWTYPE;
BEGIN
OPEN c1;
LOOP
FETCH c1 INTO rec;
EXIT WHEN c1%NOTFOUND;
IF power(rec.i1, rec.i1) > 50000 THEN
continue;
ELSE
UPDATE t8 SET mathpower = power(rec.i1, rec.i1)
WHERE i1 = rec.i1;
END IF;
END LOOP;
CLOSE c1;
END;
END;
/
iSQL> EXEC proc1;
Execute success.
iSQL> SELECT * FROM t8;
T8.I1 T8.MATHPOWER
----------------------------
7 0
20 0
15 0
9 0
3 27
6 46656
1 1
7 rows selected.
GOTO#
구문#
기능#
지정된 LABEL로 제어를 이동하는 분기문이다.
label_name#
제어를 이동할 LABEL의 이름이다.
제약 사항#
GOTO는 다음과 같은 제약사항을 가진다.
-
IF 나 CASE 블록 내에서 사용될 때, THEN, ELS(E)IF, ELSE 또는 WHEN 문에 해당하는 블록 안으로 이동할 수 없다. 따라서 아래 예제의 경우 에러를 출력한다.
CREATE OR REPLACE PROCEDURE PROC1 AS V1 INTEGER; BEGIN V1 := 1; IF V1 = 1 THEN GOTO LABEL1; ELSE <<LABEL1>> PRINTLN(V1); END IF; END; / [ERR-3120F : Illegal GOTO statement. In PROC1 0007 : GOTO LABEL1; ^ ^ ]
-
외부 블록에서 내부 블록으로 이동할 수 없다. 모든 BEGIN/END 블록 및 LOOP 문장에 이 제약 조건이 적용된다.
CREATE OR REPLACE PROCEDURE PROC1 AS V1 INTEGER; BEGIN V1 := 1; DECLARE V2 INTEGER; BEGIN <<LABEL1>> V2 := 1; END; GOTO LABEL1; END; / [ERR-3120F : Illegal GOTO statement. In PROC1 0012 : GOTO LABEL1; ^ ^ ]
예제#
<예제 1> 예외 처리부에서 분기하는 경우, 그 예외 처리부가 속한 블록으로는 분기할 수 없다. 따라서 다음의 예제1은 에러를 반환한다.
CREATE OR REPLACE PROCEDURE PROC1
AS
E1 EXCEPTION;
BEGIN
RAISE E1;
<<LABEL1>>
PRINTLN('END');
EXCEPTION
WHEN E1 THEN
GOTO LABEL1;
END;
/
[ERR-3120F : Illegal GOTO statement.
In PROC1
0010 : GOTO LABEL1;
^ ^
]
<예제 2> 그러나, 예외 처리부로부터 그 상위 블록으로의 분기는 가능하다. 예제2 의 경우 V1이 5가 될 때까지 EXCEPTION을 4번 발생시키고 정상으로 종료한다.
CREATE OR REPLACE PROCEDURE PROC1
AS
E1 EXCEPTION;
V1 INTEGER;
BEGIN
V1 := 1;
<<LABEL1>>
V1 := V1 + 1;
PRINTLN('BLOCK1');
BEGIN
PRINTLN('BLOCK2');
PRINTLN(V1);
IF V1 = 5 THEN
PRINTLN('goto label2 '||v1);
GOTO LABEL2;
ELSE
RAISE E1;
END IF;
EXCEPTION
WHEN E1 THEN
PRINTLN('goto label1 '|| v1);
GOTO LABEL1;
END;
<<LABEL2>>
PRINTLN('BLOCK1 AFTER BLOCK2');
END;
/
iSQL> EXEC PROC1;
BLOCK1
BLOCK2
2
goto label1 2
BLOCK1
BLOCK2
3
goto label1 3
BLOCK1
BLOCK2
4
goto label1 4
BLOCK1
BLOCK2
5
goto label2 5
BLOCK1 AFTER BLOCK2
Execute success.
NULL#
구문#
기능#
NULL 문은 흐름에 영향을 미치지 않고 아무것도 수행하지 않고 다음으로 넘어감을 명시적으로 나타내기 위해서 사용한다. 프로그램의 가독성을 높이기 위해서 사용한다.
예제#
CREATE OR REPLACE PROCEDURE bonus (amount NUMBER(10,2))
AS
CURSOR c1 IS SELECT eno, sum(qty) FROM orders group by eno;
order_eno orders.eno%TYPE;
order_qty orders.qty%TYPE;
BEGIN
OPEN c1;
LOOP
FETCH c1 INTO order_eno, order_qty;
EXIT WHEN c1%NOTFOUND;
IF order_qty > 20000 THEN
UPDATE employees SET salary = salary + amount
WHERE eno = order_eno;
ELSE
NULL;
END IF;
END LOOP;
CLOSE c1;
END;
/
iSQL> SELECT e.eno, salary, sum(qty)
FROM employees e, orders o
WHERE e.eno = o.eno
group by e.eno, salary;
ENO SALARY SUM(QTY)
-----------------------------------------------
12 1890 17870
19 1800 25350
20 13210
3 rows selected.
iSQL> EXEC bonus(75);
Execute success.
iSQL> SELECT eno, salary FROM employees WHERE eno = 19;
ENO SALARY
---------------------------
19 1875
1 row selected.