ヘッダーをスキップ

Oracle Database PL/SQLユーザーズ・ガイドおよびリファレンス
10g リリース2(10.2)

B19257-01
目次
目次
索引
索引

戻る 次へ

4 PL/SQLの制御構造の使用

この章では、PL/SQLプログラムの制御の流れを構造化する方法を示します。PL/SQLで提供される条件テスト、ループおよび分岐を使用すると、優れた構造を持つプログラムを作成できます。

この章の項目は、次のとおりです。

PL/SQLの制御構造の概要

図4-1に、手続き型コンピュータ・プログラムで使用される基本制御構造を示します。

図4-1    制御構造


画像の説明

選択構造は、条件をテストし、条件の真偽に応じて一連の文を実行します。条件とは、BOOLEAN値(TRUEまたはFALSE)を戻す任意の変数または式です。反復構造は、ある条件が真の間、一連の文を繰り返して実行します。順次構造は、一連の文を、出現する順番にそのまま実行します。

条件テスト: IF文およびCASE文

IF文は、条件の値に応じて、異なる一連の文を実行します。IF文には、IF-THENIF-THEN-ELSEおよびIF-THEN-ELSIFの3つの形式があります。IF文の構文の説明は、「IF文」を参照してください。

CASE文は、単一の条件を評価して多数の代替アクションから選択するコンパクトな手段です。3つ以上の選択肢がある場合は、CASE文が有効です。CASE文の構文の説明は、「CASE文」を参照してください。

IF-THEN文の使用

IF文の最も単純な形式であるIF-THENは、例4-1で示すように、キーワードTHENEND IFENDIFではない)によって囲まれた一連の文に条件を関連付けます。

一連の文は、条件がTRUEである場合にのみ実行されます。条件がFALSEまたはNULLである場合、IF文は何も実行しません。いずれの場合も、制御は次の文に渡されます。

例4-1    単純なIF-THEN文の使用

DECLARE
  sales  NUMBER(8,2) := 10100;
  quota  NUMBER(8,2) := 10000;
  bonus  NUMBER(6,2);
  emp_id NUMBER(6) := 120;
BEGIN
  IF sales > (quota + 200) THEN
     bonus := (sales - quota)/4;
     UPDATE employees SET salary = salary + bonus WHERE employee_id = emp_id;
  END IF;
END;
/

IF-THEN-ELSE文の使用

IF文の2つ目の形式であるIF-THEN-ELSEは、例4-2で示すように、キーワードELSEを追加し、その後に一連の代替文を続けます。

ELSE句の中の文は、条件がFALSEまたはNULLに評価された場合にのみ実行されます。IF-THEN-ELSE文を使用すると、一連の文のどちらかが確実に実行されます。

例4-2    単純なIF-THEN-ELSE文の使用

DECLARE
  sales  NUMBER(8,2) := 12100;
  quota  NUMBER(8,2) := 10000;
  bonus  NUMBER(6,2);
  emp_id NUMBER(6) := 120;
BEGIN
  IF sales > (quota + 200) THEN
     bonus := (sales - quota)/4;
  ELSE
     bonus := 50;
  END IF;
  UPDATE employees SET salary = salary + bonus WHERE employee_id = emp_id;
END;
/

IF文は、例4-3で示すようにネストできます。

例4-3    IF文のネスト

DECLARE
  sales  NUMBER(8,2) := 12100;
  quota  NUMBER(8,2) := 10000;
  bonus  NUMBER(6,2);
  emp_id NUMBER(6) := 120;
BEGIN
  IF sales > (quota + 200) THEN
     bonus := (sales - quota)/4;
  ELSE
     IF sales > quota THEN
        bonus := 50;
     ELSE
        bonus := 0;
     END IF;
  END IF;
  UPDATE employees SET salary = salary + bonus WHERE employee_id = emp_id;
END;
/

IF-THEN-ELSEIF文の使用

複数の選択肢から選択する必要がある場合があります。キーワードELSIFELSEIFまたはELSE IFではない)を使用すると、例4-4で示すように条件を追加できます。

