Programmfluss steuern

Im Gegensatz zu SQL, wo Anweisungen in die Reihenfolge ausgeführt werden, in die sie eingegeben wurden, verfügt PL/SQL über Kontrollanweisungen, mit denen Sie den Flow des Programms steuern können.

Informationen zu Kontrollanweisungen

PL/SQL verfügt über drei Kategorien von Kontrollanweisungen: bedingte Auswahlanweisungen, Schleifenanweisungen und sequenzielle Kontrollanweisungen.

Mit Bedingte Auswahlanweisungen können Sie verschiedene Anweisungen für verschiedene Datenwerte ausführen. Die bedingten Auswahlanweisungen sind IF und CASE.

Schleifenanweisungen, mit denen Sie dieselben Anweisungen mit einer Reihe unterschiedlicher Datenwerte wiederholen können. Schleifenanweisungen sind: FOR LOOP, WHILE LOOP und Basis-LOOP. Die EXIT-Anweisung überträgt die Kontrolle an das Ende einer Schleife. Die Anweisung CONTINUE beendet die aktuelle Wiederholung einer Schleife und überträgt die Kontrolle an die nächste Wiederholung. EXIT und CONTINUE haben eine optionale WHEN-Klausel, in der Sie eine Bedingung angeben können.

Mit Sequenzielle Kontrollanweisungen können Sie zu einer angegebenen beschrifteten Anweisung gehen oder keine Aktion ausführen. Die sequenziellen Kontrollanweisungen sind GOTO und NULL.

Siehe auch: Oracle Database PL/SQL Language Reference für einen Überblick über PL/SQL-Kontrollanweisungen

IF-Anweisung verwenden

Mit der Anweisung IF wird eine Reihe von Anweisungen je nach Wert eines Booleschen Ausdrucks entweder ausgeführt oder übersprungen.

Die IF-Anweisung hat folgende Syntax:

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

Angenommen, Ihre Firma führt in den ersten zehn Beschäftigungsjahren eines Mitarbeiters zweimal jährlich und nach dem zehnten Jahr nur einmal jährlich eine Evaluierung durch. Sie benötigen eine Funktion, die die Evaluierungsfrequenz eines Mitarbeiters zurückgibt. Sie können den Rückgabewert der Funktion mit einer IF-Anweisung ermitteln, wie in Beispiel 5-4.

Fügen Sie die Funktion EVAL_FREQUENCY dem Body des Packages EMP_EVAL hinzu, jedoch nicht der Spezifikation. Da sich EVAL_FREQUENCY nicht in der Spezifikation befinden, ist es lokal im Package. Es kann nur von anderen Unterprogrammen im Package aufgerufen, nicht aber von außerhalb des Packages.

Tipp: Wenn Sie eine PL/SQL-Variable in einer SQL-Anweisung verwenden, wie in der zweiten SELECT-Anweisung in Beispiel 5-4, qualifizieren Sie die Variable mit dem Unterprogrammnamen, um sicherzustellen, dass sie nicht mit einer Tabellenspalte verwechselt wird.

Siehe:

Beispiel 5-4: IF-Anweisung bestimmt den Rückgabewert der Funktion

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-Ausdruck verwenden

Die CASE-Anweisung trifft eine Wahl aus einer Bedingungssequenz und führt die entsprechende Anweisung durch.

Mit der einfachen CASE-Anweisung wird ein einzelner Ausdruck ausgewertet und mit mehreren potenziellen Werten verglichen. Die Anweisung hat folgende Syntax:

CASE expression
WHEN value THEN statement
[ WHEN value THEN statement ]...
[ ELSE statement [, statement ]... ]
END CASE;

Die gesuchte CASE-Anweisung bewertet mehrere boolesche Ausdrücke und wählt den ersten, dessen Wert TRUE ist. Weitere Informationen zur gesuchten CASE-Anweisung finden Sie unter Oracle Database PL/SQL Language Reference.

Tipp: Wenn Sie entweder eine CASE-Anweisung oder verschachtelte IF-Anweisungen verwenden können, verwenden Sie die CASE-Anweisung, da sie leichter lesbar und effizienter ist.

Nehmen wir an, dass ein Mitarbeiter einmal pro Jahr evaluiert wird, und Sie möchten die Funktion EVAL_FREQUENCY eine Gehaltserhöhung vorschlagen, die an die JOB_ID gekoppelt ist.

Ändern Sie die Funktion EVAL_FREQUENCY wie in Beispiel 5-5 gezeigt. (Informationen zu den Zeichenfolgen gedruckt werden, DBMS_OUTPUT.PUT_LINE erhalten Sie unter Oracle Database PL/SQL Packages and Types Reference.)

Beispiel 5-5: CASE-Anweisung, die bestimmt, welche Zeichenfolge gedruckt werden soll

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;

Siehe:

"FOR LOOP"-Anweisung verwenden

Mit der Anweisung FOR LOOP wird eine Anweisungssequenz jeweils eine Mal pro Ganzzahl im Bereich lower_bound bis upper_bound wiederholt.

Die Syntax des FOR LOOP lautet:

FOR counter IN lower_bound..upper_bound LOOP
  statement [, statement ]...
END LOOP;

Die Anweisungen zwischen LOOP und END LOOP können Zähler verwenden, den Wert jedoch nicht ändern.

Nehmen wir an, Sie möchten, dass Ihnen die Funktion EVAL_FREQUENCY nicht nur eine Gehaltserhöhung vorschlägt, sondern auch mitteilt, wie das Gehalt aussähe, wenn es fünf Jahre lang jedes Jahr um den vorgeschlagenen Betrag erhöht würde.

Ändern Sie die Funktion EVAL_FREQUENCY wie in Beispiel 5-6 gezeigt. (Informationen zu dem Verfahren, über das die Zeichenfolgen gedruckt werden, DBMS_OUTPUT.PUT_LINE, finden Sie unter Oracle Database PL/SQL Packages and Types Reference.)

Beispiel 5-6: FOR LOOP-Anweisung, über die das Gehalt nach fünf Jahren berechnet wird

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;

Siehe:

"WHILE LOOP"-Anweisung verwenden

Mit der Anweisung WHILE LOOP wird eine Reihe von Anweisungen wiederholt, solange eine Bedingung TRUE ist.

Die Syntax der WHILE LOOP-Anweisung lautet:

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

Hinweis: Wenn die Anweisungen zwischen LOOP und END LOOP nie dazu führen, dass die Bedingung FALSE wird, wird die Anweisung WHILE LOOP endlos ausgeführt.

Angenommen, die Funktion EVAL_FREQUENCY verwendet die Anweisung WHILE LOOP anstelle der Anweisung FOR LOOP und endet, nachdem das vorgeschlagene Gehalt das Maximalgehalt der JOB_ID übersteigt.

Ändern Sie die Funktion EVAL_FREQUENCY wie in Beispiel 5-7 gezeigt. (Informationen zu den Zeichenfolgen gedruckt werden, DBMS_OUTPUT.PUT_LINE erhalten Sie unter Oracle Database PL/SQL Packages and Types Reference.)

Beispiel 5-7 WHILE LOOP-Anweisung zur Berechnung von Gehalt bis Maximum

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;

Siehe:

"Basic LOOP"- und "EXIT WHEN"-Anweisungen verwenden

Die grundlegende Anweisung LOOP wiederholt eine Sequenz von Anweisungen.

Die Syntax der grundlegenden LOOP-Anweisung lautet:

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

Mindestens eine Anweisung muss eine EXIT-Anweisung sein. Andernfalls wird die LOOP-Anweisung unbegrenzt ausgeführt.

Die Anweisung EXIT WHEN (die Anweisung EXIT mit der optionalen WHEN-Klausel) verlässt eine Schleife, wenn eine Bedingung TRUE ist und an das Ende der Schleife überträgt.

In der Funktion EVAL_FREQUENCY übersteigt während der letzten Wiederholung der WHILE LOOP-Anweisung der letzte berechnete Wert normalerweise das Höchstgehalt.

Ändern Sie die Anweisung WHILE LOOP in eine grundlegende LOOP-Anweisung mit der Anweisung EXIT WHEN, wie in Beispiel 5-8.

Beispiel 5-8: EXIT WHEN-Anweisung verwenden

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;

Siehe: