프로그램 흐름 제어

명령문을 입력된 순서대로 실행하는 SQL과 달리 PL/SQL에는 프로그램 흐름을 제어할 수 있는 제어 명령문이 있습니다.

제어 명령문 정보

PL/SQL에는 조건부 선택 문, 루프 문 및 순차 제어문의 세 가지 제어문 범주가 있습니다.

조건부 선택 명령문 - 다른 데이터 값에 대해 서로 다른 명령문을 실행할 수 있습니다. 조건부 선택문은 IFCASE입니다.

루프 명령문 - 일련의 다른 데이터 값에서 동일한 명령문을 반복할 수 있습니다. 루프 명령문은 FOR LOOP, WHILE LOOP 및 기본 LOOP입니다. EXIT 문은 제어권을 루프 끝으로 전달하고 CONTINUE 문은 루크의 현재 반복을 종료하고 다음 반복으로 제어권을 전달합니다. EXITCONTINUE에는 모두 조건을 지정할 수 있는 선택적 WHEN 절이 있습니다.

순차적 제어 명령문 - 지정된 레이블이 있는 명령문으로 이동하거나 작업을 수행하지 않을 수 있습니다. 순차 제어문은 GOTONULL입니다.

참조: PL/SQL 제어문의 개요는 Oracle Database PL/SQL Language Reference

IF 문 사용

IF 문은 부울 표현식의 값에 따라 명령문 시퀀스를 실행하거나 건너뜁니다.

IF 문의 구문은 다음과 같습니다.

IF boolean_expression THEN statement [, statement ]
[ ELSIF boolean_expression THEN statement [, statement ] ]...
[ ELSE  statement [, statement ] ]
END IF;

회사가 직원을 고용한 후 첫 10년 간은 매년 두 번씩 평가하지만 그 이후에는 매년 한 번만 평가한다고 가정합니다. 그래서 직원의 평가 빈도를 반환하는 함수가 필요합니다. IF 문을 사용하여 함수의 반환 값을 결정할 수 있습니다(예: Example 5-4).

EVAL_FREQUENCY 함수를 EMP_EVAL 패키지의 본문에 추가하고 사양에 추가하지 않습니다. EVAL_FREQUENCY는 사양에 없으므로 패키지의 로컬입니다. 즉, 패키지 외부에서 호출할 수는 없고 패키지의 다른 하위 프로그램에서만 호출될 수 있습니다.

참고: Example 5-4의 두번째 SELECT 문에서와 같이 SQL 문에서 PL/SQL 변수를 사용하는 경우 변수에 서브 프로그램 이름을 한정하여 테이블 열에 대해 오류가 발생하지 않도록 하십시오.

참조:

함수의 반환 값을 결정하는 IF 문 예 5-4

FUNCTION eval_frequency (emp_id IN EMPLOYEES.EMPLOYEE_ID%TYPE)
  RETURN PLS_INTEGER
AS
  h_date     EMPLOYEES.HIRE_DATE%TYPE;
  today      EMPLOYEES.HIRE_DATE%TYPE;
  eval_freq  PLS_INTEGER;
BEGIN
  SELECT SYSDATE INTO today FROM DUAL;

  SELECT HIRE_DATE INTO h_date
  FROM EMPLOYEES
  WHERE EMPLOYEE_ID = eval_frequency.emp_id;

  IF ((h_date + (INTERVAL '120' MONTH)) < today) THEN
    eval_freq := 1;
  ELSE
    eval_freq := 2;
  END IF;

  RETURN eval_freq;
END eval_frequency;

CASE 문 사용

CASE 문은 조건 시퀀스에서 선택하여 해당 명령문을 실행합니다.

단순 CASE 문은 단일 표현식을 평가하고 여러 잠재적 값과 비교합니다. 이 문의 구문은 다음과 같습니다.

CASE expression
WHEN value THEN statement
[ WHEN value THEN statement ]...
[ ELSE statement [, statement ]... ]
END CASE;

검색된 CASE 문은 여러 부울 표현식을 평가하고 값이 TRUE인 첫번째 표현품을 선택합니다. 검색된 CASE 문에 대한 자세한 내용은 Oracle Database PL/SQL Language Reference를 참조하세요.

팁: CASE 문이나 중첩 IF 문을 사용할 수 있는 경우 CASE 문을 사용하십시오. 이렇게 읽기도 쉬울 뿐더 아니라 더 효율적입니다.

직원을 매년 한 번만 평가하는 경우 EVAL_FREQUENCY 함수를 사용하여 JOB_ID에 따라 임금 인상을 제안하려고 한다고 가정합니다.

Example 5-5에 표시된 대로 EVAL_FREQUENCY 함수를 변경합니다. 문자열을 인쇄하는 프로시저 DBMS_OUTPUT.PUT_LINE에 대한 자세한 내용은 Oracle Database PL/SQL Packages and Types Reference을 참조하십시오.

인쇄할 문자열을 결정하는 5-5 CASE 문 예

FUNCTION eval_frequency (emp_id IN EMPLOYEES.EMPLOYEE_ID%TYPE)
  RETURN PLS_INTEGER
AS
  h_date     EMPLOYEES.HIRE_DATE%TYPE;
  today      EMPLOYEES.HIRE_DATE%TYPE;
  eval_freq  PLS_INTEGER;
  j_id       EMPLOYEES.JOB_ID%TYPE;

BEGIN
  SELECT SYSDATE INTO today FROM DUAL;

  SELECT HIRE_DATE, JOB_ID INTO h_date, j_id
  FROM EMPLOYEES
  WHERE EMPLOYEE_ID = eval_frequency.emp_id;

  IF ((h_date + (INTERVAL '12' MONTH)) < today) THEN
    eval_freq := 1;

    CASE j_id
       WHEN 'PU_CLERK' THEN DBMS_OUTPUT.PUT_LINE(
         'Consider 8% salary increase for employee # ' || emp_id);
       WHEN 'SH_CLERK' THEN DBMS_OUTPUT.PUT_LINE(
         'Consider 7% salary increase for employee # ' || emp_id);
       WHEN 'ST_CLERK' THEN DBMS_OUTPUT.PUT_LINE(
         'Consider 6% salary increase for employee # ' || emp_id);
       WHEN 'HR_REP' THEN DBMS_OUTPUT.PUT_LINE(
         'Consider 5% salary increase for employee # ' || emp_id);
       WHEN 'PR_REP' THEN DBMS_OUTPUT.PUT_LINE(
         'Consider 5% salary increase for employee # ' || emp_id);
       WHEN 'MK_REP' THEN DBMS_OUTPUT.PUT_LINE(
         'Consider 4% salary increase for employee # ' || emp_id);
       ELSE DBMS_OUTPUT.PUT_LINE(
         'Nothing to do for employee #' || emp_id);
    END CASE;
  ELSE
    eval_freq := 2;
  END IF;

  RETURN eval_freq;
END eval_frequency;

참조:

FOR LOOP 문 사용

FOR LOOP 문은 lower_bound에서 upper_bound 사이 범위의 각 정수에 대해 한 번씩 명령문 시퀀스를 반복합니다.

FOR LOOP의 구문은 다음과 같습니다.

FOR counter IN lower_bound..upper_bound LOOP
  statement [, statement ]...
END LOOP;

LOOP 및 END LOOP 사이의 명령문에서 카운터를 사용할 수 있지만 그 값을 변경할 수는 없습니다.

봉급 인상을 제안만 하는 것이 아니라 EVAL_FREQUENCY 함수를 사용하여 5년 동안 매년 제안된 양만큼 증가할 경우 임금이 얼마가 되는지를 보고하려고 가정합니다.

Example 5-6에 표시된 대로 EVAL_FREQUENCY 함수를 변경합니다. 문자열을 인쇄하는 프로시저 DBMS_OUTPUT.PUT_LINE에 대한 자세한 내용은 Oracle Database PL/SQL Packages and Types Reference을 참조하십시오.

5년 후의 봉급을 계산하는 FOR LOOP 문 예 5-6

FUNCTION eval_frequency (emp_id IN EMPLOYEES.EMPLOYEE_ID%TYPE)
  RETURN PLS_INTEGER
AS
  h_date      EMPLOYEES.HIRE_DATE%TYPE;
  today       EMPLOYEES.HIRE_DATE%TYPE;
  eval_freq   PLS_INTEGER;
  j_id        EMPLOYEES.JOB_ID%TYPE;
  sal         EMPLOYEES.SALARY%TYPE;
  sal_raise   NUMBER(3,3) := 0;

BEGIN
  SELECT SYSDATE INTO today FROM DUAL;

  SELECT HIRE_DATE, JOB_ID, SALARY INTO h_date, j_id, sal
  FROM EMPLOYEES
  WHERE EMPLOYEE_ID = eval_frequency.emp_id;

  IF ((h_date + (INTERVAL '12' MONTH)) < today) THEN
    eval_freq := 1;

    CASE j_id
      WHEN 'PU_CLERK' THEN sal_raise := 
0.08;
      WHEN 'SH_CLERK' THEN sal_raise := 0.07;
      WHEN 'ST_CLERK' THEN sal_raise := 0.06;
      WHEN 'HR_REP'   THEN sal_raise := 0.05;
      WHEN 'PR_REP'   THEN sal_raise := 0.05;
      WHEN 'MK_REP'   THEN sal_raise := 0.04;
      ELSE NULL;
    END CASE;

    IF (sal_raise != 0) THEN
      BEGIN
        DBMS_OUTPUT.PUT_LINE('If salary ' || sal || ' increases by ' ||
          ROUND((sal_raise * 100),0) ||
          '% each year for 5 years, it will be:');

        FOR i IN 1..5 LOOP
          sal := sal * (1 + sal_raise);
          DBMS_OUTPUT.PUT_LINE(ROUND(sal, 2) || ' after ' || i || ' year(s)');
        END LOOP;
      END;
    END IF;

  ELSE
    eval_freq := 2;
  END IF;

  RETURN eval_freq;
END eval_frequency;

참조:

WHILE LOOP 문 사용

WHILE LOOP 문은 조건이 TRUE인 동안 명령문 시퀀스를 반복합니다.

WHILE LOOP 문의 구문은 다음과 같습니다.

WHILE condition LOOP
  statement [, statement ]...
END LOOP;

주: LOOP와 END LOOP 사이의 명령문으로 인해 조건이 FALSE가 되지 않는 경우 WHILE LOOP 문은 무기한 실행됩니다.

EVAL_FREQUENCY 함수가 FOR LOOP 문 대신 WHILE LOOP 문을 사용하고 제안된 급여가 JOB_ID의 최대 급여를 초과하면 끝난다고 가정합니다.

Example 5-7에 표시된 대로 EVAL_FREQUENCY 함수를 변경합니다. 문자열을 인쇄하는 프로시저 DBMS_OUTPUT.PUT_LINE에 대한 자세한 내용은 Oracle Database PL/SQL Packages and Types Reference을 참조하십시오.

최대값까지 급여를 계산하는 5-7 WHILE LOOP 문 예

FUNCTION eval_frequency (emp_id IN EMPLOYEES.EMPLOYEE_ID%TYPE)
  RETURN PLS_INTEGER
AS
  h_date      EMPLOYEES.HIRE_DATE%TYPE;
  today       EMPLOYEES.HIRE_DATE%TYPE;
  eval_freq   PLS_INTEGER;
  j_id        EMPLOYEES.JOB_ID%TYPE;
  sal         EMPLOYEES.SALARY%TYPE;
  sal_raise   NUMBER(3,3) := 0;
  sal_max     JOBS.MAX_SALARY%TYPE;