最初の条件がFALSEまたはNULLの場合、ELSIF句は別の条件をテストします。IF文は任意の数のELSIF句を持つことができます。最後のELSE句はオプションです。条件は上から下に向かって1つずつ評価されます。いずれかの条件がTRUEである場合は、それに対応する一連の文が実行され、制御は次の文に移ります。すべての条件がFALSEまたはNULLである場合、例4-4で示すように、ELSE句の一連の文が実行されます。

例4-4    IF-THEN-ELSEIF文の使用

DECLARE
  sales  NUMBER(8,2) := 20000;
  bonus  NUMBER(6,2);
  emp_id NUMBER(6) := 120;
BEGIN
   IF sales > 50000 THEN
      bonus := 1500;
   ELSIF sales > 35000 THEN
      bonus := 500;
   ELSE
      bonus := 100;
   END IF;
   UPDATE employees SET salary = salary + bonus WHERE employee_id = emp_id;
END;
/

salesの値が50000よりも大きい場合は、1番目と2番目の条件がTRUEになります。ただし、2番目の条件はテストされないため、bonusには1500という正しい値が代入されます。1番目の条件がTRUEに評価されると、それに対応する文が実行され、制御はUPDATE文に移ります。

例4-5IF-THEN-ELSE文の別の例です。

例4-5    IF-THEN文の拡張

DECLARE
  grade CHAR(1);
BEGIN
  grade := 'B';
  IF grade = 'A' THEN
    DBMS_OUTPUT.PUT_LINE('Excellent');
  ELSIF grade = 'B' THEN
    DBMS_OUTPUT.PUT_LINE('Very Good');
  ELSIF grade = 'C' THEN
    DBMS_OUTPUT.PUT_LINE('Good');
  ELSIF grade = 'D' THEN
    DBMS_OUTPUT. PUT_LINE('Fair');
  ELSIF grade = 'F' THEN
    DBMS_OUTPUT.PUT_LINE('Poor');
  ELSE
    DBMS_OUTPUT.PUT_LINE('No such grade');
  END IF;
ENd;
/

CASE文の使用

IF文と同様に、CASE文では一連の文を選択して実行できます。ただし、CASE文では、順序を選択するために複数のブール式ではなく選択子を使用します。選択子は複数の選択肢から1つ選択するために値が使用される式です。

IF文とCASE文を比較するために、学業成績の説明を出力する例4-5のコードを考えます。5つのブール式があることに注意してください。各インスタンスで、同じ変数gradeが5つの値'A''B''C''D'または'F'のどれかをテストしています。例4-5のコードを、例4-6に示すようにCASE文を使用して書き直します。

例4-6    CASE-WHEN文の使用

DECLARE
  grade CHAR(1);
BEGIN
  grade := 'B';
  CASE grade
    WHEN 'A' THEN DBMS_OUTPUT.PUT_LINE('Excellent');
    WHEN 'B' THEN DBMS_OUTPUT.PUT_LINE('Very Good');
    WHEN 'C' THEN DBMS_OUTPUT.PUT_LINE('Good');
    WHEN 'D' THEN DBMS_OUTPUT.PUT_LINE('Fair');
    WHEN 'F' THEN DBMS_OUTPUT.PUT_LINE('Poor');
    ELSE DBMS_OUTPUT.PUT_LINE('No such grade');
  END CASE;
END;
/

CASE文の方が読みやすく効率的です。長いIF-THEN-ELSIF文はできるかぎりCASE文として書き直してください。

CASE文は、キーワードCASEで始まります。キーワードの後に選択子(前述の例では変数grade)があります。選択子式は、どんなに複雑でもかまいません。たとえば、ファンクション・コールを含めることができます。ただし、通常は、1個の変数で構成されています。選択子式が評価されるのは1度のみです。生成される値は、BLOBBFILE、オブジェクト型、PL/SQLレコード、索引付き表またはVARRAY、ネストした表以外であれば、どのようなPL/SQLデータ型でもかまいません。

選択子の後に1つ以上のWHEN句があり、各句が順番にチェックされます。選択子の値によって、どの句が実行されるかが決定されます。選択子の値がWHEN句の式の値と等しければ、そのWHEN句が実行されます。たとえば、最後の例では、grade'C'であれば、'Good'が出力されます。実行が失敗することはなく、WHEN句が1つでも実行されると、制御が次の文に渡されます。

ELSE句の機能は、IF文のELSE句に似ています。前述の例では、学年がWHEN句のオプションの1つでなければ、ELSE句が選択され、'No such grade'という句が出力されます。ELSE句はオプションです。ただし、ELSE句を省略すると、PL/SQLでは次の暗黙的なELSE句が追加されます。

ELSE RAISE CASE_NOT_FOUND;

ELSE句を省略しても、常にデフォルト・アクションがあります。CASE文がどのWHEN句にも一致せず、ELSE句を省略している場合、PL/SQLは事前定義された例外CASE_NOT_FOUNDを呼び出します。

CASE文は、キーワードEND CASEで終了します。この2つのキーワードは、空白で区切る必要があります。CASE文の書式は、次のとおりです。

PL/SQLブロックと同様に、CASE文にもラベルを付けることができます。ラベルは二重の山カッコで囲んだ未宣言の識別子で、CASE文の先頭に置きます。オプションとして、CASE文の末尾にもラベル名を付けることができます。

CASE文の実行中に呼び出された例外は、通常の方法で処理されます。つまり、通常の実行は中止され、PL/SQLブロックまたはサブプログラムの例外処理部に制御が移ります。

CASE文はCASE式の代替であり、各WHEN句が式になっています。詳細は、「CASE式」を参照してください。

検索CASE文

PL/SQLには、単純なCASE文と同様の検索CASE文も用意されています。その書式は例4-7で示すとおりです。

検索CASE文には選択子はありません。また、例4-7で示すように、WHEN句には、結果がなんらかの型の値になる式ではなく、ブール値になる検索条件が含まれます。

例4-7    検索CASE文の使用

DECLARE
  grade CHAR(1);
BEGIN
  grade := 'B';
  CASE
    WHEN grade = 'A' THEN DBMS_OUTPUT.PUT_LINE('Excellent');
    WHEN grade = 'B' THEN DBMS_OUTPUT.PUT_LINE('Very Good');
    WHEN grade = 'C' THEN DBMS_OUTPUT.PUT_LINE('Good');
    WHEN grade = 'D' THEN DBMS_OUTPUT.PUT_LINE('Fair');
    WHEN grade = 'F' THEN DBMS_OUTPUT.PUT_LINE('Poor');
    ELSE DBMS_OUTPUT.PUT_LINE('No such grade');
  END CASE;
END;
-- rather than using the ELSE in the CASE, could use the following
--  EXCEPTION
--    WHEN CASE_NOT_FOUND THEN
--      DBMS_OUTPUT.PUT_LINE('No such grade');
/

検索条件は順番に評価されます。各検索条件のブール値によって、どのWHEN句が実行されるかが決定されます。検索条件がTRUEになると、そのWHEN句が実行されます。WHEN句が1つでも実行されると、制御が次の文に渡されるため、後続の検索条件は評価されません。

TRUEになる検索条件がなければ、ELSE句が実行されます。ELSE句はオプションです。ただし、ELSE句を省略すると、PL/SQLでは次の暗黙的なELSE句が追加されます。

ELSE RAISE CASE_NOT_FOUND;

検索CASE文の実行中に呼び出された例外は、通常の方法で処理されます。つまり、通常の実行は中止され、PL/SQLブロックまたはサブプログラムの例外処理部に制御が移ります。

PL/SQL条件文のガイドライン

次の例のようなIF文の使用は避けてください。

IF new_balance < minimum_balance THEN
  overdrawn := TRUE;
ELSE
  overdrawn := FALSE;
END IF;
  ...
IF overdrawn = TRUE THEN
  RAISE insufficient_funds;
END IF;

ブール式の値はブール変数に直接代入できます。1番目のIF文は、次のように単純な代入に置き換えることができます。

overdrawn := new_balance < minimum_balance;

ブール変数はそれ自身がTRUEまたはFALSEです。2番目のIF文の条件は、次のように単純化できます。

IF overdrawn THEN ...

可能ならば、IF文をネストするのではなく、ELSIF句を使用してください。それによって、読みやすく、理解しやすいコードになります。次のIF文を比較してください。

IF condition1 THEN statement1;
  ELSE IF condition2 THEN statement2;
    ELSE IF condition3 THEN statement3; END IF;
  END IF;
