控制程式流程

與執行陳述式的 SQL 不同,其執行順序為您輸入陳述式,PL/SQL 具有控制陳述式,可讓您控制程式的流程。

關於控制敘述句

PL/SQL 有三種類別的控制敘述句:條件選取敘述句、迴圈敘述句以及循序控制敘述句。

條件式選擇敘述句可讓您針對不同的資料值執行不同的敘述句。條件選取陳述式為 IFCASE

迴圈敘述句可讓您使用一系列不同的資料值重複相同的敘述句。迴圈敘述句為 FOR LOOPWHILE LOOP 以及基本的 LOOPEXIT 敘述句會將控制權傳輸至迴圈的結尾。CONTINUE 敘述句會結束迴圈的目前重複,並將控制轉移到下一個重複。EXITCONTINUE 都具有選擇性的 WHEN 子句,您可以在此指定條件。

循序控制陳述式可讓您移至指定的有標籤陳述式,或不執行任何動作。循序控制陳述式為 GOTONULL

另請參閱:Oracle Database PL/SQL Language Reference,瞭解 PL/SQL 控制敘述句的簡介

使用 IF 敘述句

根據布林表示式的值,IF 陳述式會執行或略過一系列的陳述式。

IF 陳述式具有下列語法:

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

假設您的公司在員工任職的第一個十年中,每一年進行兩次員工評估,之後每年評估一次。如需可傳回員工評估頻率的函數,您可以使用 IF 陳述式來決定函數的傳回值,如範例 5-4 中所示。

將 EVAL_FREQUENCY 功能新增至 EMP_EVAL 套裝程式的主體,而非新增至規格。因為它不在規格中,所以 EVAL_FREQUENCY 是套件的本機元件 - 它只能由套件中的其他子程式呼叫,而非從套件外部叫用。

提示:在 SQL 陳述式中使用 PL/SQL 變數時,如同在範例 5-4 中的第二個 SELECT 陳述式中,使用子程式名稱來限定變數,以確保表格資料欄不會發生錯誤。

另請參閱:

範例 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 敘述句會從一系列的條件中選擇,並執行對應的敘述句。

簡單的 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。

變更 EVAL_FREQUENCY 函數,如範例 5-5 所示。(如需列印字串 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 函數報告薪資在每年建議的金額增加五年後,會增加多少薪資。

變更 EVAL_FREQUENCY 函數,如範例 5-6 所示。(如需列印字串 DBMS_OUTPUT.PUT_LINE 的程序相關資訊,請參閱 Oracle Database PL/SQL Packages and Types Reference。)

範例 5-6 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;

另請參閱:

使用 WHILE LOOP 敘述句

WHILE LOOP 陳述式在條件為 TRUE 時,重複一系列陳述式。

WHILE LOOP 敘述句的語法為:

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

注意:如果 LOOP 與 END LOOP 之間的敘述句永遠不會導致條件變成 FALSE,則 WHILE LOOP 敘述句會無限期地執行。

假設 EVAL_FREQUENCY 函數使用 WHILE LOOP 陳述式,而非 FOR LOOP 陳述式,且在提議的薪資超過 JOB_ID 的最高薪資之後結束。

變更 EVAL_FREQUENCY 函數,如範例 5-7 所示。(如需列印字串 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 敘述句將無限期執行。

當條件為 TRUE 且將控制權傳輸至迴圈結尾時,EXIT WHEN 敘述句 (EXIT 敘述句及其選擇性的 WHEN 子句) 就會結束迴圈。

在 EVAL_FREQUENCY 函數中,最後一次反覆執行 WHILE LOOP 陳述式時,最後計算的值通常超過最高薪資。

將 WHILE LOOP 敘述句變更成包含 EXIT WHEN 敘述句的基本 LOOP 敘述句,如範例 5-8

範例 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;

另請參閱: