4.2 LOOP文

ループ文は、一連の異なる値を使用して、同じ文を実行します。ループ文には次のものがあります。

  • 基本LOOP

  • FOR LOOP

  • カーソルFOR LOOP

  • WHILE LOOP

ループを終了する文には次のものがあります。

  • EXIT

  • EXIT WHEN

現行のループの反復を終了する文には次のものがあります。

  • CONTINUE

  • CONTINUE WHEN

EXITEXIT WHENCONTINUEおよびCONTINUE WHENは、ループ内の任意の場所に置くことができますが、ループの外に置くことはできません。ループの外の文に制御を移してループまたは現行のループの反復を終了することができるGOTO文のかわりに、これらの文を使用することをお薦めします。(例外が呼び出された場合もループは終了します。例外の詳細は、「例外処理の概要」を参照してください。)

LOOP文にはラベルを付けることができ、LOOP文をネストすることもできます。わかりやすさを向上させるために、ネステッド・ループにはラベルを付けることをお薦めします。END LOOP文のラベルと同じループ文の先頭のラベルが一致していることを確認する必要があります(コンパイラはチェックしません)。

ここでのトピック

4.2.1 基本LOOP文

基本LOOP文の構造は、次のとおりです。

[ label ] LOOP
  statements
END LOOP [ label ];

ループが反復されるたびにstatementsが実行され、制御がループの先頭に戻ります。無限ループが発生しないように、文または例外の呼び出しによってループを終了する必要があります。

関連項目:

「基本LOOP文」

4.2.2 EXIT文

EXIT文は、現行のループの反復を無条件で終了し、カレント・ループまたはラベルが付けられている外側のループのいずれかの最後に制御を移します。

例4-9では、基本LOOP文内のEXIT文が無条件でカレント・ループの最後に制御を移します。

関連項目:

「EXIT文」

例4-9 EXIT文が含まれている基本LOOP文

DECLARE
  x NUMBER := 0;
BEGIN
  LOOP
    DBMS_OUTPUT.PUT_LINE ('Inside loop:  x = ' || TO_CHAR(x));
    x := x + 1;
    IF x > 3 THEN
      EXIT;
    END IF;
  END LOOP;
  -- After EXIT, control resumes here
  DBMS_OUTPUT.PUT_LINE(' After loop:  x = ' || TO_CHAR(x));
END;
/
 

結果:

Inside loop:  x = 0
Inside loop:  x = 1
Inside loop:  x = 2
Inside loop:  x = 3
After loop:  x = 4

4.2.3 EXIT WHEN文

EXIT WHEN文は、WHEN句内の条件がTRUEの場合、現行のループの反復を終了し、カレント・ループまたはラベルが付けられている外側のループのいずれかの最後に制御を移します。

制御がEXIT WHEN文に達するたびに、WHEN句内の条件が評価されます。条件がTRUEでない場合、EXIT WHEN文は何も実行しません。例4-10に示すように、無限ループが発生しないように、ループ内の文で条件がTRUEになるようにする必要があります。

例4-10では、xが3より大きい場合、基本LOOP文内のEXIT WHEN文がカレント・ループの最後に制御を移します。例4-10例4-9と論理的に等価です。

関連項目:

「EXIT文」

例4-11では、基本LOOP文が別の基本LOOP文にネストされていて、両方にラベルが付けられています。内部ループにはEXIT WHEN文が2つあり、1つは内部ループを終了し、もう1つは外部ループを終了します。

内部ループ内のEXIT WHEN文から外部ループに制御を移すことができるのは、外部ループにラベルが付けられている場合のみです。

例4-12では、外部ループにはラベルが付いていないため、内部ループは制御を外部ループに移すことができません。

例4-10 EXIT WHEN文が含まれている基本LOOP文

DECLARE
  x NUMBER := 0;
BEGIN
  LOOP
    DBMS_OUTPUT.PUT_LINE('Inside loop:  x = ' || TO_CHAR(x));
    x := x + 1;  -- prevents infinite loop
    EXIT WHEN x > 3;
  END LOOP;
  -- After EXIT statement, control resumes here
  DBMS_OUTPUT.PUT_LINE('After loop:  x = ' || TO_CHAR(x));
END;
/
 

結果:

