プログラム・フローの制御
入力順に実行する文であるSQLとは異なり、PL/SQLには、プログラムのフローを制御できる制御文があります。
制御文について
PL/SQLには、条件付き選択文、繰り返し文および順次制御文の3つのカテゴリの制御文があります。
条件付き選択文では、異なるデータ値に対して異なる文を実行できます。条件選択文は、IFとCASEです。
Loop文では、一連の異なるデータ値で同じ文を繰り返すことができます。ループ文は、FOR LOOP、WHILE LOOPおよび基本LOOPです。EXIT文は、ループの終わりまで制御を転送します。CONTINUE文は、現行のループの反復を終了し、制御を次の反復に転送します。EXITとCONTINUEの両方で、条件を指定できるオプションのWHEN句があります。
順次制御文では、指定されたラベル付き文に移動するか、または何も処理を行いません。順次制御文は、GOTOおよびNULLです。
関連項目: PL/SQL制御文の概要は、『Oracle Database PL/SQL言語リファレンス』
IF文の使用
IF文は、ブール式の値に応じて、一連の文を実行またはスキップします。
IF文には次の構文があります。
IF boolean_expression THEN statement [, statement ]
[ ELSIF boolean_expression THEN statement [, statement ] ]...
[ ELSE statement [, statement ] ]
END IF;
会社が従業員を雇用後の最初の10年間は1年に2回評価し、その後は1年に1回評価すると仮定します。従業員の評価頻度を戻す関数が必要です。例5-4のように、IF文を使用してファンクションの戻り値を判断できます。
EVAL_FREQUENCY関数をEMP_EVALパッケージの本体に追加しますが、仕様には 追加しません。EVAL_FREQUENCYは、仕様にはないため、パッケージに対してローカルであり、パッケージの外部からではなく、パッケージ内の他のサブプログラムでのみ起動できます。
ヒント: SQL文でPL/SQL変数を使用する場合、例5-4の2つ目のSELECT文に示すとおり、変数をサブプログラム名で修飾して、表の列および間違えないようにします。
関連情報:
-
IF文の構文は、Oracle Database PL/SQL言語リファレンスを参照
-
IF文の使用に関する詳細は、Oracle Database PL/SQL言語リファレンスを参照してください。
例5-4 ファンクションの戻り値を判断するIF文
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文は、一連の条件から構成され、対応する文を実行します。
Simple CASE文は、1つの式を評価し、それをいくつかの潜在的な値と比較します。CASE文には、この構文があります。
CASE expression
WHEN value THEN statement
[ WHEN value THEN statement ]...
[ ELSE statement [, statement ]... ]
END CASE;
検索されたCASE文は、複数のブール式を評価し、その値がTRUEである最初のものを選択します。検索されたCASE文の詳細は、Oracle Database PL/SQL言語リファレンスに関する項を参照してください。
ヒント: CASE文またはネストされたIF文を使用する場合、CASE文を使用します。つまり、CASE文は、読取りやすく、効率的です。
従業員が1年に1回のみ評価され、JOB_IDに応じて昇給を提案するEVAL_FREQUENCY関数を必要とすると仮定します。
例5-5に示すとおり、EVAL_FREQUENCYファンクションを変更します。(文字列DBMS_OUTPUT.PUT_LINEを出力する手順の詳細は、『Oracle Database PL/SQLパッケージおよびタイプ・リファレンス』を参照。)
例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;
関連情報:
-
CASE文の構文は、Oracle Database PL/SQL言語リファレンスを参照
-
CASE文の使用に関する詳細は、Oracle Database PL/SQL言語リファレンスを参照してください。
FOR LOOP文の使用
FOR LOOP文は、lower_boundからupper_boundの範囲の整数ごとに、一連の文を1回繰り返します。
FOR LOOPの構文は次のとおりです。
FOR counter IN lower_bound..upper_bound LOOP
statement [, statement ]...
END LOOP;
LOOPとEND LOOPの間のステートメントはカウンタを使用できますが、その値を変更することはできません。
給与の値上げを想定するだけでなく、EVAL_FREQUENCYファンクションを使用して、5年間推定額が増加した場合に給与がどう変わるかをレポートするとします。
例5-6に示すとおり、EVAL_FREQUENCYファンクションを変更します。(文字列DBMS_OUTPUT.PUT_LINEを出力するプロシージャの詳細は、Oracle Database PL/SQLパッケージ・プロシージャおよびタイプ・リファレンスを参照。)
例5-6 5年後の給与を計算するFOR 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;
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;
関連情報:
-
FOR LOOP文の構文は、Oracle Database PL/SQL言語リファレンスを参照
-
FOR LOOP文の使用に関する詳細は、Oracle Database PL/SQL言語リファレンスを参照してください。
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の最大給与を超過したときに終了するとします。
例5-7に示すように、EVAL_FREQUENCYファンクションを変更します。(文字列DBMS_OUTPUT.PUT_LINEを出力する手順の詳細は、『Oracle Database PL/SQLパッケージおよびタイプ・リファレンス』を参照。)
例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;
関連情報:
-
WHILE LOOP文の構文は、Oracle Database PL/SQL言語リファレンスを参照
-
WHILE LOOP文の使用に関する詳細は、Oracle Database PL/SQL言語リファレンスを参照してください。
基本LOOPおよびEXIT WHEN文の使用
基本LOOP文は、一連の文を繰り返します。
基本的なLOOP文の構文は次のとおりです。
LOOP
statement [, statement ]...
END LOOP;
少なくとも1つのステートメントがEXITステートメントである必要があり、そうでない場合、LOOPステートメントは無期限に実行されます。
EXIT WHEN文(オプションのWHEN句があるEXIT文)は、条件がTRUEで、制御をループの終了に転送すると、ループを終了します。
EVAL_FREQUENCYファンクションでは、WHILE LOOP文の最後の反復で、通常、最後に計算された値が最大給与を超過します。
例5-8に示すように、WHILE LOOP文を、EXIT WHEN文を含む基本の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;
関連情報:
-
LOOP文の構文は、Oracle Database PL/SQL言語リファレンスを参照
-
EXIT文の構文は、Oracle Database PL/SQL言語リファレンスを参照
-
LOOP文とEXIT文の使用に関する詳細は、Oracle Database PL/SQL言語リファレンスを参照してください。