Cursores en SQL Server

Publicado el 17 junio 2014 por Jamesrevelo @herprogramacion

Un cursor es un puntero que apunta a los datos de una fila. Esto nos permite acceder individualmente a cada registro arrojado por una consulta. Para ello usaremos el bucle WHILE de SQL Server .
Como uso un cursor en SQL Server?Es sencillo, debemos seguir al pie de la letra las siguientes 5 fases con cada cursor que usemos:
  1. Declaración
  2. Apertura
  3. Lectura
  4. Cierre
  5. Limpieza

Cuando vimos cursores en MySQL solo usábamos las primeras 4 fases debido a que la limpieza de memoria se realizaba automáticamente. En SQL Server nosotros somos los responsables de la limpieza. 

1. DECLARACION

La declaración de un cursor en SQL Server se hace con la sentencia DECLARE del estándar SQL-92. Simplemente ponemos un nombre acorde al nuevo cursor y luego indicamos la consulta a la cual apuntará. Veamos:
DECLARE nombre_cursor CURSOR [opciones]FOR <consulta>;
También podemos indicarle algunas opciones que caractericen el cursor dependiendo de su naturaleza. Algunas de estas son:
STATIC
Crea una copia de la consulta en tempdb(Base de datos para uso temporal de operaciones), para que el cursor apunte justo a esa copia.
KEYSET
Crea una tabla con los valores prioritarios de la consulta en la base de datos tempdb. Esto permite crear un JOIN entre la consulta original y dicha tabla para que el cursor itere entre esta relación, por lo que puede ser muy engorroso usar este tipo de cursores.
DYNAMIC
Este tipo de cursor apunta a las filas reales que se obtuvieron en la consulta.
FAST_FORWAD
El cursor solo puede ser leído desde el primer elemento hacia el ultimo y será de solo lectura.

2. APERTURA

Abrir el cursor significa ejecutar la consulta a la cual hace referencia. Esta operación se realiza con la palabra reservada OPEN, la cual tiene la siguiente sintaxis:
OPEN nombre_cursor;
Si no abres el cursor no se podrá comenzar a leer información con SQL Server.

3. LECTURA

Usaremos el comando FETCH para obtener los datos de las columnas del siguiente registro en la tabla. Es necesario declarar previamente variables que almacenen los valores mientras leemos las filas:
FETCH [orden] nombre_cursorINTO @var1, @var2,...

4. CIERRE

El cierre de un cursor desbloquea el lote de memoria asignado para la consulta. Lo indicamos con la sentencia CLOSE.
CLOSE cursor_nombre;
Hasta este punto el cursor aun existe en la base de datos, por lo que puede volverse a abrir en el futuro.

5. LIMPIEZA

En esta fase desasignamos la memoria utilizada por el cursor en la base de datos. Para eliminarlo de la base de datos, usaremos la palabra reservada DEALLOCATE.
DEALLOCATE nombre_cursor;
Como recorro todas las filas de una consulta referenciada por un cursor?
Usaremos el bucle WHILE para realizar esta acción, cuya condición de parada esta dada por el valor que arroje la función del sistema @@FETCH_STATUS. Esta función indica el estado del cursor desde la ultima vez que usamos el comando FETCH. Los posibles valores que puede retornar esta función son:
  • 0: Indica que se retorno una fila en el ultimo FETCH.
  • -1: Indica que el ultimo FETCH no retornó ningún resultado debido a que ya no hay mas filas.
  • -2: Indica que la ultima fila que se iba a retornar ya no existe por que se eliminó.

Es importante hacer el primer FETCH antes del WHILE para que la función @@FETCH_STATUS obtenga un resultado positivo.
Podemos ver un ejemplo?Por supuesto!, a continuación resolveremos el siguiente enunciado empleando un cursor en SQL Server:
Transfiera el nombre y el salario de la tabla EMPLEADO, donde el salario es mayor o igual a 2500, en una nueva tabla llamada TOPE_NOMINA.

Miremos la implementación de la solución:
-- Nueva tabla TOPE_NOMINACREATE TABLE TOPE_NOMINA(ID INT NOT NULL IDENTITY PRIMARY KEY,NOMBRE VARCHAR(100) NOT NULL,SALARIO INT NOT NULL);
BEGIN;
-- Variables para obtencion de datos en el cursorDECLARE @EMP_NOMBRE VARCHAR(100), @EMP_SALARIO INT;
-- Declaración del cursorDECLARE cursor_emp CURSOR STATICFOR SELECT NOMBRE, SALARIO FROM EMPLEADOWHERE SALARIO>=2500;
-- Apertura del cursorOPEN cursor_emp;
-- Primer resultado del FETCHFETCH cursor_emp INTO @EMP_NOMBRE, @EMP_SALARIO;
--Bucle de lecturaWHILE (@@FETCH_STATUS = 0 )BEGIN;
-- Transferir los registros a la nueva tablaINSERT INTO TOPE_NOMINA(NOMBRE, SALARIO)VALUES (@EMP_NOMBRE,@EMP_SALARIO);
-- enesima iteración sobre el cursorFETCH cursor_emp INTO @EMP_NOMBRE, @EMP_SALARIO;
END;
-- Cierre del cursorCLOSE cursor_emp;
-- LimpiezaDEALLOCATE cursor_emp;
END;
El código anterior implementa un bucle WHILE que se ejecuta solo si la función @@FETCH_STATUS es igual a cero, es decir, si aun quedan resultados a los que referenciar con el cursor.
James Revelo Urrea - Desarrollador independiente