Inside loop:  x = 0
Inside loop:  x = 1
Inside loop:  x = 2
Inside loop:  x = 3
After loop:  x = 4

例4-11 EXIT WHEN文が含まれているネストされたラベル付き基本LOOP文

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 several 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;
/
 

結果:

The sum of products equals: 166

例4-12 EXIT WHEN文が含まれているネストされたラベルなしの基本LOOP文

DECLARE
  i PLS_INTEGER := 0;
  j PLS_INTEGER := 0;
 
BEGIN
  LOOP
    i := i + 1;
    DBMS_OUTPUT.PUT_LINE ('i = ' || i);
    
    LOOP
      j := j + 1;
      DBMS_OUTPUT.PUT_LINE ('j = ' || j);
      EXIT WHEN (j > 3);
    END LOOP;
 
    DBMS_OUTPUT.PUT_LINE ('Exited inner loop');
 
    EXIT WHEN (i > 2);
  END LOOP;
 
  DBMS_OUTPUT.PUT_LINE ('Exited outer loop');
END;
/

結果:

i = 1
j = 1
j = 2
j = 3
j = 4
Exited inner loop
i = 2
j = 5
Exited inner loop
i = 3
j = 6
Exited inner loop
Exited outer loop
 
PL/SQL procedure successfully completed.

4.2.4 CONTINUE文

CONTINUE文は、現行のループの反復を無条件で終了し、カレント・ループまたはラベルが付けられている外側のループのいずれかの次の反復に制御を移します。

例4-13では、基本LOOP文内のCONTINUE文が無条件でカレント・ループの次の反復に制御を移します。

関連項目:

CONTINUE文

例4-13 基本LOOP文内のCONTINUE文

DECLARE
  x NUMBER := 0;
BEGIN
  LOOP -- After CONTINUE statement, control resumes here
    DBMS_OUTPUT.PUT_LINE ('Inside loop:  x = ' || TO_CHAR(x));
    x := x + 1;
    IF x < 3 THEN
      CONTINUE;
    END IF;
    DBMS_OUTPUT.PUT_LINE
      ('Inside loop, after CONTINUE:  x = ' || TO_CHAR(x));
    EXIT WHEN x = 5;
  END LOOP;
 
  DBMS_OUTPUT.PUT_LINE (' After loop:  x = ' || TO_CHAR(x));
END;
/
 

結果:

Inside loop:  x = 0
Inside loop:  x = 1
Inside loop:  x = 2
Inside loop, after CONTINUE:  x = 3
Inside loop:  x = 3
Inside loop, after CONTINUE:  x = 4
Inside loop:  x = 4
Inside loop, after CONTINUE:  x = 5
After loop:  x = 5

4.2.5 CONTINUE WHEN文

CONTINUE WHEN文は、WHEN句内の条件がTRUEの場合、現行のループの反復を終了し、カレント・ループまたはラベルが付けられている外側のループのいずれかの次の反復に制御を移します。

制御がCONTINUE WHEN文に達するたびに、WHEN句内の条件が評価されます。条件がTRUEでない場合、CONTINUE WHEN文は何も実行しません。

例4-14では、xが3より小さい場合、基本LOOP文内のCONTINUE WHEN文がカレント・ループの次の反復に制御を移します。例4-14例4-13と論理的に等価です。

関連項目:

CONTINUE文

例4-14 基本LOOP文内のCONTINUE WHEN文

DECLARE
  x NUMBER := 0;
BEGIN
  LOOP -- After CONTINUE statement, control resumes here
    DBMS_OUTPUT.PUT_LINE ('Inside loop:  x = ' || TO_CHAR(x));
    x := x + 1;
    CONTINUE WHEN x < 3;
    DBMS_OUTPUT.PUT_LINE
      ('Inside loop, after CONTINUE:  x = ' || TO_CHAR(x));
    EXIT WHEN x = 5;
  END LOOP;
  DBMS_OUTPUT.PUT_LINE (' After loop:  x = ' || TO_CHAR(x));
END;
/
 

結果:

Inside loop:  x = 0
Inside loop:  x = 1
Inside loop:  x = 2
Inside loop, after CONTINUE:  x = 3
Inside loop:  x = 3
Inside loop, after CONTINUE:  x = 4
Inside loop:  x = 4
Inside loop, after CONTINUE:  x = 5
After loop:  x = 5