END IF;
IF condition1 THEN statement1;
  ELSEIF condition2 THEN statement2;
  ELSEIF condition3 THEN statement3;
END IF;

これらの文は論理的に等価ですが、2番目の文の方が論理が明快です。

単一の式を複数の値と比較する場合は、IFELSIF句の組合せのかわりに単一のCASE文を使用すると、論理を簡素化できます。

ループの反復の制御: LOOP文とEXIT文

LOOP文は一連の文を複数回実行します。LOOP文には、LOOPWHILE-LOOPおよびFOR-LOOPの3つの形式があります。LOOP文の構文の説明は、「LOOP文」を参照してください。

LOOP文の使用

LOOP文の最も単純な形式は、キーワードLOOPEND LOOPで一連の文を囲む基本ループです。次に例を示します。

LOOP
  sequence_of_statements
END LOOP;

ループが繰り返されるたびに一連の文が実行され、制御がループの先頭に戻ります。EXIT文は、ループを終了し無限ループを防ぐために使用します。ループの中では、任意の場所に1つまたは複数のEXIT文を置くことができます。ただし、ループの外には置くことができません。EXIT文には、EXITおよびEXIT-WHENの2つの形式があります。

EXIT文の使用

EXIT文はループを無条件に終了させます。EXIT文が現れると、例4-8で示すように、ループはただちに終了し、制御は次の文に移ります。

例4-8    EXIT文の使用

DECLARE
  credit_rating NUMBER := 0;
BEGIN
  LOOP
    credit_rating := credit_rating + 1;
    IF credit_rating > 3 THEN
       EXIT;  -- exit loop immediately
    END IF;
 END LOOP;
 -- control resumes here
 DBMS_OUTPUT.PUT_LINE ('Credit rating: ' || TO_CHAR(credit_rating));
 IF credit_rating > 3 THEN
       RETURN;  -- use RETURN not EXIT when outside a LOOP
 END IF;
 DBMS_OUTPUT.PUT_LINE ('Credit rating: ' || TO_CHAR(credit_rating)); 
END;
/

EXIT文はループの中に置くことに注意してください。PL/SQLブロックを通常終了より前の段階で終了させる場合は、RETURN文を使用します。詳細は、「RETURN文の使用」を参照してください。

EXIT-WHEN文の使用

EXIT-WHEN文を使用すると、ループを条件に合わせて終了できます。EXIT文が見つかると、WHEN句の中の条件が評価されます。条件の評価結果がTRUEならば、ループは終了し、制御はそのループの後の文に移ります。EXIT-WHEN文を使用した例の例1-10を参照してください。

条件の評価結果がTRUEになるまで、ループは終了できません。ループの中で条件の値を変更する必要があります。前述の例で、FETCH文が行を戻すと、条件はFALSEに評価されます。FETCH文が行を戻すことに失敗した場合、条件はTRUEに評価され、ループは終了し、制御はCLOSE文に移ります。

EXIT-WHEN文は単純なIF文のかわりとして使用できます。たとえば、次の2つの文を比較してください。

IF count > 100 THEN EXIT; ENDIF;
EXIT WHEN count > 100;

この2つの文は論理的に等価ですが、EXIT-WHEN文の方がわかりやすく、理解しやすくなっています。

PL/SQLループのラベル付け

PL/SQLブロックと同様に、ループにもラベルを付けることができます。オプション・ラベルは二重の山カッコで囲んだ未宣言の識別子で、LOOP文の先頭に置きます。また、LOOP文の末尾にもラベル名を付けることができます。ラベル付きのループをネストする場合は、末尾のラベルを使用してわかりやすくします。

どちらの形式のEXIT文でも、カレント・ループにかぎらず、任意の外側のループも終了させることができます。これを行うには、終了する外側のループにラベルを付けます。次に、例4-9に示すように、EXIT文でそのラベルを使用します。ラベルを付けた外側のループが、内側のループを含めて終了します。

例4-9    ラベル付きループのEXITの使用

DECLARE
  s      PLS_INTEGER := 0;
  i      PLS_INTEGER := 0;
  j      PLS_INTEGER;
BEGIN
  <<outer_loop>>
  LOOP
    i := i + 1;
    j := 0;
    <<inner_loop>>
    LOOP
      j := j + 1;
      s := s + i * j; -- sum a bunch of products
      EXIT inner_loop WHEN (j > 5);
      EXIT outer_loop WHEN ((i * j) > 15);
    END LOOP inner_loop;
  END LOOP outer_loop;
  DBMS_OUTPUT.PUT_LINE('The sum of products equals: ' || TO_CHAR(s));
END;
/

WHILE-LOOP文の使用

WHILE-LOOP文は、条件がTRUEに評価されるかぎり、ループ本体の文を実行します。

WHILE condition LOOP
  sequence_of_statements
END LOOP;

ループを反復する前に条件が評価されます。条件がTRUEならば、一連の文が実行されてから、ループの先頭で制御が再開します。FALSEまたはNULLの場合、ループは実行されず、制御は次の文に渡されます。WHILE-LOOP文を使用する例については、例1-9を参照してください。

反復の回数は条件に依存し、ループが終了するまでわかりません。条件はループの先頭でテストされるため、一連の文が一度も実行されない可能性もあります。

いくつかの言語は、条件をループの先頭ではなく末尾でテストするLOOP UNTIL構造またはREPEAT UNTIL構造を持っています。そのため、一連の文は1回以上実行されます。PL/SQLでの等価のコードを次に示します。

LOOP
  sequence_of_statements
  EXIT WHEN boolean_expression;
END LOOP;

WHILEループが1回以上実行されるようにするには、初期化済のブール変数を条件の中で使用します。

done := FALSE;
WHILE NOT done LOOP
  sequence_of_statements
  done := boolean_expression;
END LOOP;

ループの中の文でブール変数に新しい値を代入して、無限ループを回避します。

FOR-LOOP文の使用

単純なFORループは、指定された整数の範囲内でループを繰り返し実行します。反復の回数はループに入る前からわかっています。二重ドット(..)は、範囲演算子です。繰返しの範囲はFORループに入った段階で評価され、それ以降は評価されません。下限と上限が等しければ、ループ本体は1回のみ実行されます。

例4-10の例に示すように、一連の文は1から500までの整数につき、1回ずつ実行されます。繰り返しが1回行われるたびに、ループ・カウンタが1つ増えます。

例4-10    単純なFOR..LOOP文の使用

DECLARE
  p     NUMBER := 0;
BEGIN
  FOR k IN 1..500 LOOP -- calculate pi with 500 terms
    p := p +  (  ( (-1) ** (k + 1) ) / ((2 * k) - 1) );
  END LOOP;
  p := 4 * p;
  DBMS_OUTPUT.PUT_LINE( 'pi is approximately : ' || p ); -- print result
END;
/

デフォルトでは、反復は下限から上限の向きに進みます。キーワードREVERSEを使用した場合、反復は上限から下限に下向きに進みます。繰返しが1回行われるたびに、ループ・カウンタが1つ減ります。この場合でも、範囲の上限と下限は(降順ではなく)昇順に書きます。

例4-11    反転FOR..LOOP文の使用

BEGIN
  FOR i IN REVERSE 1..3 LOOP  -- assign the values 1,2,3 to i
    DBMS_OUTPUT.PUT_LINE (TO_CHAR(i));
  END LOOP;
END;
/

FORループの中では、ループ・カウンタを参照できますが、変更することはできません。

BEGIN
  FOR i IN 1..3 LOOP  -- assign the values 1,2,3 to i
    IF i < 3 THEN
       DBMS_OUTPUT.PUT_LINE (TO_CHAR(i));
    ELSE
       i := 2; -- not allowed, raises an error
    END IF;
  END LOOP;
END;
/

FORループで、整数の範囲のかわりにSQL問合せを使用することをお薦めします。この手法を使用すると、単純な構文で、問合せを実行し、結果セットのすべての行を処理できます。詳細は、「PL/SQLを使用したデータの問合せ: 暗黙カーソルのFORループ」を参照してください。

PL/SQLループの反復方法

ループ範囲の境界にはリテラル、変数または式を使用できますが、整数に評価されるものにする必要があります。それ以外の場合、PL/SQLは事前定義の例外VALUE_ERRORを呼び出します。下限は1である必要はありませんが、ループ・カウンタの増分値(または減分値)は1である必要があります。

j IN -5..5
k IN REVERSE first..last
step IN 0..TRUNC(high/low) * 2

内部的に、PL/SQLはPLS_INTEGER一時変数に境界の値を代入します。さらに、必要に応じてその値を最も近い整数に四捨五入します。PLS_INTEGERの大きさの範囲は、32ビットで表すと、-2147483648〜2147483647です。範囲外の数値を評価した場合、PL/SQLが代入をすると、数値オーバーフローのエラーが発生します。「PLS_INTEGERデータ型」を参照してください。

言語によっては、STEP句を使用して異なる増分値(たとえば、1ではなく5)を指定できるものがあります。PL/SQLはこのような構造を持っていませんが、作成するのは簡単です。FORループの内部で、ループ・カウンタへの各参照に新しい増分値を乗じます。例4-12では、本日の日付を索引付き表の要素5、10、および15に代入します。

例4-12    FOR..LOOP文におけるカウンタの増分の変更

DECLARE
   TYPE DateList IS TABLE OF DATE INDEX BY PLS_INTEGER;
   dates DateList;
   k CONSTANT INTEGER := 5;  -- set new increment
BEGIN
   FOR j IN 1..3 LOOP
      dates(j*k) := SYSDATE;  -- multiply loop counter by increment
   END LOOP;
END;
/

ループの動的な範囲

例4-13で示すように、PL/SQLでは、範囲に変数を使用することでループの範囲を実行時に指定できます。

例4-13    実行時のLOOP範囲の指定

CREATE TABLE temp (emp_no NUMBER, email_addr VARCHAR2(50));
DECLARE
  emp_count NUMBER;
BEGIN
  SELECT COUNT(employee_id) INTO emp_count FROM employees;
  FOR i IN 1..emp_count LOOP
   INSERT INTO temp VALUES(i, 'to be added later');
  END LOOP;
  COMMIT;
END;
/

ループ範囲の下限が、上限より大きい整数であると評価された場合、ループ本体は実行されず、制御は次の文に移ります。

-- 上限が1になる
FOR i IN 2..limit LOOP
  sequence_of_statements -- ゼロ(0)回実行
END LOOP;
-- 制御はここに移る

ループ・カウンタ変数の有効範囲

ループ・カウンタはループの中でしか定義されません。そのため、その変数名をループの外側からは参照できません。ループが終了すると、ループ・カウンタは未定義になります。

例4-14    ループ・カウンタ変数の有効範囲

BEGIN
  FOR i IN 1..3 LOOP  -- assign the values 1,2,3 to i
    DBMS_OUTPUT.PUT_LINE (TO_CHAR(i));
  END LOOP;
  DBMS_OUTPUT.PUT_LINE (TO_CHAR(i)); -- raises an error
END;
/

ループ・カウンタはINTEGER型のローカル変数として暗黙的に宣言されているため、ループ・カウンタを宣言する必要はありません。ローカル宣言はグローバル宣言を隠すため、最も安全な方法は、既存の変数の名前を使用しないことです。

DECLARE
  i NUMBER := 5;
BEGIN
  FOR i IN 1..3 LOOP  -- assign the values 1,2,3 to i
    DBMS_OUTPUT.PUT_LINE (TO_CHAR(i));
  END LOOP;
  DBMS_OUTPUT.PUT_LINE (TO_CHAR(i)); -- refers to original variable value (5)
END;
/

この例でグローバル変数を使用する場合は、例4-15で示すように、ラベルとドット表記法を使用する必要があります。

例4-15    ループの外で変数を参照する場合のラベルの使用

<<main>>
DECLARE
  i NUMBER := 5;
BEGIN
  FOR i IN 1..3 LOOP  -- assign the values 1,2,3 to i
    DBMS_OUTPUT.PUT_LINE( 'local: ' || TO_CHAR(i) 
                       || ' global: ' || TO_CHAR(main.i));
  END LOOP;
END main;
/

ネストされたFORループにも同じ有効範囲規則が適用されます。例4-16では、どちらのループ・カウンタも同じ名前を持っています。内側のループから外側のループ・カウンタを参照する場合は、次のようにラベルとドット表記法を使用します。

