Cursores en PL/SQL: ejemplos prácticos de declaración, FETCH y control de filas
Ejemplos de cursores en PL/SQL
A continuación se presentan varios ejemplos de cursores explícitos en PL/SQL, con el uso de atributos como %NOTFOUND, %ROWCOUNT y %ROWTYPE.
Ejemplo 35: Recorrer estudiantes de la carrera de Historia
En este bloque se declara un cursor que recupera el identificador, nombre y apellido de los estudiantes de la carrera de Historia.
DECLARE
v_idestudiante estudiantes.idestudiante%TYPE;
v_nombre estudiantes.nombre%TYPE;
v_apellido estudiantes.apellido%TYPE;
v_carrera estudiantes.carrera%TYPE := 'Historia';
CURSOR c_estudiantes IS
SELECT idestudiante, nombre, apellido
FROM estudiantes
WHERE carrera = v_carrera;
BEGIN
/* Identificar las filas en el conjunto activo y preparar para el procesamiento posterior de los datos */
OPEN c_estudiantes;
LOOP
/* Recuperar cada fila del conjunto activo en las variables PL/SQL */
FETCH c_estudiantes INTO v_idestudiante, v_nombre, v_apellido;
DBMS_OUTPUT.PUT_LINE(v_idestudiante || ' ' || v_nombre || ' ' || v_apellido);
/* Si no hay más filas que mostrar, salir del loop */
EXIT WHEN c_estudiantes%NOTFOUND;
END LOOP;
/* Liberar recursos de la consulta */
CLOSE c_estudiantes;
END;Nota: en este ejemplo la condición de salida se evalúa después del DBMS_OUTPUT, por lo que la última fila se muestra dos veces o el último registro vacío puede imprimirse. Conviene situar EXIT WHEN justo después del FETCH para evitarlo.
Ejemplo 36: Uso de %ROWTYPE con un cursor
DECLARE
v_departamento asignaturas.departamento%TYPE;
v_asignatura asignaturas.asignatura%TYPE;
CURSOR c_asignaturas IS
SELECT * FROM asignaturas;
v_registroasignaturas c_asignaturas%ROWTYPE;
BEGIN
OPEN c_asignaturas;
FETCH c_asignaturas INTO v_registroasignaturas;
DBMS_OUTPUT.PUT_LINE(v_registroasignaturas.departamento || ' ' ||
v_registroasignaturas.asignatura || ' ' ||
c_asignaturas%ROWCOUNT);
END;Ejemplo 37: Cursor con parámetros
DECLARE
CURSOR c_asignaturas(p_departamento asignaturas.departamento%TYPE,
p_asignatura asignaturas.asignatura%TYPE) IS
SELECT * FROM asignaturas
WHERE departamento = p_departamento
AND asignatura = p_asignatura;
v_registroasignaturas c_asignaturas%ROWTYPE;
BEGIN
OPEN c_asignaturas('MAT', 102);
FETCH c_asignaturas INTO v_registroasignaturas;
DBMS_OUTPUT.PUT_LINE(v_registroasignaturas.departamento || ' ' ||
v_registroasignaturas.asignatura);
END;Ejemplo 38: UPDATE seguido de INSERT (SQL%NOTFOUND y SQL%ROWCOUNT)
Se intenta actualizar una sala; si no existe, se inserta. Se muestran dos variantes equivalentes.
BEGIN
UPDATE salas
SET numero_asientos = 100
WHERE id_sala = 20001;
IF SQL%NOTFOUND THEN
INSERT INTO salas (id_sala, numero_asientos)
VALUES (20001, 100);
END IF;
END;
BEGIN
UPDATE salas
SET numero_asientos = 100
WHERE id_sala = 20001;
IF SQL%ROWCOUNT = 0 THEN
INSERT INTO salas (id_sala, numero_asientos)
VALUES (20001, 100);
END IF;
END;Ejemplo 39: Manejo de NO_DATA_FOUND
DECLARE
v_datossala salas%ROWTYPE;
BEGIN
SELECT * INTO v_datossala
FROM salas
WHERE id_sala = -1;
IF SQL%NOTFOUND THEN
DBMS_OUTPUT.PUT_LINE('SQL%NOTFOUND es TRUE!');
END IF;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('NO_DATA_FOUND fue usado!');
END;Ejemplo 40: Cursor con LOOP e inserciones
DECLARE
v_idestudiante estudiantes.idestudiante%TYPE;
v_nombre estudiantes.nombre%TYPE;
v_apellido estudiantes.apellido%TYPE;
CURSOR c_estudiantesdehistoria IS
SELECT idestudiante, nombre, apellido
FROM estudiantes
WHERE carrera = 'Historia';
BEGIN
OPEN c_estudiantesdehistoria;
LOOP
FETCH c_estudiantesdehistoria INTO v_idestudiante, v_nombre, v_apellido;
EXIT WHEN c_estudiantesdehistoria%NOTFOUND;
INSERT INTO estudiantes_registrados (idestudiante, departamento, asignatura)
VALUES (v_idestudiante, 'MAT', 102);
INSERT INTO tabla_temp (numero_columna, caracteres_columna)
VALUES (v_idestudiante, v_nombre || ' ' || v_apellido);
END LOOP;
CLOSE c_estudiantesdehistoria;
END;Ejemplo 41: Cursor con WHILE y %FOUND
DECLARE
CURSOR c_estudiantesdehistoria IS
SELECT idestudiante, nombre, apellido
FROM estudiantes
WHERE carrera = 'Historia';
v_datosestudiante c_estudiantesdehistoria%ROWTYPE;
BEGIN
OPEN c_estudiantesdehistoria;
FETCH c_estudiantesdehistoria INTO v_datosestudiante;
WHILE c_estudiantesdehistoria%FOUND LOOP
INSERT INTO estudiantes_registrados (idestudiante, departamento, asignatura)
VALUES (v_datosestudiante.idestudiante, 'MAT', 102);
INSERT INTO tabla_temp (numero_columna, caracteres_columna)
VALUES (v_datosestudiante.idestudiante,
v_datosestudiante.nombre || ' ' || v_datosestudiante.apellido);
FETCH c_estudiantesdehistoria INTO v_datosestudiante;
END LOOP;
CLOSE c_estudiantesdehistoria;
END;