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 AI 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:

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

variable_name record_name;

Consulte además:

Tutorial: Declaración de un Tipo RECORD

En este tutorial se muestra cómo utilizar la herramienta Editar de SQL Developer para declarar un tipo RECORD, sal_info, cuyos campos pueden contener el sueldo mínimo y máximo para ese identificador de puesto, el sueldo actual y el aumento sugerido.

Para declarar un 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. Haga clic en 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. Haga clic en 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

En este tutorial se muestra 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 creó en "Tutorial: Declaración de un tipo de registro".

En este tutorial se muestra cómo utilizar la herramienta SQL Developer Edit para realizar las siguientes acciones:

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.

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, haga clic en Editar.

    Aparecerá el panel del cuerpo 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 deEND EMP_EVAL, agregue esta 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 del cuerpo de EMP_EVAL, introduzca el código que se muestra en negrita, en esta 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 cambios mostrados en negrita:

    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. Haga clic en Compilar.

Acerca de los Cursores

Cuando la base de datos 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 instrucción SQL DML o una instrucción PL/SQL SELECT INTO, 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 5-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 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 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, 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.

Para los atributos %FOUND, %NOTFOUND y %ROWCOUNT, 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.

Para utilizar el cursor declarado para recuperar las 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 AI 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 AI 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 AI Database.

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

      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 AI 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 AI Database.

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

Este tutorial muestra cómo implementar el procedimiento EMP_EVAL.EVAL_DEPARTMENT, que utiliza un cursor declarado, emp_cursor.

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 negrita:

    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.

Para utilizar una variable de cursor para recuperar una a una las filas de juegos de resultados:

  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 AI 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 AI 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 AI 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 AI 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 AI 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 AI Database.

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

Este tutorial muestra cómo cambiar el procedimiento EMP_EVAL.EVAL_DEPARTMENT para que use una variable del cursor en lugar de una declarada (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.

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 negrita:

    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 el 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 negrita:

    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 negrita:

    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 en negrita:

    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.