Controllo del flusso dei programmi

A differenza del linguaggio SQL, che esegue le istruzioni nell'ordine in cui sono state immesse, PL/SQL dispone delle istruzioni del controllo che consentono il controllo del flusso del programma.

Informazioni sulle istruzioni di controllo

PL/SQL dispone di tre categorie di istruzioni di controllo: istruzioni di selezione condizionale, istruzioni loop e istruzioni di controllo sequenziali.

Le istruzioni di selezione condizionali consentono di eseguire istruzioni diverse per valori di dati diversi. Le istruzioni di selezione condizionale sono IF e CASE.

Le istruzioni LOOP consentono che si ripetano le stesse istruzioni con una serie di valori dei dati differenti. Le istruzioni LOOP sono FOR LOOP, WHILE LOOP e l'istruzione LOOP di base. L'istruzione EXIT trasferisce il controllo alla fine di un loop. L'istruzione CONTINUE permette di uscire dall'iterazione corrente di un loop e trasferisce il controllo all'iterazione successiva. Sia EXIT che CONTINUE hanno una clausola opzionale WHEN, in cui è possibile specificare una condizione.

Le istruzioni di controllo sequenziali consentono di passare a un'istruzione con etichetta specifica o di non eseguire nulla. Le istruzioni di controllo sequenziali sono GOTO e NULL.

Per una panoramica delle istruzioni di controllo PL/SQL, vedere anche: Oracle Database PL/SQL Language Reference

Uso dell'istruzione IF

L'istruzione IF esegue o salta una sequenza di istruzioni, in base al valore di un'espressione booleana.

L'istruzione IF ha la seguente sintassi:

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

Si supponga che la propria azienda valuti i dipendenti due volte all'anno nei primi 10 anni di impiego, ma solo una volta l'anno successivamente. Si desidera una funzione che restituisca la frequenza di valutazione per un dipendente. È possibile utilizzare un'istruzione IF per determinare il valore restituito della funzione, come nell'Esempio 5-4.

Aggiungere la funzione EVAL_FREQUENCY al corpo del package EMP_EVAL, ma non alla specifica. Poiché non si trova nella specifica, la funzione EVAL_FREQUENCY è locale rispetto al package, pertanto può essere richiamata solo da altri sottoprogrammi nel package, non dall'esterno di quest'ultimo.

Suggerimento: quando si utilizza una variabile PL/SQL in un'istruzione SQL, come nella seconda istruzione SELECT in Esempio 5-4, qualificare la variabile con il nome del sottoprogramma per assicurarsi che non venga scambiata per una colonna di tabella.

Vedere anche:

Istruzione IF di esempio 5-4 che determina il valore restituito della funzione

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;

Uso dell'istruzione CASE

L'istruzione CASE sceglie da una sequenza di condizioni ed esegue l'istruzione corrispondente.

L'istruzione CASE semplice valuta una singola espressione e la confronta con diversi valori potenziali. La sintassi è la seguente:

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

L'istruzione CASE ricercata valuta più espressioni booleane e sceglie la prima parola il cui valore corrisponde a TRUE. Per informazioni sull'istruzione CASE ricercata, vedere Oracle Database PL/SQL Language Reference.

Suggerimento: quando si può utilizzare un'istruzione CASE o istruzioni IF nidificate, utilizzare un'istruzione CASE, in quanto più leggibile e anche al contempo più efficace.

Si supponga che, se un dipendente viene valutato solo una volta l'anno, la funzione EVAL_FREQUENCY suggerisca un aumento di stipendio, che dipende da JOB_ID.

Modificare la funzione EVAL_FREQUENCY come illustrato nell'Esempio 5-5. Per informazioni sulle procedure per la stampa delle stringhe, DBMS_OUTPUT.PUT_LINE, vedere Oracle Database PL/SQL Packages and Types Reference.

Istruzione CASE di esempio 5-5 che determina la stringa da stampare

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;

Vedere anche:

Uso dell'istruzione FOR LOOP

L'istruzione FOR LOOP ripete una sequenza di istruzioni una volta per ogni numero intero nell'intervallo tra lower_bound e upper_bound.

La sintassi di FOR LOOP è:

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

Le istruzioni tra LOOP e END LOOP possono utilizzare un contatore, ma non può modificarne il valore.

Supponiamo che, invece di suggerire solo un aumento di stipendio, si supponga che la funzione EVAL_FREQUENCY indichi a quanto corrisponderà lo stipendio se aumentato dell'importo suggerito ogni anno per cinque anni.

Modificare la funzione EVAL_FREQUENCY come illustrato nell'Esempio 5-6. Per informazioni sulla procedura per la stampa delle stringhe, DBMS_OUTPUT.PUT_LINE, vedere Oracle Database PL/SQL Packages and Types Reference.

Esempio 5-6 FOR LOOP Statement che calcola lo stipendio dopo cinque anni

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;

Vedere anche:

Uso dell'istruzione WHILE LOOP

L'istruzione WHILE LOOP ripete una sequenza di istruzioni mentre una condizione è TRUE.

La sintassi dell'istruzione WHILE LOOP è:

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

Nota: se le istruzioni tra LOOP e END LOOP non fanno mai accadere che la condizione diventi FALSE, l'istruzione WHILE LOOP viene eseguita continuamente.

Si supponga che la funzione EVAL_FREQUENCY utilizzi l'istruzione WHILE LOOP al posto dell'istruzione FOR LOOP e termini prima che lo stipendio proposto superi lo stipendio massimo per JOB_ID.

Modificare la funzione EVAL_FREQUENCY come illustrato nell'Esempio 5-7. Per informazioni sulle procedure per la stampa delle stringhe, DBMS_OUTPUT.PUT_LINE, vedere Oracle Database PL/SQL Packages and Types Reference.

Esempio 5-7 istruzione WHILE LOOP che calcola lo stipendio al massimo

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;

Vedere anche:

Uso delle istruzioni EXIT WHEN e LOOP di base

L'istruzione LOOP di base ripete una sequenza di istruzioni.

La sintassi dell'istruzione LOOP di base è:

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

Almeno un'istruzione deve essere un'istruzione EXIT; in caso contrario, l'istruzione LOOP viene eseguita all'infinito.

L'istruzione EXIT WHEN (l'istruzione EXIT con la clausola opzionale WHEN) consente di chiudere da un loop quando una condizione è TRUE e trasferisce il controllo alla fine del loop.

Nella funzione EVAL_FREQUENCY, nell'ultima iterazione dell'istruzione WHILE LOOP, l'ultimo valore calcolato, in genere, supera lo stipendio massimo.

Modificare l'istruzione WHILE LOOP in un'istruzione LOOP di base che includa l'istruzione EXIT WHEN, come descritto nell'Esempio 5-8.

Esempio 5-8 Uso dell'istruzione 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;

Vedere anche: