Uso de Registros y Cursores

El contenido del script de esta página es sólo para fines de navegación y no modifica el contenido de ninguna forma.

Puede almacenar valores de datos en registros y utilizar un cursor como puntero para un juego de resultados y la información de procesamiento relacionada.

Consulte también: Referencia de lenguaje PL/SQL de Oracle Database para obtener más información sobre los registros

Acerca de los Registros

Un registro es una variable compuesta PL/SQL que puede almacenar valores de datos de diferentes tipos. Puede tratar los componentes internos (campos) como variables escalares. Puede transferir registros completos como parámetros de subprograma. Los registros son útiles para contener datos de las filas de tablas o de determinadas columnas de las filas de tablas.

Un registro es una variable compuesta PL/SQL que puede almacenar valores de datos de diferentes tipos, similares a un tipo de estructura en C, C++ o Java. Los componentes internos de un registro se llaman campos. Para acceder a un campo de registro, utilice notación de punto: nombre_campo_registro.

Puede tratar campos de registro como variables escalares. También puede transferir registros completos como parámetros de subprograma.

Los registros son útiles para contener datos de las filas de tablas o de determinadas columnas de las filas de tablas. Cada campo de registro corresponde a una columna de tabla.

Hay tres formas de crear un registro:

Consulte además:

Tutorial: Declaración de un Tipo RECORD

En los siguientes pasos se muestra cómo utilizar la herramienta Editar SQL Developer para declarar un tipo RECORD, sal_info, cuyos campos pueden contener los datos del salario de un empleado: el identificador de Trabajo, el sueldo mínimo y máximo para ese identificador de Trabajo, el sueldo actual y el aumento sugerido.

Pasos para declarar el tipo RECORD sal_info:

  1. En el marco Conexiones, expanda hr_conn.

    Bajo el icono hr_conn, aparecerá una lista de tipos del objeto de esquema.

  2. Amplíe Paquetes.

    Aparecerá una lista de paquetes.

  3. Haga clic con el mouse en EMP_EVAL.

    Aparecerá una lista de opciones.

  4. Seleccione Editar.

    Se mostrará el panel EMP_EVAL en el que se ha creado el paquete: CREATE PACKAGE

    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. En el panel EMP_EVAL, inmediatamente antes deEND EMP_EVAL, agregue este código:

     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) );
    

    El título del panel aparece en cursiva, lo que indica que los cambios no se han guardado para la base de datos.

  6. Seleccione el icono Compilar.

    La especificación del paquete cambiada se compila y guarda en la base de datos. El título del panel EMP_EVAL dejará de estar en cursiva.

    Ahora puede declarar registros del tipo sal_info, como se indica en "Tutorial: Creación e Invocación del Subprograma con un Parámetro de Registro".

Tutorial: Creación e Invocación de un Subprograma con un Parámetro de Registro

Los siguientes pasos muestran cómo utilizar la herramienta de edición de SQL Developer para crear y llamar a un subprograma con un parámetro del tipo de registro sal_info.

El tipo de registro sal_info se ha creado en "Tutorial: Declaración de un tipo de registro".

En este tutorial se muestra cómo utilizar la herramienta de edición de SQL Developer para realizar las siguientes tareas:

Puesto que EVAL_FREQUENCY invocará SALARY_SCHEDULE, la declaración de SALARY_SCHEDULE debe preceder a la declaración de EVAL_FREQUENCY (de lo contrario, el paquete no se compilará). Sin embargo, la definición de SALARY_SCHEDULE puede estar en cualquier lugar del cuerpo del paquete.

Pasos para crear SALARY_SCHEDULE y cambiar EVAL_FREQUENCY:

  1. En el marco Conexiones, expanda hr_conn.

  2. En la lista de tipos de objetos de esquema, amplíe Paquetes.

  3. En la lista de paquetes, expanda EMP_EVAL.

  4. En la lista de opciones, haga clic con el botón derecho en EMP_EVAL Body (Cuerpo EVAL).

  5. En la lista de opciones, seleccione Editar.

    Aparecerá el panel del cuerpo de EMP_EVAL, en el que se muestra el código del cuerpo del paquete.

  6. En el panel del cuerpo de EMP_EVAL, inmediatamente antes de la END EMP_EVAL, agregue la siguiente definición del procedimiento SALARY_SCHEDULE:

     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;
    

    El título del panel aparece en cursiva, lo que indica que los cambios no se han guardado para la base de datos.

  7. En el panel Cuerpo EMP_EVAL, escriba la función eval_frequency y los procedimientos salary_schedule y add_eval en la siguiente posición:

     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. Edite la función EVAL_FREQUENCY, realizando los siguientes cambios:

     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. Seleccione Compilar.

