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 auch: Oracle AI Database PL/SQL Language Reference für weitere Informationen zu Datensätzen

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:

TYPE record_name IS RECORD
  ( field_name data_type [:= initial_value]
 [, field_name data_type [:= initial_value ] ]... );

variable_name record_name;

Siehe:

Tutorial: Einen RECORD-Typ deklarieren

In diesem Tutorial wird beschrieben, wie Sie mit dem SQL Developer-Tool "Bearbeiten" einen RECORD-Typ namens sal_info deklarieren können, dessen Felder Gehaltsinformationen für einen Mitarbeiter enthält, wie etwa job-ID, Mindest- und Höchstgehalt für diese Job-ID, aktuelles Gehalt und Gehaltserhöhung.

So deklarieren Sie den RECORD-Typ "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. Klicken Sie auf 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. Klicken Sie auf 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

In diesem Tutorial wird gezeigt, wie Sie mit dem SQL Developer-Tool Edit 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 folgende 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.

So erstellen Sie SALARY_SCHEDULE und ändern 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. Klicken Sie in der Auswahlliste auf Bearbeiten.

    Der Bodypan 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 folgende Definition 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 Body-Fenster EMP_EVAL den in Fettschrift dargestellten Code an der folgende Position an:

    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, indem Sie die in Fettschrift dargestellten Änderungen vornehmen:

    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. Klicken Sie auf Kompilieren.

Info zu Cursorn

Wenn die Datenbank 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 wird, 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 5-1 werden die Cursorattribute und die Werte aufgeführt, die sie zurückgeben können. (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, 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, 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, wird eine Zahl größer oder gleich Null angezeigt. 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.

Wenn der Cursor nicht geöffnet ist, löst das Attribut für die Attribute %FOUND,%NOTFOUND und %ROWCOUNT die vordefinierte Ausnahme INVALID_CURSOR aus.

Siehe:

Verwendung eines deklarierten Cursors zum Abrufen von Ergebnismengenzeilen nacheinander

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.

So rufen Sie Ergebnismengenzeilen einzeln mit einem deklarierten Cursor ab:

  1. Im deklarativen Teil:

    1. Deklarieren Sie den Cursor:

      CURSOR cursor_name IS query;

      Eine vollständige Syntax der deklarierten Cursor-Deklaration finden Sie in der Oracle AI 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 AI 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 AI Database PL/SQL Language Reference.

    2. Mit einer LOOP-Anweisung können Sie Zeilen einzeln vom Cursor (Zeilen aus der Ergebnismenge) abrufen. Diese Anweisung sieht ähnlich wie die folgende Syntax aus:

      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 AI Database PL/SQL Language Reference.

    3. Schließen Sie den Cursor:

      CLOSE cursor_name;

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

Tutorial: Einen deklarierten Cursor zum Abrufen von Ergebnismengenzeilen verwenden

In diesem Tutorial wird gezeigt, wie Sie die Prozedur EMP_EVAL.EVAL_DEPARTMENT implementieren, in der ein deklarierter Cursor, emp_cursor, verwendet wird.

So implementieren Sie die Prozedur EMP_EVAL.EVAL_DEPARTMENT:

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

    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 fett 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.

So rufen Sie Ergebnismengenzeilen einzeln mittels einer Cursor-Variablen ab:

  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 AI 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 AI 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 Datensatz-Deklaration finden Sie in Oracle AI 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 OPEN FOR-Anweisungen finden Sie in Oracle AI 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 AI 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 AI Database PL/SQL Language Reference.

Tutorial: Eine Cursorvariable zum Abrufen von einzelnen Ergebnismengenzeilen verwenden

In diesem Tutorial wird beschrieben, wie Sie die Prozedur EMP_EVAL.EVAL_DEPARTMENT ändern können, damit sie anstelle eines deklarierten Cursors eine Cursorvariable verwendet (mit der sie mehrere Abteilungen verarbeiten kann) 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.

So ändern Sie die Prozedur EMP_EVAL.EVAL_DEPARTMENT, um eine Cursorvariable zu verwenden:

  1. Fügen der Package-Spezifikation EMP_EVAL die Prozedurdeklaration und die Typdefinition für REF CURSOR hinzu, wie fett 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 fett 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 wie in Fettschrift dargestellt aufzurufen:

    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 fett dargestellt:

    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.