Cuando trabajas con bases de datos y ejecutas una consulta que devuelve múltiples filas, a menudo necesitas procesar esos resultados de manera programática, es decir, dentro de un bloque de código como un procedimiento almacenado o una función. Aquí es donde entra en juego el concepto de FETCH.

A diferencia de una simple instrucción SELECT que devuelve un conjunto de resultados (result set), la sentencia FETCH se utiliza en conjunción con un mecanismo llamado cursor para recuperar esas filas una a una o en grupos, permitiéndote trabajar con cada registro individualmente o en lotes dentro de tu lógica de programación.
¿Qué es un Cursor y Por Qué lo Necesitas?
Antes de sumergirnos en FETCH, es fundamental entender el papel de un cursor. Piensa en un cursor como un puntero o un identificador a un área de memoria en el servidor de la base de datos donde se almacena el conjunto de resultados de una consulta. Un cursor te permite:
- Ejecutar una consulta (abrir el cursor).
- Navegar a través del conjunto de resultados.
- Recuperar filas del conjunto de resultados (usando FETCH).
- Saber cuándo has llegado al final del conjunto de resultados.
- Liberar los recursos asociados al conjunto de resultados (cerrar el cursor).
Sin un cursor, sería difícil procesar un conjunto de resultados grande fila por fila dentro de un lenguaje de programación procedural como PL/SQL (usado en Oracle Database) o T-SQL (usado en SQL Server), que son entornos donde la sentencia FETCH es comúnmente encontrada.
La Sentencia FETCH: Recuperando Datos del Cursor
La sentencia FETCH es el comando que utilizas para extraer datos del conjunto de resultados asociado a un cursor que previamente has abierto. La sintaxis básica implica especificar el cursor del que quieres recuperar datos y las variables o estructuras donde quieres almacenar la información de la fila actual.
FETCH Fila a Fila
La forma más simple de usar FETCH es para recuperar una sola fila cada vez que ejecutas la sentencia. Esto se hace típicamente dentro de un bucle (LOOP) que continúa hasta que no hay más filas que recuperar.

DECLARE mi_salario NUMBER(7,2); nombre_empleado VARCHAR2(100); CURSOR emp_cursor IS SELECT ename, sal FROM emp WHERE job = 'CLERK';BEGIN OPEN emp_cursor; -- Abre el cursor para ejecutar la consulta LOOP FETCH emp_cursor INTO nombre_empleado, mi_salario; -- Recupera la siguiente fila EXIT WHEN emp_cursor%NOTFOUND; -- Sale del bucle si no hay más filas -- Aquí procesas la fila recuperada (nombre_empleado, mi_salario) DBMS_OUTPUT.PUT_LINE('Empleado: ' || nombre_empleado || ', Salario: ' || mi_salario); END LOOP; CLOSE emp_cursor; -- Cierra el cursor para liberar recursosEND;En este ejemplo, FETCH emp_cursor INTO nombre_empleado, mi_salario; recupera los valores de las columnas ename y sal de la fila actual del cursor emp_cursor y los asigna a las variables nombre_empleado y mi_salario, respectivamente. Cada vez que se ejecuta FETCH, el cursor avanza a la siguiente fila.
La Cláusula INTO
La cláusula INTO es esencial en la sentencia FETCH. Especifica las variables, campos de registro o elementos de colección donde se almacenarán los datos recuperados de las columnas de la fila actual del cursor. Es crucial que el número y tipo de datos de las variables en la cláusula INTO coincidan con el número y tipo de datos de las columnas seleccionadas en la consulta del cursor.
Uso con Registros (%ROWTYPE)
En lugar de declarar múltiples variables individuales, puedes declarar un registro (record) basado en la estructura de una tabla o en el tipo de fila del cursor (usando %ROWTYPE). Esto simplifica el código cuando la consulta selecciona muchas columnas.
DECLARE TYPE EmpCurTyp IS REF CURSOR RETURN emp%ROWTYPE; -- Tipo de cursor variable emp_cv EmpCurTyp; -- Variable de cursor emp_rec emp%ROWTYPE; -- Registro basado en la estructura de la tabla empBEGIN -- Asumimos que emp_cv ya está abierto y asociado a una consulta LOOP FETCH emp_cv INTO emp_rec; -- Recupera la fila en el registro EXIT WHEN emp_cv%NOTFOUND; -- Procesar emp_rec.ename, emp_rec.sal, etc. DBMS_OUTPUT.PUT_LINE('Empleado: ' || emp_rec.ename || ', Trabajo: ' || emp_rec.job); END LOOP; -- Si el cursor variable se abrió aquí, se cerraría aquíEND;BULK COLLECT para Rendimiento
Recuperar filas una a una con FETCH dentro de un bucle procedural puede ser ineficiente para conjuntos de resultados grandes debido al constante cambio de contexto entre el motor SQL y el motor procedural (como PL/SQL). Para mejorar el rendimiento, especialmente al tratar con muchas filas, se utiliza la cláusula BULK COLLECT.
BULK COLLECT permite a la sentencia FETCH recuperar múltiples filas a la vez en colecciones (como arrays o tablas anidadas) en una sola operación. Esto reduce significativamente el número de viajes de ida y vuelta entre los motores SQL y procedural.

DECLARE TYPE NameList IS TABLE OF emp.ename%TYPE; TYPE SalList IS TABLE OF emp.sal%TYPE; nombres NameList; salarios SalList; CURSOR c1 IS SELECT ename, sal FROM emp WHERE job = 'SALESMAN';BEGIN OPEN c1; -- BULK COLLECT recupera todas las filas (o hasta el límite) en las colecciones FETCH c1 BULK COLLECT INTO nombres, salarios; -- Ahora puedes iterar sobre las colecciones nombres y salarios FOR i IN 1..nombres.COUNT LOOP DBMS_OUTPUT.PUT_LINE('Vendedor: ' || nombres(i) || ', Salario: ' || salarios(i)); END LOOP; CLOSE c1;END;La Cláusula LIMIT con BULK COLLECT
Para evitar cargar un número excesivo de filas en memoria a la vez (lo cual podría causar errores de memoria para conjuntos de resultados gigantes), puedes combinar BULK COLLECT con la cláusula LIMIT. Esto te permite recuperar filas en lotes de un tamaño especificado.
DECLARE TYPE NumList IS TABLE OF NUMBER INDEX BY BINARY_INTEGER; CURSOR c1 IS SELECT acct_id FROM accounts; acct_ids NumList; batch_size CONSTANT NATURAL := 100; -- Define el tamaño del loteBEGIN OPEN c1; LOOP -- Recupera hasta 'batch_size' filas en la colección FETCH c1 BULK COLLECT INTO acct_ids LIMIT batch_size; -- Procesar el lote de IDs recuperados en acct_ids -- ... tu lógica de procesamiento aquí ... -- Salir del bucle cuando el último FETCH no recuperó filas EXIT WHEN c1%NOTFOUND; END LOOP; CLOSE c1;END;Manejo de Errores y Atributos del Cursor
Es crucial saber cómo verificar el estado de una operación FETCH y manejar posibles situaciones.
- %NOTFOUND: Este atributo de cursor devuelve TRUE si el último FETCH no devolvió ninguna fila. Es la forma estándar de detectar el final del conjunto de resultados y salir de un bucle de FETCH.
- %FOUND: Lo opuesto a %NOTFOUND, devuelve TRUE si el último FETCH devolvió al menos una fila.
- INVALID_CURSOR: Esta excepción predefinida se lanza si intentas hacer FETCH desde un cursor que está cerrado o que nunca fue abierto.
Es importante notar que hacer FETCH más allá de la última fila *no* lanza una excepción; simplemente las variables de destino quedan en un estado indeterminado y %NOTFOUND se vuelve TRUE. Debes usar %NOTFOUND para controlar tu bucle.
Consideraciones Importantes
- Las variables utilizadas en la cláusula
WHEREde la consulta de un cursor se evalúan solo una vez, cuando el cursor se abre. Si necesitas cambiar los valores de esas variables y obtener un conjunto de resultados diferente, debes cerrar y volver a abrir el cursor (para cursores explícitos) o simplemente reabrirlo (para variables de cursor) con los nuevos valores de las variables. - Puedes usar diferentes listas INTO en distintas operaciones FETCH sobre el mismo cursor o variable de cursor. Cada FETCH recuperará la siguiente fila y asignará valores a las variables objetivo correspondientes.
- Cuando declaras una variable de cursor como parámetro formal de una subrutina que hará FETCH desde ella, debes especificar el modo
INoIN OUT. Si la subrutina también abre la variable de cursor, debe serIN OUT.
Tabla Comparativa: FETCH Fila a Fila vs. BULK COLLECT
| Característica | FETCH Fila a Fila | FETCH BULK COLLECT |
|---|---|---|
| Granularidad | Una fila por operación | Múltiples filas por operación (en lotes) |
| Destino de Datos | Variables o registros escalares | Colecciones (arrays, tablas anidadas) |
| Rendimiento (Muchas Filas) | Menos eficiente (alto overhead de contexto) | Más eficiente (reduce overhead de contexto) |
| Uso de Memoria | Bajo (procesa una fila a la vez) | Puede ser alto (carga múltiples filas en memoria), mitigado por LIMIT |
| Complejidad del Código | Generalmente más simple para lógica fila a fila | Requiere manejo de colecciones |
| Ideal Para | Procesamiento lógico complejo por fila, conjuntos de resultados pequeños | Procesamiento que opera sobre conjuntos de datos, ETL, conjuntos de resultados grandes |
Preguntas Frecuentes sobre FETCH en Bases de Datos
¿Es FETCH lo mismo que SELECT?
No. SELECT define y recupera un conjunto de resultados. FETCH es una sentencia procedural que recupera filas de un conjunto de resultados que ya ha sido generado por una consulta (generalmente asociado a un cursor).
¿Qué pasa si hago FETCH cuando no quedan filas?
La sentencia FETCH se ejecuta, pero no asigna valores a las variables de destino (su estado es indeterminado) y el atributo %NOTFOUND del cursor asociado se vuelve TRUE. No se lanza una excepción por sí sola.

¿Por qué usar BULK COLLECT en lugar de FETCH normal?
Para mejorar el rendimiento al recuperar y procesar un gran número de filas. Reduce el número de interacciones entre el motor SQL y el motor procedural, lo que disminuye el overhead.
¿Puedo usar FETCH sin un cursor?
En la mayoría de los contextos donde se usa la sentencia FETCH como tal (como PL/SQL), no. FETCH opera sobre un cursor previamente abierto que apunta a un conjunto de resultados.
¿Cuándo se evalúan las variables en la cláusula WHERE de un cursor?
Las variables en la cláusula WHERE de la consulta de un cursor se evalúan en el momento en que se ejecuta la sentencia OPEN para ese cursor.
En resumen, la sentencia FETCH es una herramienta poderosa y necesaria para interactuar programáticamente con los conjuntos de resultados de tus consultas en bases de datos. Ya sea recuperando datos fila a fila para una lógica detallada o utilizando BULK COLLECT para optimizar el rendimiento con grandes volúmenes de datos, comprender y aplicar FETCH correctamente es fundamental para el desarrollo eficiente de aplicaciones de base de datos.
Si quieres conocer otros artículos parecidos a FETCH en Bases de Datos: Recuperando Filas puedes visitar la categoría Bases de datos.

Aprende mas sobre MySQL