Acerca de los Cursores

Cuando Oracle Database ejecuta una sentencia SQL, almacena la información de procesamiento y el juego de resultado en un área SQL privada sin nombre. Un puntero a esta área sin nombre, llamado cursor, le permite recuperar el juego de resultado de una fila a una. Los atributos del cursor devuelven información sobre el estado del cursor.

Cada vez que ejecuta una sentencia DML SQL o una sentencia SELECT INTO PL/SQL, PL/SQL abre un cursor implícito. Puede obtener información sobre este cursor de sus atributos, pero no puede controlarlo. Después de ejecutar la sentencia, la base de datos cierra el cursor; sin embargo, sus valores del atributo permanecen disponibles hasta la ejecución de otra sentencia DML o SELECT INTO.

PL/SQL también permite declarar los cursores. Un cursor explícito tiene un nombre y se asocia a una consulta (sentencia SQL SELECT), generalmente, a una que devuelve varias filas. Después de declarar un cursor, debe procesarlo, ya sea implícita o explícitamente. Para procesar el cursor de forma implícita, utilice un cursor FOR LOOP. La sintaxis es:

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

Para procesar el cursor de forma explícita, ábralo (con la sentencia OPEN), recupere las filas del juego de resultados de una en una o en bloque (con la sentencia FETCH) y cierre el cursor (con la sentencia CLOSE). Después de cerrar el cursor, no puede recuperar los registros del juego de resultados ni ver los valores de atributo del cursor.

La sintaxis para el valor de un atributo de cursor implícito es SQL%attribute (por ejemplo, SQL%FOUND). SQL%attribute siempre hace referencia a la sentencia DML o SELECT INTO ejecutada más recientemente.

La sintaxis para el valor de un atributo de cursor declarado es cursor_name%attribute (por ejemplo, c1%FOUND). En la Tabla 1 se muestran los atributos de cursor y los valores que pueden devolver. (Los cursores implícitos tienen atributos adicionales que no se tratan en este libro.)

Tabla 1 Valores de atributo de cursor

Atributo Valores para el cursor declarado Valores para el Cursor Implícito
% ENCONTRADO

Si el cursor está abierto (nota de pie de página 1) pero no se ha intentado realizar ninguna recuperación, NULL.

Si la Recuperación más reciente ha devuelto una fila, TRUE.

Si la Recuperación más reciente no ha devuelto una fila, FALSE.

Si no se ha ejecutado ninguna sentencia DML o SELECT INTO, NULL.

Si la sentencia DML o SELECT INTO más reciente ha devuelto una fila, TRUE.

Si la sentencia DML o SELECT INTO más reciente no ha devuelto una fila, FALSE.

%NO ENCONTRADO

Si el cursor está abierto (nota de pie de página 1) pero no se ha intentado realizar ninguna recuperación, NULL.

Si la Recuperación más reciente ha devuelto una fila, FALSE.

Si la Recuperación más reciente no ha devuelto una fila, TRUE.

Si no se ha ejecutado ninguna sentencia DML o SELECT INTO, NULL.

Si la sentencia DML o SELECT INTO más reciente ha devuelto una fila, FALSE.

Si la sentencia DML o SELECT INTO más reciente no ha devuelto una fila, TRUE.

% DE RECUENTO DE FILAS Si el cursor está abierto (nota al pie 1), un número mayor o igual a cero. NULL si no se ha ejecutado ninguna sentencia DML ni SELECT INTO; de lo contrario, un número mayor o igual a cero.
%ISABIERTO Si el cursor está abierto, TRUE; si no, FALSE. Siempre FALSO.

Nota al pie 1: si el cursor no está abierto, el atributo emite la excepción predefinida INVALID_CURSOR.

Consulte además:

Uso de un Cursor Declarado para Recuperar Filas de Juego de Resultados de una en una

Puede utilizar un cursor declarado para recuperar las filas del juego de resultados de una en una.

El siguiente procedimiento utiliza cada sentencia necesaria en su forma más sencilla, pero proporciona referencias a su sintaxis completa.