BEGIN
  SELECT SYSDATE INTO today FROM DUAL;

  SELECT HIRE_DATE, j.JOB_ID, SALARY, MAX_SALARY INTO h_date, j_id, sal, sal_max
  FROM EMPLOYEES e, JOBS j
  WHERE EMPLOYEE_ID = eval_frequency.emp_id AND JOB_ID = eval_frequency.j_id;

  IF ((h_date + (INTERVAL '12' MONTH)) < today) THEN
    eval_freq := 1;

    CASE j_id
      WHEN 'PU_CLERK' THEN sal_raise := 0.08;
      WHEN 'SH_CLERK' THEN sal_raise := 0.07;
      WHEN 'ST_CLERK' THEN sal_raise := 0.06;
      WHEN 'HR_REP'   THEN sal_raise := 0.05;
      WHEN 'PR_REP'   THEN sal_raise := 0.05;
      WHEN 'MK_REP'   THEN sal_raise := 0.04;
      ELSE NULL;
    END CASE;

    IF (sal_raise != 0) THEN
      BEGIN
        DBMS_OUTPUT.PUT_LINE('If salary ' || sal || ' increases by ' ||
          ROUND((sal_raise * 100),0) ||
          '% each year, it will be:');

        WHILE sal <= sal_max LOOP
          sal := sal * (1 + sal_raise);
          DBMS_OUTPUT.PUT_LINE(ROUND(sal, 2));
        END LOOP;

        DBMS_OUTPUT.PUT_LINE('Maximum salary for this job is ' || sal_max);
      END;
    END IF;
  ELSE
    eval_freq := 2;
  END IF;

  RETURN eval_freq;
END eval_frequency;

참조:

기본 LOOP 및 EXIT WHEN 문 사용

기본 LOOP 문은 명령문 시퀀스를 반복하며

기본 LOOP 문의 구문은 다음과 같습니다.

LOOP
  statement [, statement ]...
END LOOP;

적어도 하나의 명령문은 EXIT 문이어야 합니다. 그렇지 않으면 LOOP 문이 무한히 실행됩니다.

EXIT WHEN 문(선택적 WHEN 절이 포함된 EXIT 문)은 조건이 TRUE일 때 루프를 종료하고 제어권을 루프 끝으로 전달합니다.

EVAL_FREQUENCY 함수에서 WHILE LOOP 문의 마지막 반복 시 마지막으로 계산되는 값은 일반적으로 최대 급여를 초과합니다.

Example 5-8과 같이 EXIT WHEN 문을 포함하는 기본 LOOP 문으로 WHILE LOOP 문을 변경합니다.

예제 5-8 EXIT WHEN 문 사용

FUNCTION eval_frequency (emp_id IN EMPLOYEES.EMPLOYEE_ID%TYPE)
  RETURN PLS_INTEGER
AS
  h_date      EMPLOYEES.HIRE_DATE%TYPE;
  today       EMPLOYEES.HIRE_DATE%TYPE;
  eval_freq   PLS_INTEGER;
  j_id        EMPLOYEES.JOB_ID%TYPE;
  sal         EMPLOYEES.SALARY%TYPE;
  sal_raise   NUMBER(3,3) := 0;
  sal_max     JOBS.MAX_SALARY%TYPE;

BEGIN
  SELECT SYSDATE INTO today FROM DUAL;

  SELECT HIRE_DATE, j.JOB_ID, SALARY, MAX_SALARY INTO h_date, j_id, sal, sal_max
  FROM EMPLOYEES e, JOBS j
  WHERE EMPLOYEE_ID = eval_frequency.emp_id AND JOB_ID = eval_frequency.j_id;

  IF ((h_date + (INTERVAL '12' MONTH)) < today) THEN
    eval_freq := 1;

    CASE j_id
      WHEN 'PU_CLERK' THEN sal_raise := 0.08;
      WHEN 'SH_CLERK' THEN sal_raise := 0.07;
      WHEN 'ST_CLERK' THEN sal_raise := 0.06;
      WHEN 'HR_REP'   THEN sal_raise := 0.05;
      WHEN 'PR_REP'   THEN sal_raise := 0.05;
      WHEN 'MK_REP'   THEN sal_raise := 0.04;
      ELSE NULL;
    END CASE;

    IF (sal_raise != 0) THEN
      BEGIN
        DBMS_OUTPUT.PUT_LINE('If salary ' || sal || ' increases by ' ||
          ROUND((sal_raise * 100),0) ||
          '% each year, it will be:');

        LOOP
          sal := sal * (1 + sal_raise);
          EXIT WHEN sal > sal_max;
          DBMS_OUTPUT.PUT_LINE(ROUND(sal,2));
        END LOOP;

        DBMS_OUTPUT.PUT_LINE('Maximum salary for this job is ' || sal_max);
      END;
    END IF;
  ELSE
    eval_freq := 2;
  END IF;

  RETURN eval_freq;
END eval_frequency;

참조: