Datensätze und Cursor verwenden

Der Skriptcontent auf dieser Seite ist ausschließlich für Navigationszwecke vorgesehen und bewirkt keinerlei Änderung des Contents.

Sie können Datenwerte in Records speichern und einen Cursor als Zeiger auf eine Ergebnismenge und zugehörige Verarbeitungsinformationen verwenden.

Siehe: Oracle Database PL/SQL-Sprachreferenz, um weitere Informationen zu Datensätzen zu erhalten

Info zu Datensätzen

Ein Datensatz ist eine zusammengesetzte PL/SQL-Variable, in der Datenwerte verschiedener Typen gespeichert werden können. Sie können interne Komponenten (Felder) wie skalare Variablen behandeln. Sie können ganze Records als Unterprogrammparameter übergeben. Datensätze eignen sich für die Speicherung von Daten in Tabellenzeilen oder aus bestimmten Spalten von Tabellenzeilen.

Ein Datensatz ist eine zusammengesetzte PL/SQL-Variable, die Datenwerte verschiedener Typen speichern kann, ähnlich wie ein Strukturtyp in C, C++ oder Java. Die internen Komponenten eines Datensatzes werden als Felder bezeichnet. Um auf ein Datensatzfeld zuzugreifen, verwenden Sie die Punktschreibweise: record_name.field_name.

Sie können die Datensatzfelder wie skalare Variablen verwenden. Sie können auch ganze Records als Unterprogrammparameter übergeben.

Datensätze eignen sich für die Speicherung von Daten in Tabellenzeilen oder aus bestimmten Spalten von Tabellenzeilen. Jedes Datensatzfeld entspricht einer Tabellenspalte.

Es gibt drei Möglichkeiten, einen Datensatz zu erstellen:

Siehe:

Tutorial: Einen RECORD-Typ deklarieren

In den folgenden Schritten wird erläutert, wie Sie mit SQL Developer-Tool "Bearbeiten" einen RECORD-Typ namens sal_info deklarieren können, dessen Felder Gehaltsinformationen für einen Mitarbeiter enthalten, wie etwa Job-ID, Mindest- und Höchstgehalt für diese Job-ID, aktuelles Gehalt und Gehaltserhöhung.

Schritte zum Deklarieren von RECORD type sal_info:

  1. Blenden Sie im Schritt "Verbindungen" hr_conn ein.

    Unter dem Symbol hr_conn wird eine Liste von Schemaobjekttypen angezeigt.

  2. Erweitern Sie Packages.

    Eine Liste der Packages wird angezeigt.

  3. Klicken Sie mit die rechte Maustaste auf EMP_EVAL.

    Eine Optionsliste wird angezeigt.

  4. Wählen Sie Bearbeiten.

    Der Fensterbereich EMP_EVAL wird geöffnet und zeigt die Anweisung CREATE PACKAGE, mit der das Package erstellt wurde:

    CREATE OR REPLACE PACKAGE EMP_EVAL AS
       
    PROCEDURE eval_department(dept_id IN NUMBER);
    FUNCTION calculate_score(evaluation_id IN NUMBER
                           , performance_id IN NUMBER)
                             RETURN NUMBER;
       
    END EMP_EVAL;
    
  5. Fügen Sie im Bereich EMP_EVAL direkt vor END EMP_EVAL folgenden Code hinzu:

     TYPE sal_info IS RECORD
       ( j_id     jobs.job_id%type
       , sal_min  jobs.min_salary%type
       , sal_max  jobs.max_salary%type
       , sal      employees.salary%type
       , sal_raise NUMBER(3,3) );
    

    Der Titel des Bereichs ist kursiv formatiert. Damit wird angegeben, dass die Änderungen noch nicht in der Datenbank gespeichert wurden.

  6. Wählen Sie das Symbol Kompilieren.

    Die geänderte Package-Spezifikation wird kompiliert und in die Datenbank gespeichert. Der Titel des Fensterbereichs EMP_EVAL wird nicht mehr kursiv dargestellt.

    Nun können Sie Datensätze des Typs sal_info deklarieren, wie in "Tutorial: Unterprogramm mit Record-Parameter erstellen und aufrufen" erläutert.

Tutorial: Ein Unterprogramm mit einem Datensatzparameter erstellen und aufrufen

Die folgenden Schritte zeigen, wie Sie mit dem SQL Developer-Tool "Bearbeiten" ein Unterprogramm mit einem Parameter des Datensatztyps sal_info erstellen und aufrufen.

Der Datensatztyp sal_info wurde in "Tutorial: RECORD-Typ deklarieren" erstellt.

In diesem Tutorial wird gezeigt, wie Sie mit dem SQL Developer-Tool Edit die folgenden Aufgaben ausführen:

Da EVAL_FREQUENCY die Prozedur SALARY_SCHEDULE aufruft, muss die Deklaration von SALARY_SCHEDULE vorangehen (anderweder kann das Package nicht kompiliert werden). Die Definition von SALARY_SCHEDULE kann jedoch an einer beliebigen Stelle innerhalb des Package-Bodys erfolgen.

Schritte zum Erstellen von SALARY_SCHEDULE und Ändern von EVAL_FREQUENCY:

  1. Blenden Sie im Schritt "Verbindungen" hr_conn ein.

  2. Blenden Sie in der Liste der Schemaobjekttypen die Option Packages ein.

  3. Blenden Sie in der Liste der Pakete EMP_EVAL ein.

  4. Klicken Sie in der Auswahlliste mit der rechten Maustaste auf EMP_EVAL Body.

  5. Wählen Sie in der Auswahlliste Bearbeiten aus.

    Der Hauptbereich von EMP_EVAL wird angezeigt. Er zeigt den Code für den Package Body.

  6. Fügen Sie im Body-Fenster EMP_EVAL unmittelbar vor END EMP_EVAL die folgenden Definitionen der Prozedur SALARY_SCHEDULE hinzu:

     PROCEDURE salary_schedule (emp IN sal_info) AS
       accumulating_sal  NUMBER;
     BEGIN
       DBMS_OUTPUT.PUT_LINE('If salary ' || emp.sal ||
         ' increases by ' || ROUND((emp.sal_raise * 100),0) ||
    
         '% each year, it will be:');
    
       accumulating_sal := emp.sal;
    
       WHILE accumulating_sal <= emp.sal_max LOOP
         accumulating_sal := accumulating_sal * (1 + emp.sal_raise);
         DBMS_OUTPUT.PUT_LINE(ROUND(accumulating_sal,2) ||', ');
    
       END LOOP;
     END salary_schedule;
    

    Der Titel des Bereichs ist kursiv formatiert. Damit wird angegeben, dass die Änderungen noch nicht in der Datenbank gespeichert wurden.

  7. Geben Sie im Bereich EMP_EVAL Body die Funktion eval_frequency und die Prozeduren salary_schedule und add_eval an der folgenden Position ein:

     CREATE OR REPLACE
     PACKAGE BODY EMP_EVAL AS
    
     FUNCTION eval_frequency (emp_id EMPLOYEES.EMPLOYEE_ID%TYPE)
    
       RETURN PLS_INTEGER;
    
     PROCEDURE salary_schedule(emp IN sal_info);
    
     PROCEDURE add_eval(employee_id IN employees.employee_id%type, today IN DATE);
    
     PROCEDURE eval_department (dept_id IN NUMBER) AS
    
  8. Bearbeiten Sie die Funktion EVAL_FREQUENCY, und nehmen Sie die folgenden Änderungen vor:

     FUNCTION eval_frequency (emp_id EMPLOYEES.EMPLOYEE_ID%TYPE)
       RETURN PLS_INTEGER
     AS
       h_date     EMPLOYEES.HIRE_DATE%TYPE;
       today      EMPLOYEES.HIRE_DATE%TYPE;
       eval_freq  PLS_INTEGER;
    
       emp_sal    SAL_INFO;  -- replaces sal, sal_raise, and sal_max
    
     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;
    
         /* populate emp_sal */
    
         SELECT j.JOB_ID, j.MIN_SALARY, j.MAX_SALARY, e.SALARY
    
         INTO emp_sal.j_id, emp_sal.sal_min, emp_sal.sal_max, emp_sal.sal
    
         FROM EMPLOYEES e, JOBS j
    
         WHERE e.EMPLOYEE_ID = eval_frequency.emp_id
    
         AND j.JOB_ID = eval_frequency.emp_id;
    
         emp_sal.sal_raise := 0;  -- default
    
         CASE emp_sal.j_id
           WHEN 'PU_CLERK' THEN emp_sal.sal_raise := 0.08;
           WHEN 'SH_CLERK' THEN emp_sal.sal_raise := 0.07;
           WHEN 'ST_CLERK' THEN emp_sal.sal_raise := 0.06;
           WHEN 'HR_REP' THEN emp_sal.sal_raise := 0.05;
           WHEN 'PR_REP' THEN emp_sal.sal_raise := 0.05;
           WHEN 'MK_REP' THEN emp_sal.sal_raise := 0.04;
           ELSE NULL;
         END CASE;
    
         IF (emp_sal.sal_raise != 0) THEN
    
           salary_schedule(emp_sal);
         END IF;
       ELSE
         eval_freq := 2;
       END IF;
    
       RETURN eval_freq;
     END eval_frequency;
    
  9. Wählen Sie Kompilieren aus.

Info zu Cursorn

Wenn Oracle Database eine SQL-Anweisung ausrichtet, werden die Ergebnismenge und Verarbeitungsinformationen in einem unbenannten privaten SQL-Bereich gespeichert. Über einen Zeiger zu diesem unbenannten Bereich, der als Cursor bezeichnet ist, können Sie die Ergebnismenge einzeln abrufen. Cursorattribute geben Informationen über den Status des Cursors zurück.

Jedes Mal, wenn Sie eine SQL-DML-Anweisung oder eine PL/SQL SELECT INTO-Anweisung ausführen, öffnet PL/SQL einen impliziten Cursor. Informationen zu diesem Cursor können Sie seinen Attributen entnehmen. Sie können ihn jedoch nicht steuern. Nachdem die Anweisung ausgeführt wird, schließt die Datenbank den Cursor. Seine Attributwerte bleiben jedoch verfügbar, bis eine andere DML- oder SELECT INTO-Anweisung ausgeführt wird.

Mit PL/SQL können Sie auch Cursor deklarieren. Ein deklarierter Cursor hat einen Namen und ist mit einer Abfrage verknüpft (SQL SELECT-Anweisung), die in der Regel mehrere Zeilen zurückgibt. Nachdem Sie einen Cursor deklariert haben, müssen Sie ihn implizit oder explizit verarbeiten. Um den Cursor implizit zu verarbeiten, verwenden Sie einen Cursor FOR LOOP. Die Syntax lautet:

FOR record_name IN cursor_name LOOP
  statement
  [ statement ]...
END LOOP;

Um den Cursor explizit zu verarbeiten, öffnen Sie ihn (mit der OPEN-Anweisung), rufen Zeilen aus der Ergebnismenge entweder einzeln oder im Bulkverfahren (mit der FETCH-Anweisung) ab und schließen den Cursor (mit der CLOSE-Anweisung). Wenn Sie den Cursor geschlossen haben, können Sie weder Datensätze aus der Ergebnismenge abrufen noch die Cursorattributwerte sehen.

Die Syntax für den Wert eines impliziten Cursorattributs lautet SQL%attribute (z.B. SQL%FOUND). SQL%attribute bezieht sich immer auf die zuletzt ausgeführte DML- oder SELECT INTO-Anweisung.

Die Syntax für den Wert eines deklarierten Cursorattributs lautet cursor_name%attribute (z.B. c1%FOUND). In Tabelle 1 sind die Cursorattribute und die Werte, die sie zurückgeben können, aufgeführt. (Implizite Cursor verfügen über zusätzliche Attribute, die nicht in diesem Buch behandelt werden.)

Tabelle 1: Cursorattributwerte

Attribut Werte für deklarierten Cursor Werte für impliziten Cursor
% GEFUNDEN

Wenn der Cursor offen ist (Fußnote 1), aber kein Fetch (Abruf) versucht wurde, NULL.

Wenn der letzte Fetch eine Zeile zurückgegeben hat, TRUE.

Wenn der letzte Fetch keine Zeile zurückgegeben hatte, FALSE.

Wenn keine DML- oder SELECT INTO-Anweisung ausgeführt wurde, NULL.

Wenn die letzte DML- oder SELECT INTO-Anweisung eine Zeile zurückgegeben hat, TRUE.

Wenn die letzte DML- oder SELECT INTO-Anweisung keine Zeile zurückgegeben hatte, FALSE.

% NICHT GEFUNDEN

Wenn der Cursor offen ist (Fußnote 1), aber kein Fetch (Abruf) versucht wurde, NULL.

Wenn der letzte Fetch eine Zeile zurückgegeben hat, FALSE.

Wenn der letzte Fetch keine Zeile zurückgegeben hatte, TRUE.

Wenn keine DML- oder SELECT INTO-Anweisung ausgeführt wurde, NULL.

Wenn die letzte DML- oder SELECT INTO-Anweisung eine Zeile zurückgegeben hat, FALSE.

Wenn die letzte DML- oder SELECT INTO-Anweisung keine Zeile zurückgegeben haben, TRUE.

% ZEILENANZAHL Wenn der Cursor geöffnet ist (Fußnote 1), eine Zahl größer oder gleich Null. NULL, wenn keine DML- oder SELECT INTO-Anweisung ausgeführt wurde; andernfalls eine Zahl größer oder gleich NULL.
%IST GEÖFFNET Wenn der Cursor geöffnet ist, TRUE; wenn nicht, FALSE. Immer falsch.

Fußnote 1: Wenn der Cursor nicht geöffnet ist, löst das Attribut die vordefinierte Ausnahme INVALID_CURSOR aus.

Siehe:

Verwendung eines deklarierten Cursors zum Abrufen von Ergebnismengenzeilen einzeln

Mit einem deklarierten Cursor können Sie Ergebnismengenzeilen nacheinander abrufen.

Die folgende Prozedur verwendet jede benötigte Anweisung in ihrer einfachsten Form, verweist jedoch auf ihre vollständige Syntax.

Schritte zur Verwendung eines deklarierten Cursors zum Abrufen von Ergebnismengenzeilen nacheinander:

  1. Im deklarativen Teil:

    1. Deklarieren Sie den Cursor:

       CURSOR cursor_name IS query;
      

      Informationen zur vollständigen Deklarationssyntax für expliziten Cursor finden Sie in der Oracle Database PL/SQL Language Reference.

    2. Deklarieren Sie einen Datensatz, der die vom Cursor zurückgegebene Zeile enthalten soll:

       record_name cursor_name%ROWTYPE;
      

      Informationen zur vollständigen %ROWTYPE-Syntax finden Sie unter Oracle Database PL/SQL Language Reference.

  2. Im ausführbaren Teil:

    1. Öffnen Sie den Cursor:

       OPEN cursor_name;
      

      Informationen zur vollständigen OPEN-Anweisungssyntax finden Sie unter Oracle Database PL/SQL Language Reference.

    2. Zeilen (aus der Ergebnismenge) einzeln aus dem Cursor (Zeilen aus der Ergebnismenge) abrufen, wobei eine LOOP-Anweisung verwendet wird, die Syntax wie die folgende ausführt:

       LOOP
         FETCH cursor_name INTO record_name;
         EXIT WHEN cursor_name%NOTFOUND;
      
         -- Process row that is in record_name:
      
         statement;
         [ statement; ]...
       END LOOP;
      

      Informationen zur vollständigen FETCH-Anweisungssyntax finden Sie unter Oracle Database PL/SQL Language Reference.

    3. Schließen Sie den Cursor:

       CLOSE cursor_name;
      