例4-16    ループのラベルを使用した参照

BEGIN
<<outer_loop>>
  FOR i IN 1..3 LOOP  -- assign the values 1,2,3 to i
    <<inner_loop>>
    FOR i IN 1..3 LOOP
      IF outer_loop.i = 2 THEN
        DBMS_OUTPUT.PUT_LINE( 'outer: ' || TO_CHAR(outer_loop.i) || ' inner: ' 
                              || TO_CHAR(inner_loop.i));
      END IF;
    END LOOP inner_loop;
  END LOOP outer_loop;
END;
/

FORループでのEXIT文の使用

EXIT文を使用すると、FORループを途中で終了させることができます。例4-17で示すように、ループは通常は10回実行されますが、FETCH文が行を戻さなくなると、ループはそれまで何回実行されていてもただちに終了します。

例4-17    LOOPでのEXITの使用

DECLARE
   v_employees employees%ROWTYPE;  -- declare record variable
   CURSOR c1 is SELECT * FROM employees;
BEGIN
  OPEN c1; -- open the cursor before fetching
-- An entire row is fetched into the v_employees record
  FOR i IN 1..10 LOOP
    FETCH c1 INTO v_employees;
    EXIT WHEN c1%NOTFOUND;
    -- process data here
  END LOOP;
  CLOSE c1;
END;
/

ネストされたFORループから途中で出る必要があるとします。カレント・ループのみでなく外側のループも終了するには、例4-18で示すように、終了する外側のループにラベルを付け、EXIT文でそのラベルを使用します。

例4-18    LOOPでのラベル付きEXITの使用

DECLARE
   v_employees employees%ROWTYPE;  -- declare record variable
   CURSOR c1 is SELECT * FROM employees;
BEGIN
  OPEN c1; -- open the cursor before fetching
-- An entire row is fetched into the v_employees record
<<outer_loop>>
  FOR i IN 1..10 LOOP
    -- process data here
    FOR j IN 1..10 LOOP
      FETCH c1 INTO v_employees;
      EXIT WHEN c1%NOTFOUND;
      -- process data here
    END LOOP;
  END LOOP outer_loop;
  CLOSE c1;
END;
/

例6-10を参照してください。

順次制御: GOTO文とNULL文

GOTO文とNULL文は、PL/SQLプログラミングにとってIF文やLOOP文ほど重要なものではありません。通常、GOTO文は必要ありません。ただし、GOTO文を使用すると論理を単純化できる場合もあります。NULL文には、条件文の意味とアクションを明確にすることによって、コードをわかりやすくする効果があります。

GOTO文を多用すると、コードの理解やメンテナンスが困難になる可能性があります。GOTO文の使用は最小限にしてください。たとえば、深くネストされた構造からエラー処理ルーチンに分岐する場合は、GOTO文を使用するのではなく、例外を呼び出してください。PL/SQLの例外処理メカニズムについては、第10章「PL/SQLエラーの処理」を参照してください。

GOTO文の使用

GOTO文はラベルに無条件に分岐する場合に使用します。ラベルは有効範囲の中で他と重複しないもので、実行可能文かPL/SQLブロックの前に置かれている必要があります。GOTO文が実行されると、ラベルが付けられた文またはブロックに制御が移ります。ラベル付きの文またはブロックを、一連の文の中で上の方にも下の方にも置くことができます。例4-19では、一連の文の下の方にあるPL/SQLブロックに制御が渡されています。

例4-19    簡単なGOTO文の使用

DECLARE
  p        VARCHAR2(30);
  n        PLS_INTEGER := 37; -- test any integer > 2 for prime
BEGIN
  FOR j in 2..ROUND(SQRT(n)) LOOP
    IF n MOD j = 0 THEN -- test for prime
      p := ' is not a prime number'; -- not a prime number
      GOTO print_now;
    END IF;
  END LOOP;
  p := ' is a prime number';
<<print_now>>
  DBMS_OUTPUT.PUT_LINE(TO_CHAR(n) || p);
END;
/

例4-20に示すラベルend_loopは、実行可能文の前に置かれていないと使用できません。ラベルを有効にするため、NULL文を追加します。

例4-20    GOTOをラベルに使用するためにNULLを使用

