4.3 順次制御文

sequential control statementsであるGOTO文とNULL文は、PL/SQLプログラミングではIF文やLOOP文ほど重要ではありません。

指定した文に移動するGOTO文が必要となることはほとんどありません。ただし、これを使用すると論理を単純化できる場合もあります。

何も実行しないNULL文には、条件文の意味とアクションを明確にすることによって、コードをわかりやすくする効果があります。

ここでのトピック

4.3.1 GOTO文

GOTO文は無条件に制御をラベルに移します。ラベルは有効範囲の中で他と重複しないもので、実行可能文かPL/SQLブロックの前に置かれている必要があります。GOTO文が実行されると、ラベルが付けられた文またはブロックに制御が移ります。GOTO文の制限については、「GOTO文」を参照してください。

GOTO文の使用は最小限にしてください(多用すると、コードの理解やメンテナンスが困難になります)。深いネスト構造から例外ハンドラに制御を移す場合、GOTO文は使用しないでください。かわりに、例外を呼び出してください。PL/SQLの例外処理メカニズムの詳細は、「PL/SQLのエラー処理」を参照してください。

ラベルは、ブロックの前(例4-21)または文の前(例4-29)にのみ置くことができます。例4-30のように文の途中に置くことはできません。

例4-30を修正する場合は、例4-31に示すように、NULL文を追加します。

GOTO文は、例4-32に示すように、カレント・ブロックから外側のブロックに制御を移すことができます。

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

例4-33では、GOTO文がIF文内に制御を移しているため、エラーが発生します。

例4-29 GOTO文

DECLARE
  p  VARCHAR2(30);
  n  PLS_INTEGER := 37;
BEGIN
  FOR j in 2..ROUND(SQRT(n)) LOOP
    IF n MOD j = 0 THEN
      p := ' is 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;
/
 

結果:

37 is a prime number

例4-30 不適切なラベル配置

DECLARE
  done  BOOLEAN;
BEGIN
  FOR i IN 1..50 LOOP
    IF done THEN
       GOTO end_loop;
    END IF;
    <<end_loop>>
  END LOOP;
END;
/
 

結果:

  END LOOP;
  *
ERROR at line 9:
ORA-06550: line 9, column 3:
PLS-00103: Encountered the symbol "END" when expecting one of the following:
( begin case declare exit for goto if loop mod null raise
return select update while with <an identifier>
<a double-quoted delimited-identifier> <a bind variable> <<
continue close current delete fetch lock insert open rollback
savepoint set sql run commit forall merge pipe purge

例4-31 GOTO文によるラベル付きのNULL文への移動

DECLARE
  done  BOOLEAN;
BEGIN
  FOR i IN 1..50 LOOP
    IF done THEN
      GOTO end_loop;
    END IF;
    <<end_loop>>
    NULL;
  END LOOP;
END;
/

例4-32 GOTO文による外側のブロックへの制御の移動

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;
    END IF;
  END;
END;
/
 

結果:

Weiss

例4-33 GOTO文によるIF文内への無効な制御の移動

DECLARE
  valid BOOLEAN := TRUE;
BEGIN
  GOTO update_row;
  
  IF valid THEN
  <<update_row>>
    NULL;
  END IF;
END;
/
 

結果:

  GOTO update_row;
  *
ERROR at line 4:
ORA-06550: line 4, column 3:
PLS-00375: illegal GOTO statement; this GOTO cannot transfer control to label
'UPDATE_ROW'
ORA-06550: line 6, column 12:
PL/SQL: Statement ignored

4.3.2 NULL文

NULL文は、後続の文に制御を移すのみです。一部の言語では、このような命令をno-op(何もしない)と呼びます。

NULL文は、次のように使用できます。

  • GOTO文に対するターゲットとして使用できます(例4-31を参照)。

  • 条件文の意味とアクションを明確にすることによって、わかりやすさを向上できます(例4-34を参照)。

  • プレースホルダおよびスタブ・サブプログラムを作成できます(例4-35を参照)。

  • 考慮はするがアクションは必要ないことを示すことができます(例4-36を参照)。

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

例4-35では、NULL文を使用してサブプログラムをコンパイルしています。実際の本体は、後で挿入できます。

ノート:

NULL文を使用する場合に警告が有効になっていると、unreachable codeという警告が発生する可能性があります。警告の詳細は、「コンパイル時の警告」を参照してください。

例4-36では、NULL文によって、A、B、C、D、F以外のgradeではアクションを起こさないことを示しています。

例4-34 アクションを実行しないことを明示する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;  -- Employee is not a sales rep
  END IF;
END;
/

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

CREATE OR REPLACE PROCEDURE award_bonus (
  emp_id NUMBER,
  bonus NUMBER
) AUTHID DEFINER AS
BEGIN    -- Executable part starts here
  NULL;  -- Placeholder
  -- (raises "unreachable code" if warnings enabled)
END award_bonus;
/

例4-36 単純なCASE文のELSE句でのNULL文

CREATE OR REPLACE PROCEDURE print_grade (
  grade CHAR
) AUTHID DEFINER AS
BEGIN
  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 NULL;
  END CASE;
END;
/
BEGIN
  print_grade('A');
  print_grade('S');
END;
/

結果:

Excellent