Informationen zur vollständigen CLOSE-Anweisungssyntax finden Sie unter Oracle Database PL/SQL Language Reference.

Tutorial: Einen deklarierten Cursor zum Abrufen von Ergebnismengenzeilen verwenden

Die folgenden Schritte zeigen, wie die Prozedur EMP_EVAL.EVAL_DEPARTMENT implementiert wird, die den deklarierten Cursor emp_cursor verwendet.

Schritte zur Implementierung der Prozedur EMP_EVAL.EVAL_DEPARTMENT:

  1. Ändern Sie in der Package-Spezifikation EMP_EVAL die Deklaration der Prozedur EVAL_DEPARTMENT wie folgt:

     PROCEDURE eval_department(dept_id IN employees.department_id%TYPE);
    
  2. Ändern Sie im Package Body EMP_EVAL die Definition der Prozedur EVAL_DEPARTMENT wie im folgenden Beispiel dargestellt:

     PROCEDURE eval_department (dept_id IN employees.department_id%TYPE)
     AS
    
       CURSOR emp_cursor IS
    
         SELECT * FROM EMPLOYEES
    
         WHERE DEPARTMENT_ID = eval_department.dept_id;
    
       emp_record  EMPLOYEES%ROWTYPE;  -- for row returned by cursor
    
       all_evals   BOOLEAN;  -- true if all employees in dept need evaluations
    
       today       DATE;
    
     BEGIN
    
       today := SYSDATE;
    
       IF (EXTRACT(MONTH FROM today) < 6) THEN
    
         all_evals := FALSE; -- only new employees need evaluations
    
       ELSE
    
         all_evals := TRUE;  -- all employees need evaluations
    
       END IF;
    
       OPEN emp_cursor;
    
       DBMS_OUTPUT.PUT_LINE (
    
         'Determining evaluations necessary in department # ' ||
    
         dept_id );
    
       LOOP
    
         FETCH emp_cursor INTO emp_record;
    
         EXIT WHEN emp_cursor%NOTFOUND;
    
         IF all_evals THEN
    
           add_eval(emp_record.employee_id, today);
    
         ELSIF (eval_frequency(emp_record.employee_id) = 2) THEN
    
           add_eval(emp_record.employee_id, today);
    
         END IF;
    
       END LOOP;
    
       DBMS_OUTPUT.PUT_LINE('Processed ' || emp_cursor%ROWCOUNT || ' records.');
    
       CLOSE emp_cursor;
     END eval_department;
    

    (Ein Beispiel für eine schrittweise Änderung eines Packagebodys finden Sie unter "Tutorial: Variablen und Konstanten in Unterprogrammen deklarieren".)

  3. Kompilieren Sie die Package-Spezifikation EMP_EVAL.

  4. Kompilieren Sie den Package Body EMP_EVAL.

Info zu Cursorvariablen

Eine Cursor-Variable ist wie ein Cursor, der nicht auf eine einzelne Abfrage beschränkt ist. Sie können eine Cursor-Variable für eine Abfrage öffnen, die Ergebnismenge verarbeiten und die Cursor-Variable anschließend für eine andere Abfrage wiederverwenden. Cursor-Variablen sind insbesondere für das Übergeben von Abfrageergebnissen zwischen Unterprogrammen nützlich.

Informationen zu Cursor finden Sie unter "Informationen zu Cursor".

Um eine CURSOR-Variable zu deklarieren, deklarieren Sie zunächst einen REF CURSOR-Typ und deklarieren anschließend eine Variable dieses Typs (daher wird CURSOR-Variablen häufig auch REF CURSOR genannt). Ein REF CURSOR-Typ kann schwach oder stark sein.

Ein starker REF CURSOR-Typ gibt einen Rückgabetyp an, bei dem es sich um den RECORD-Typ der Cursorvariablen handelt. Der PL/SQL-Compiler erlaubt die Verwendung dieser starken Cursorvariablen nicht für Abfragen, die Zeilen zurückgeben, welche nicht den Rückgabetyp aufweisen. Starke REF-CURSOR-Typen sind weniger fehleranfällig als schwache, doch schwache Arten bieten größere Flexibilität.

Ein schwacher REF CURSOR-Typ gibt keinen Rückgabetyp an. Der PL/SQL-Compiler akzeptiert schwache Cursor-Variablen in sämtlichen Abfragen. Schwache REF-Cursortypen sind austauschbar; statt schwache REF-Cursortypen zu erstellen, können Sie auch den vordefinierten schwachen Cursortyp SYS_REFCURSOR verwenden.

Nach dem Deklarieren einer Cursor-Variablen müssen Sie sie für eine spezifische Abfrage öffnen (mit der Anweisung OPEN FOR), die Zeilen einzeln aus der Ergebnismenge abrufen (mit der Anweisung FETCH) und ihn für eine weitere spezifische Abfrage öffnen (mit der Anweisung CLOSE) oder ihn für eine weitere spezifische Abfrage öffnen (mit der Anweisung OPEN FOR). Wenn Sie die Cursor-Variable für eine andere Abfrage öffnen, wird sie für die vorherige Abfrage geschlossen. Nach dem Schließen einer Cursor-Variablen für eine spezifische Abfrage können keine Records mehr aus der Ergebnismenge abgerufen werden, noch können die Cursor-Attributwerte für diese Abfrage angezeigt werden.

Siehe:

Eine Cursorvariable zum Abrufen von einzelnen Ergebnismengenzeilen verwenden

Sie können Ergebnismengenzeilen mit einer Cursorvariablen einzeln abrufen.

Die folgende Prozedur verwendet die erforderlichen Anweisungen in der jeweils einfachsten Form, enthält jedoch Verweise auf die vollständige Syntax.

Schritte zur Verwendung einer Cursorvariablen zum Abrufen von Ergebnismengenzeilen einzeln:

  1. Im deklarativen Teil:

    1. Deklarieren Sie den Typ REF CURSOR:

       TYPE cursor_type IS REF CURSOR [ RETURN return_type ];
      

      Die vollständige Syntax für die REF CURSOR-Typendeklaration finden Sie in Oracle Database PL/SQL Language Reference.

    2. Deklarieren Sie eine Cursor-Variable dieses Typs:

       cursor_variable cursor_type;
      

      Die vollständige Syntax der Deklaration von Cursor-Variablen finden Sie in Oracle Database PL/SQL Language Reference.

    3. Deklarieren Sie einen Datensatz, der die vom Cursor zurückgegebene Zeile enthalten soll:

       record_name return_type;
      

      Vollständige Informationen zur Syntax für die Record-Deklaration finden Sie in Oracle Database PL/SQL Language Reference.

  2. Im ausführbaren Teil:

    1. Öffnen Sie die Cursor-Variable für eine spezifische Abfrage:

       OPEN cursor_variable FOR query;
      

      Vollständige Informationen zur Syntax für die Anweisung OPEN FOR finden Sie in Oracle Database PL/SQL Language Reference.

    2. Rufen sie Zeilen (aus der Ergebnismenge) einzeln aus der Cursor-Variablen ab. Verwenden sie hierzu eine LOOP-Anweisung mit folgender Syntax:

       LOOP
         FETCH cursor_variable INTO record_name;
         EXIT WHEN cursor_variable%NOTFOUND;
      
         -- Process row that is in record_name:
      
         statement;
         [ statement; ]...
       END LOOP;
      

      Vollständige Informationen zur Syntax für die FETCH-Anweisung finden Sie in Oracle Database PL/SQL Language Reference.

    3. Schließen Sie die Cursor-Variable:

       CLOSE cursor_variable;
      

      Alternativ dazu können Sie die Cursor-Variable auch für eine weitere Abfrage öffnen, wodurch sie für die aktuelle Abfrage geschlossen wird.

      Vollständige Informationen zur Syntax für die Anweisung CLOSE finden Sie in Oracle Database PL/SQL Language Reference.

Tutorial: Eine Cursorvariable zum Abrufen von einzelnen Ergebnismengenzeilen verwenden

In den folgenden Schritten wird gezeigt, wie Sie die Prozedur EMP_EVAL.EVAL_DEPARTMENT so ändern, dass sie anstelle eines deklarierten Cursors eine Cursorvariable verwendet (mit der mehrere Abteilungen verarbeitet werden können) und wie Sie EMP_EVAL.EVAL_DEPARTMENT und EMP_EVAL.ADD_EVAL effizienter machen.

In diesem Tutorial wird außerdem gezeigt, wie Sie EMP_EVAL.EVAL_DEPARTMENT und EMP_EVAL.ADD_EVAL optimieren: Anstatt ein Feld eines Datensatzes an ADD_EVAL zu übergeben und mit ADD_EVAL drei Abfragen zu verwenden, um drei weitere Felder desselben Datensatzes zu extrahieren, übergibt EVAL_DEPARTMENT den gesamten Datensatz an ADD_EVAL, und ADD_EVAL verwendet Punktschreibweise, um auf Werte der anderen drei Felder zuzugreifen.

Schritte zum Ändern der Prozedur EMP_EVAL.EVAL_DEPARTMENT, um eine Cursorvariable zu verwenden:

  1. Geben Sie in der Package-Spezifikation EMP_EVAL die Prozedurdeklaration und die Typdefinition für REF CURSOR an, wie im folgenden Beispiel dargestellt:

     CREATE OR REPLACE
     PACKAGE emp_eval AS
    
       PROCEDURE eval_department (dept_id IN employees.department_id%TYPE);
    
       PROCEDURE eval_everyone;
    
       FUNCTION calculate_score(eval_id IN scores.evaluation_id%TYPE
                             , perf_id IN scores.performance_id%TYPE)
                               RETURN NUMBER;
       TYPE SAL_INFO IS RECORD
           ( j_id jobs.job_id%type
           , sal_min jobs.min_salary%type
           , sal_max jobs.max_salary%type
           , salary employees.salary%type
           , sal_raise NUMBER(3,3));
    
       TYPE emp_refcursor_type IS REF CURSOR RETURN employees%ROWTYPE;
     END emp_eval;
    
  2. Fügen sie im Package Body EMP_EVAL eine Weiterleitungsdeklaration für die Prozedur EVAL_LOOP_CONTROL hinzu, und ändern sie die Deklaration der Prozedur ADD_EVAL wie dargestellt:

     CREATE OR REPLACE
     PACKAGE BODY EMP_EVAL AS
    
       FUNCTION eval_frequency (emp_id IN EMPLOYEES.EMPLOYEE_ID%TYPE)
         RETURN PLS_INTEGER;
    
       PROCEDURE salary_schedule(emp IN sal_info);
    
       PROCEDURE add_eval(emp_record IN EMPLOYEES%ROWTYPE, today IN DATE);
    
       PROCEDURE eval_loop_control(emp_cursor IN emp_refcursor_type);
     ...
    

    (Ein Beispiel für eine schrittweise Änderung eines Packagebodys finden Sie unter "Tutorial: Variablen und Konstanten in Unterprogrammen deklarieren".)

  3. Ändern sie die Prozedur EVAL_DEPARTMENT, um drei separate Ergebnismengen für die Abteilung abzurufen und um die Prozedur EVAL_LOOP_CONTROL aufzurufen, wie im folgenden Beispiel dargestellt:

     PROCEDURE eval_department(dept_id IN employees.department_id%TYPE) AS
    
       emp_cursor    emp_refcursor_type;
    
       current_dept  departments.department_id%TYPE;
    
     BEGIN
    
       current_dept := dept_id;
    
       FOR loop_c IN 1..3 LOOP
    
         OPEN emp_cursor FOR
    
           SELECT *
    
           FROM employees
    
           WHERE current_dept = eval_department.dept_id;
    
         DBMS_OUTPUT.PUT_LINE
           ('Determining necessary evaluations in department #' ||
    
           current_dept);
    
         eval_loop_control(emp_cursor);
    
         DBMS_OUTPUT.PUT_LINE
           ('Processed ' || emp_cursor%ROWCOUNT || ' records.');
    
         CLOSE emp_cursor;
    
         current_dept := current_dept + 10;
    
       END LOOP;
     END eval_department;
    
  4. Ändern Sie die Prozedur ADD_EVAL wie folgt:

     PROCEDURE add_eval(emp_record IN employees%ROWTYPE, today IN DATE)
     AS
    
     -- (Delete local variables)
     BEGIN
       INSERT INTO EVALUATIONS (
         evaluation_id,
         employee_id,
         evaluation_date,
         job_id,
         manager_id,
         department_id,
         total_score
       )
       VALUES (
         evaluations_sequence.NEXTVAL,   -- evaluation_id
    
         emp_record.employee_id,    -- employee_id
         today,                     -- evaluation_date
    
         emp_record.job_id,         -- job_id
    
         emp_record.manager_id,     -- manager_id
    
         emp_record.department_id,  -- department_id
         0                           -- total_score
     );
     END add_eval;
    
  5. Fügen Sie vor END EMP_EVAL die folgende Prozedur hinzu, um die einzelnen Datensätze aus der Ergebnismenge abzurufen und zu verarbeiten:

     PROCEDURE eval_loop_control (emp_cursor IN emp_refcursor_type) AS
       emp_record      EMPLOYEES%ROWTYPE;
       all_evals       BOOLEAN;
       today           DATE;
     BEGIN
       today := SYSDATE;
    
       IF (EXTRACT(MONTH FROM today) < 6) THEN
    
         all_evals := FALSE;
       ELSE
         all_evals := TRUE;
       END IF;
    
       LOOP
         FETCH emp_cursor INTO emp_record;
         EXIT WHEN emp_cursor%NOTFOUND;
    
         IF all_evals THEN
           add_eval(emp_record, today);
         ELSIF (eval_frequency(emp_record.employee_id) = 2) THEN
    
           add_eval(emp_record, today);
         END IF;
       END LOOP;
     END eval_loop_control;
    
  6. Fügen Sie vor END EMP_EVAL folgende Prozedur hinzu, um eine Ergebnismenge abzurufen, die alle Mitarbeiter des Unternehmens enthält:

     PROCEDURE eval_everyone AS
       emp_cursor emp_refcursor_type;
     BEGIN
       OPEN emp_cursor FOR SELECT * FROM employees;
       DBMS_OUTPUT.PUT_LINE('Determining number of necessary evaluations.');
       eval_loop_control(emp_cursor);
       DBMS_OUTPUT.PUT_LINE('Processed ' || emp_cursor%ROWCOUNT || ' records.');
       CLOSE emp_cursor;
     END eval_everyone;
    
  7. Kompilieren Sie die Package-Spezifikation EMP_EVAL.

  8. Kompilieren Sie den Package Body EMP_EVAL.