Pasos para utilizar un cursor declarado para recuperar filas de juegos de resultados de una en una:

  1. En la parte declarativa:

    1. Declare el cursor:

       CURSOR cursor_name IS query;
      

      Para obtener una sintaxis de declaración de cursor declarada completa, consulte Referencia de lenguaje PL/SQL de Oracle Database.

    2. Declare un registro que va a contener la fila devuelta por el cursor:

       record_name cursor_name%ROWTYPE;
      

      Para obtener una sintaxis %ROWTYPE completa, consulte Referencia de lenguaje PL/SQL de Oracle Database.

  2. En la parte ejecutable:

    1. Abra el cursor:

       OPEN cursor_name;
      

      Para obtener una sintaxis de sentencia OPEN completa, consulte Referencia de lenguaje PL/SQL de Oracle Database.

    2. Recupere una a las filas del cursor (filas del juego de resultados) de una en una, con una sentencia LOOP cuya sintaxis sea similar al siguiente código:

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

      Para obtener una sintaxis de sentencia FETCH completa, consulte Referencia de lenguaje PL/SQL de Oracle Database.

    3. Cierre el cursor:

       CLOSE cursor_name;
      

Para obtener una sintaxis completa de la sentencia CLOSE, consulte Referencia de lenguaje PL/SQL de Oracle Database.

Tutorial: Uso de Un Cursor Declarado para Recuperar Filas de Juego de Resultados de una en una

Los siguientes pasos muestran cómo implementar el procedimiento EMP_EVAL.EVAL_DEPARTMENT, que utiliza un cursor declarado, emp_cursor.

Pasos para implementar el procedimiento EMP_EVAL.EVAL_DEPARTMENT:

  1. En la especificación de paquete EMP_EVAL, cambie la declaración del procedimiento EVAL_DEPARTMENT, de la forma que se muestra en negrita

     PROCEDURE eval_department(dept_id IN employees.department_id%TYPE);
    
  2. En el cuerpo el paquete EMP_EVAL, cambie la definición del procedimiento EVAL_DEPARTMENT, de la forma que se muestra en el siguiente ejemplo:

     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;
    

    (Para ver un ejemplo detallado de cambio en un cuerpo del paquete, consulte "Tutorial: Declaración de Variables y Constantes en un Subprograma".)

  3. Compile la especificación de paquete EMP_EVAL.

  4. Compile el cuerpo de paquete EMP_EVAL.

Acerca de las Variables del Cursor

Una variable de cursor es como un cursor que no se limita a una consulta. Puede abrir una variable de cursor para una consulta, procesar el juego de resultados y, después, utilizar la variable de cursor para otra consulta. Las variables de cursor son útiles para transferir resultados de consulta a otro subprograma.

Para obtener información sobre los cursores, consulte "About Cursors".

Para declarar una variable del CURSOR, declare un tipo REF CURSOR y, después, declare una variable del tipo (por eso, una variable del CURSOR se suele llamar REF CURSOR). Un tipo REF CURSOR puede ser fuerte o débil.

Un tipo fuerte REF CURSOR especifica un tipo de devolución, que es el tipo RECORD de sus variables del CURSOR. El compilador PL/SQL no le permite utilizar estas variables tipo fuerte de cursor para consultas que devuelven filas que no sean de tipo de retorno. Los tipos REF CURSOR fuertes son menos propensos a errores que los débiles, pero los débiles son más flexibles.

Un tipo débil REF CURSOR type no especifica un tipo de retorno. El compilador PL/SQL acepta variables de cursor tipo débil en cualquier consulta. Los tipos REF CURSOR débiles son intercambiables; por lo tanto, en lugar de crear tipos REF CURSOR débiles, puede utilizar el tipo del CURSOR débil predefinido SYS_REFCURSOR.

Después de declarar una variable del cursor, debe abrirla para una consulta concreta (con la sentencia OPEN FOR) y, a continuación, cerrar el cursor (con la sentencia CLOSE) o abrirlo para otra consulta concreta (con la sentencia OPEN FOR). Al abrir la variable de cursor para otra consulta, ésta se cierra para la consulta anterior. Después de cerrar una variable de cursor para una consulta concreta, no puede recuperar los registros del juego de resultados de esa consulta, ni ver los valores de atributo del cursor para esa consulta.

Consulte además:

Uso de una Variable de Cursor para Recuperar Filas de Juegos de Resultados una a una

Puede utilizar una variable de cursor para recuperar las filas de juegos de resultados de una en una.

El siguiente procedimiento utiliza cada una de las sentencias necesarias en su forma más sencilla, pero proporciona referencias a su sintaxis completa.

Pasos para utilizar una variable de cursor para recuperar filas de juegos de resultados de una en una:

  1. En la parte declarativa:

    1. Declare el tipo REF CURSOR:

       TYPE cursor_type IS REF CURSOR [ RETURN return_type ];
      

      Para obtener una sintaxis de declaración de tipo REF CURSOR completa, consulte Referencia de lenguaje PL/SQL de Oracle Database.

    2. Declare una variable del cursor de ese tipo:

       cursor_variable cursor_type;
      

      Para obtener una sintaxis completa de declaración de variables de cursor, consulte Referencia de lenguaje PL/SQL de Oracle Database.

    3. Declare un registro que va a contener la fila devuelta por el cursor:

       record_name return_type;
      

      Para obtener información completa sobre la sintaxis de declaración de registros, consulte Referencia de lenguaje PL/SQL de Oracle Database.

  2. En la parte ejecutable:

    1. Abra la variable de cursor para una consulta concreta:

       OPEN cursor_variable FOR query;
      

      Para obtener información completa sobre la sintaxis de la sentencia OPEN FOR, consulte Referencia de lenguaje PL/SQL de Oracle Database.

    2. Recupere una a las filas de la variable de cursor (filas del juego de resultados) de una en una, con una sentencia LOOP cuya sintaxis es similar a la siguiente:

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

      Para obtener información completa sobre la sintaxis de sentencias FETCH, consulte Referencia de lenguaje PL/SQL de Oracle Database.

    3. Cierre la variable de cursor:

       CLOSE cursor_variable;
      

      Como alternativa, puede abrir la variable de cursor para otra consulta, lo que la cerrará para la consulta actual.

      Para obtener información completa sobre la sintaxis de las sentencias CLOSE, consulte Referencia de lenguaje PL/SQL de Oracle Database.

Tutorial: Uso de una Variable de Cursor para Recuperar Filas de Juegos de Resultados una a una

En los siguientes pasos se muestra cómo cambiar el procedimiento EMP_EVAL.EVAL_DEPARTMENT para que utilice una variable de cursor en lugar de un cursor declarado (que le permite procesar varios departamentos) y cómo hacer que EMP_EVAL.EVAL_DEPARTMENT y EMP_EVAL.ADD_EVAL sean más eficientes.

Cómo hacer que EMP_EVAL.EVAL_DEPARTMENT y EMP_EVAL.ADD_EVAL sean más eficaces: En lugar de transferir a ADD_EVAL un campo de un registro y tener ADD_EVAL utilizar tres consultas para extraer otros tres campos del mismo registro, EVAL_DEPARTMENT transfiere el registro entero a ADD_EVAL y ADD_EVAL utiliza la notación a puntos para acceder a los valores de otros tres campos.

Pasos para cambiar el procedimiento EMP_EVAL.EVAL_DEPARTMENT para que utilice una variable de cursor:

  1. En la especificación de paquete EMP_EVAL, agregue la declaración del procedimiento y la definición de tipo REF CURSOR, como se muestra en el siguiente ejemplo:

     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. En el cuerpo de paquete EMP_EVAL, agregue una declaración anticipada del procedimiento EVAL_LOOP_CONTROL y cambie la declaración del procedimiento ADD_EVAL, como se muestra en:

     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);
     ...
    

    (Para ver un ejemplo detallado de cambio en un cuerpo del paquete, consulte "Tutorial: Declaración de Variables y Constantes en un Subprograma".)

  3. Cambie el procedimiento EVAL_DEPARTMENT para recuperar tres juegos de resultado independientes basados en el departamento y para invocar al procedimiento EVAL_LOOP_CONTROL, como se muestra en el siguiente ejemplo:

     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. Cambie el procedimiento ADD_EVAL como se muestra a continuación:

     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. Antes de END EMP_EVAL, agregue el procedimiento siguiente, que recupera los registros individuales del conjunto de resultados y los procesa:

     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. Antes de END EMP_EVAL, agregue el procedimiento siguiente, que recupera un juego de resultado que contiene todos los empleados de la compañía:

     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. Compile la especificación de paquete EMP_EVAL.

  8. Compile el cuerpo de paquete EMP_EVAL.