DECLARE
   done  BOOLEAN;
BEGIN
   FOR i IN 1..50 LOOP
      IF done THEN
         GOTO end_loop;
      END IF;
   <<end_loop>>  -- not allowed unless an executable statement follows
   NULL; -- add NULL statement to avoid error
   END LOOP;  -- raises an error without the previous NULL
END;
/

例4-21で示すように、GOTO文でカレント・ブロックから外側のブロックに分岐できます。

例4-21    GOTO文を使用した外側のブロックへの分岐

-- example with GOTO statement
DECLARE
   v_last_name  VARCHAR2(25);
   v_emp_id     NUMBER(6) := 120;
BEGIN
   <<get_name>>
   SELECT last_name INTO v_last_name FROM employees 
          WHERE employee_id = v_emp_id;
   BEGIN
      DBMS_OUTPUT.PUT_LINE (v_last_name);
      v_emp_id := v_emp_id + 5;
      IF v_emp_id < 120 THEN
        GOTO get_name;  -- branch to enclosing block
      END IF;
   END;
END;
/

このGOTO文では、参照されたラベルが置かれている最初の外側のブロックに分岐します。

GOTO文の制限

GOTO文の宛先として使用できないものがあります。特に、GOTO文はIF文、CASE文、LOOP文またはサブブロックには分岐できません。たとえば、次のGOTO文は誤りです。

BEGIN
  GOTO update_row; -- IF文には分岐できない
  IF valid THEN
    <<update_row>>
    UPDATE emp SET ...
  END IF;
END;

GOTO文では、あるIF文の句から句へ分岐したり、あるCASE文のWHEN句から別の句へ分岐することはできません。

GOTO文では、外部のブロックからサブブロック(内側のBEGIN-ENDブロック)には分岐できません。

GOTO文では、サブプログラムの外に分岐できません。サブプログラムを途中で終了するには、RETURN文を使用するか、またはGOTO文を使用してサブプログラムの終了直前の場所に分岐します。

GOTO文では、例外ハンドラからカレント・ブロックBEGIN-ENDに分岐できません。ただし、GOTO文は、例外ハンドラから外側のブロックに分岐することはできます。

NULL文の使用

NULL文は、何も行わずに制御を次の文に渡します。一部の言語では、このような命令をno-op(何もしない)と呼びます。「NULL文」を参照してください。

例4-22では、NULL文によって、販売員のみがコミッションを受け取れることを明確にしています。

例4-22    アクションを起こさないことを明示するNULL文の使用

DECLARE
  v_job_id  VARCHAR2(10);
  v_emp_id  NUMBER(6) := 110;
BEGIN
  SELECT job_id INTO v_job_id FROM employees WHERE employee_id = v_emp_id;
  IF v_job_id = 'SA_REP' THEN
    UPDATE employees SET commission_pct = commission_pct * 1.2;
  ELSE
    NULL; -- do nothing if not a sales representative
  END IF;
END;
/

NULL文を使用すると、プレースホルダやスタブ・プロシージャを簡単に作成できます。例4-23では、NULL文を使用してプロシージャをコンパイルしています。実際の本体は、後で挿入できます。NULL文を使用する場合は、警告が有効であると、到達不能コードという警告が発生する場合があることに注意してください。「PL/SQLのコンパイル時の警告の概要」を参照してください。

例4-23    サブプログラム作成時のプレースホルダとしてのNULLの使用

CREATE OR REPLACE PROCEDURE award_bonus (emp_id NUMBER, bonus NUMBER) AS
BEGIN  -- executable part starts here
  NULL; -- use NULL as placeholder, raises "unreachable code" if warnings enabled
END award_bonus;
/

NULL文を使用すると、考慮はするがアクションが必要ないことを示すことができます。次の例外ブロックでは、NULL文によって、名前のない例外ではアクションを起こさないことを示しています。

EXCEPTION
  WHEN ZERO_DIVIDE THEN
    ROLLBACK;
  WHEN OTHERS THEN
    NULL;
END;

例1-12「ストアド・サブプログラムの作成」を参照してください。


戻る 次へ
Oracle
Copyright © 2005 Oracle Corporation.

All Rights Reserved.
目次
目次
索引
索引