4.2.6 FOR LOOP文

FOR LOOP文は、指定された範囲にループ索引がある場合に1つ以上の文を実行します。この文の構造は、次のとおりです。

[ label ] FOR index IN [ REVERSE ] lower_bound..upper_bound LOOP
  statements
END LOOP [ label ];

REVERSEが指定されていない場合、indexの値はlower_boundから始まり、upper_boundに達するまで、ループを反復するたびに1ずつ増加します。lower_boundupper_boundよりも大きい場合、statementsは実行されません。

REVERSEが指定されている場合、indexの値はupper_boundから始まり、lower_boundに達するまで、ループを反復するたびに1ずつ減少します。upper_boundlower_boundよりも小さい場合、statementsは実行されません。

EXITEXIT WHENCONTINUEまたはCONTINUE WHENstatements内に置くと、ループまたは現行のループの反復を途中で終了させることができます。

ヒント:

問合せの結果セットの行を処理するには、整数の範囲のかわりに問合せが含まれているカーソルFOR LOOPを使用してください。詳細は、「 カーソルFOR LOOP文による問合せ結果セットの処理」を参照してください。

関連項目:

FOR LOOP文

例4-15では、indexilower_boundは1、upper_boundは3です。このループは1から3の数値を出力します。

例4-16FOR LOOP文は、例4-15を反転したものであり、3から1の数値を出力します。

一部の言語のFOR LOOPには、ループ索引の増分値を1以外に指定することが可能なSTEP句があります。PL/SQLでSTEP句をシミュレートするには、ループ索引の参照のたびに、必要な増分値を乗じます。

例4-17では、FOR LOOPの索引が実質的に5ずつ増加します。

ここでのトピック

例4-15 FOR LOOP文

BEGIN
  DBMS_OUTPUT.PUT_LINE ('lower_bound < upper_bound');
 
  FOR i IN 1..3 LOOP
    DBMS_OUTPUT.PUT_LINE (i);
  END LOOP;
 
  DBMS_OUTPUT.PUT_LINE ('lower_bound = upper_bound');
 
  FOR i IN 2..2 LOOP
    DBMS_OUTPUT.PUT_LINE (i);
  END LOOP;
 
  DBMS_OUTPUT.PUT_LINE ('lower_bound > upper_bound');
 
  FOR i IN 3..1 LOOP
    DBMS_OUTPUT.PUT_LINE (i);
  END LOOP;
END;
/

結果:

lower_bound < upper_bound
1
2
3
lower_bound = upper_bound
2
lower_bound > upper_bound

例4-16 反転FOR LOOP文

BEGIN
  DBMS_OUTPUT.PUT_LINE ('upper_bound > lower_bound');
 
  FOR i IN REVERSE 1..3 LOOP
    DBMS_OUTPUT.PUT_LINE (i);
  END LOOP;
 
  DBMS_OUTPUT.PUT_LINE ('upper_bound = lower_bound');
 
  FOR i IN REVERSE 2..2 LOOP
    DBMS_OUTPUT.PUT_LINE (i);
  END LOOP;
 
  DBMS_OUTPUT.PUT_LINE ('upper_bound < lower_bound');
 
  FOR i IN REVERSE 3..1 LOOP
    DBMS_OUTPUT.PUT_LINE (i);
  END LOOP;
END;
/
 

結果:

upper_bound > lower_bound
3
2
1
upper_bound = lower_bound
2
upper_bound < lower_bound

例4-17 FOR LOOP文でのSTEP句のシミュレート

DECLARE
  step  PLS_INTEGER := 5;
BEGIN
  FOR i IN 1..3 LOOP
    DBMS_OUTPUT.PUT_LINE (i*step);
  END LOOP;
END;
/

結果:

5
10
15

4.2.6.1 FOR LOOP索引

FOR LOOP文の索引は、ループに対してローカルであるPLS_INTEGER型の変数として暗黙的に宣言されます。ループ内の文は索引の値を参照できますが、変更することはできません。ループの外側の文は、索引を参照できません。FOR LOOP文の実行後、索引は未定義となります。(ループ索引はループ・カウンタと呼ばれる場合があります。)

例4-18では、FOR LOOP文が索引の値を変更しようとしているため、エラーが発生します。

例4-19では、FOR LOOP文の外側の文がループ索引を参照しようとしているため、エラーが発生します。

例4-20に示すように、FOR LOOP文の索引の名前が、外側のブロックで宣言された変数の名前と同じ場合、ローカルの暗黙的な宣言によって他の宣言が隠されます。

例4-21は、Example 4-20を変更して、外側のブロックで宣言された変数をループ内の文で参照できるようにする方法を示しています。

例4-22では、ネストしたFOR LOOP文の索引で同じ名前を使用しています。内部ループでは、外部ループのラベルで参照を修飾して、外部ループの索引を参照しています。また、内部ループでは、意味を明確にするために、それ自体のラベルでそれ自体の索引への参照も修飾しています。

例4-18 索引値を変更しようとするFOR LOOP文

BEGIN
  FOR i IN 1..3 LOOP
    IF i < 3 THEN
      DBMS_OUTPUT.PUT_LINE (TO_CHAR(i));
    ELSE
      i := 2;
    END IF;
  END LOOP;
END;
/
 

結果:

       i := 2;
       *
ERROR at line 6:
ORA-06550: line 6, column 8:
PLS-00363: expression 'I' cannot be used as an assignment target
ORA-06550: line 6, column 8:
PL/SQL: Statement ignored

例4-19 外側の文によるFOR LOOP文の索引の参照

BEGIN
  FOR i IN 1..3 LOOP
    DBMS_OUTPUT.PUT_LINE ('Inside loop, i is ' || TO_CHAR(i));
  END LOOP;
  
  DBMS_OUTPUT.PUT_LINE ('Outside loop, i is ' || TO_CHAR(i));
END;
/
 

結果:

  DBMS_OUTPUT.PUT_LINE ('Outside loop, i is ' || TO_CHAR(i));
                                                         *
ERROR at line 6:
ORA-06550: line 6, column 58:
PLS-00201: identifier 'I' must be declared
ORA-06550: line 6, column 3:
PL/SQL: Statement ignored

例4-20 変数と同じ名前を持つFOR LOOP文の索引

DECLARE
  i NUMBER := 5;
BEGIN
  FOR i IN 1..3 LOOP
    DBMS_OUTPUT.PUT_LINE ('Inside loop, i is ' || TO_CHAR(i));
  END LOOP;
  
  DBMS_OUTPUT.PUT_LINE ('Outside loop, i is ' || TO_CHAR(i));
END;
/
 

結果:

Inside loop, i is 1
Inside loop, i is 2
Inside loop, i is 3
Outside loop, i is 5

例4-21 FOR LOOP文による索引と同じ名前を持つ変数の参照

<<main>>  -- Label block.
DECLARE
  i NUMBER := 5;
BEGIN
  FOR i IN 1..3 LOOP
    DBMS_OUTPUT.PUT_LINE (
      'local: ' || TO_CHAR(i) || ', global: ' ||
      TO_CHAR(main.i)  -- Qualify reference with block label.
    );
  END LOOP;
END main;
/
 

結果:

local: 1, global: 5
local: 2, global: 5
local: 3, global: 5

例4-22 同じ索引名のネストしたFOR LOOP文

BEGIN
  <<outer_loop>>
  FOR i IN 1..3 LOOP
    <<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;
/
 

結果:

outer: 2 inner: 1
outer: 2 inner: 2
outer: 2 inner: 3

4.2.6.2 下限と上限

FOR LOOP文の下限と上限には、数値リテラル、数値変数または数式を使用できます。境界の数値が指定されていない場合、PL/SQLは事前定義の例外VALUE_ERRORを呼び出します。

例4-24では、FOR LOOP文の上限が変数になっており、値は実行時に決定されます。

例4-23 FOR LOOP文の境界

DECLARE
  first  INTEGER := 1;
  last   INTEGER := 10;
  high   INTEGER := 100;
  low    INTEGER := 12;
BEGIN
  -- Bounds are numeric literals:
  FOR j IN -5..5 LOOP
    NULL;
  END LOOP;
 
  -- Bounds are numeric variables:
  FOR k IN REVERSE first..last LOOP
    NULL;
  END LOOP;
 
 -- Lower bound is numeric literal,
 -- Upper bound is numeric expression:
  FOR step IN 0..(TRUNC(high/low) * 2) LOOP
    NULL;
  END LOOP;
END;
/

例4-24 実行時に境界が指定されるFOR LOOP文

DROP TABLE temp;
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 (emp_no, email_addr)
    VALUES(i, 'to be added later');
  END LOOP;
END;
/

4.2.6.3 FOR LOOP文内のEXIT WHENまたはCONTINUE WHEN文

特定の条件が発生した場合はただちにFOR LOOP文を終了する必要があるとします。FOR LOOP文内のEXIT WHEN文に、条件を指定できます。

例4-25では、FOR LOOP文が10回実行されますが、中のFETCH文から行が戻されなくなった場合は、ただちにループが終了します。

次は、途中で終了する必要のあるFOR LOOP文が、別のFOR LOOPの中にネストされているとします。内側のループが途中で終了した場合に外側のループも終了する必要があるなら、例4-26に示すように、外側のループにラベルを付け、EXIT WHEN文にラベル名を指定します。

内側のループが途中で終了しても外側のループの現行の反復を完了する必要があるなら、例4-27に示すように、外側のループにラベルを付け、CONTINUE WHEN文にラベル名を指定します。

関連項目:

例外の詳細は、「例外処理の概要」を参照してください。特定の条件が発生した場合は、例外を使用してただちにループを終了させることもできます

例4-25 FOR LOOP文内のEXIT WHEN文

DECLARE
  v_employees employees%ROWTYPE;
  CURSOR c1 is SELECT * FROM employees;
BEGIN
  OPEN c1;
  -- Fetch entire row into 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;
/

例4-26 内側のFOR LOOP文内のEXIT WHEN文

DECLARE
  v_employees employees%ROWTYPE;
  CURSOR c1 is SELECT * FROM employees;
BEGIN
  OPEN c1;
  
  -- Fetch entire row into 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 outer_loop WHEN c1%NOTFOUND;
      -- Process data here
    END LOOP;
  END LOOP outer_loop;
 
  CLOSE c1;
END;
/

例4-27 内側のFOR LOOP文内のCONTINUE WHEN文

DECLARE
  v_employees employees%ROWTYPE;
  CURSOR c1 is SELECT * FROM employees;
BEGIN
  OPEN c1;
  
  -- Fetch entire row into 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;
      CONTINUE outer_loop WHEN c1%NOTFOUND;
      -- Process data here
    END LOOP;
  END LOOP outer_loop;
 
  CLOSE c1;
END;
/

4.2.7 WHILE LOOP文

WHILE LOOP文は、条件がTRUEの場合に1つ以上の文を実行します。この文の構造は、次のとおりです。

[ label ] WHILE condition LOOP
  statements
END LOOP [ label ];

conditionがTRUEの場合は、statementsが実行され、ループの先頭に制御が戻り、そこでconditionが再度評価されます。conditionがTRUEでない場合は、WHILE LOOP文の後の文に制御が移ります。無限ループが発生しないように、ループ内の文で条件がFALSEまたはNULLになるようにする必要があります。構文の詳細は、「WHILE LOOP文」を参照してください。

EXITEXIT WHENCONTINUEまたはCONTINUE WHENstatements内に置くと、ループまたは現行のループの反復を途中で終了させることができます。

一部の言語は、条件をループの先頭ではなく最後でテストするLOOP UNTILまたはREPEAT UNTIL構造を持っているため、一連の文は1回以上実行されます。この構造をPL/SQLでシミュレートするには、EXIT WHEN文とともに基本LOOP文を使用します。

LOOP
  statements
  EXIT WHEN condition;
END LOOP;

例4-28では、1番目のWHILE LOOP文内の文は実行されず、2番目のWHILE LOOP文内の文が1回実行されます。

例4-28 WHILE LOOP文

DECLARE
  done  BOOLEAN := FALSE;
BEGIN
  WHILE done LOOP
    DBMS_OUTPUT.PUT_LINE ('This line does not print.');
    done := TRUE;  -- This assignment is not made.
  END LOOP;

  WHILE NOT done LOOP
    DBMS_OUTPUT.PUT_LINE ('Hello, world!');
    done := TRUE;
  END LOOP;
END;
/

結果:

Hello, world!