lunes, 27 de diciembre de 2010

Programas con DB2 II: CURSOR.

Listado: CURSOR.

Una rutina de listado realiza un CURSOR a una tabla para recuperar todos los registros que cumplan una determinada condición.
Nuestra rutina devolverá la información recuperada al programa llamante a través de una tabla interna definida en la linkage. Esta tabla tendrá un número de ocurrencias OCCURS que puede ser menor que el número de registros recuperados por el cursor.
Tendremos que tener en cuenta todo esto a la hora de codificar el programa.

Vamos con el ejemplo:
Imaginemos que la tabla a la que queremos acceder tiene estos datos:


Siendo CAMPO1 + CAMPO2 la clave.

Definiremos un cursor del siguiente modo:

WORKING-STORAGE SECTION.

EXEC SQL
   DECLARE CURSOR CUR_TABLA FOR
    SELECT CAMPO1,
           CAMPO2,
           CAMPO3,
           CAMPO4,
           CAMPO5
      FROM TABLA1
     WHERE CAMPO1 = :CLAVE-CAMPO1
       AND CAMPO2 > :CLAVE-CAMPO2
     ORDER BY CAMPO1, CAMPO2
END-EXEC

En la linkage recibiremos la información de los campos clave.

LINKAGE SECTION.
01 ENTRADA.
   05 ENT-CAMPO1 PIC XX.
   05 ENT-CAMPO2 PIC XX.
01 SALIDA.
   05 SAL-ULTIMA-CLAVE PIC XXXX.
   05 SAL-RECONSULTAR PIC X.
   05 SAL-DATOS OCCURS 5 TIMES.
      10 SAL-CAMPO1 PIC XX.
      10 SAL-CAMPO2 PIC XX.
      10 SAL-CAMPO3 PIC XX.
      10 SAL-CAMPO4 PIC XX.
      10 SAL-CAMPO5 PIC XX.

En el inicio del programa informaremos las variables de acceso al WHERE, realizaremos el OPEN del cursor y un primer FETCH. Podemos poner una validación del campo clave, para comprobar que trae datos, y en caso de no ser así finalizar la ejecución.

INICIO

IF ENT-CAMPO1 EQUAL SPACES OR LOW-VALUES
   DISPLAY 'LA CLAVE ESTA VACIA'
   PERFORM 90000-FINAL
END-IF

MOVE ENT-CAMPO1 TO CLAVE-CAMPO1
MOVE ENT-CAMPO2 TO CLAVE-CAMPO2

EXEC SQL
   OPEN CUR_TABLA
END-EXEC

Controlaremos el SQLCODE. Si no fuese cero, tendríamos que dar un error.

IF SQLCODE NOT EQUAL ZEROES
   DISPLAY 'ERROR EN EL OPEN CURSOR. EL SQLCODE ES: 'SQLCODE
   PERFORM 90000-FINAL
END-IF

EXEC SQL
   FETCH CUR_TABLA
    INTO :TABLA1-CAMPO1,
         :TABLA1-CAMPO2,
         :TABLA1-CAMPO3,
         :TABLA1-CAMPO4,
         :TABLA1-CAMPO5
END-EXEC

Controlamos el SQLCODE devuelto:

EVALUATE TRUE
   WHEN SQLCODE EQUAL ZEROES
      DISPLAY 'TODO EN ORDEN'
   WHEN SQLCODE EQUAL +100
      DISPLAY 'NO ENCUENTRO NADA'
      PERFORM 90000-FINAL
   WHEN OTHER
      DISPLAY 'ALGO VA MAL. EL SQLCODE ES: 'SQLCODE
      PERFORM 90000-FINAL
END-EVALUATE

SÓLO si el primer FETCH que realizamos nos devuelve un SQLCODE +100 quiere decir que no hay ningún registro con la clave que hemos introducido.
En posteriores FETCH, un SQLCODE +100 significa que YA NO HAY MÁS registros para esa clave.

En el proceso realizaremos un bucle para ir guardando la información devuelta por el cursor en nuestra tabla SAL-TABLA, e ir haciendo sucesivos FETCH hasta que recupere todos o el máximo de OCCURS de nuestra tabla interna:

PROCESO

PERFORM UNTIL SQLCODE EQUAL +100 OR IND-REGISTROS GREATER 5
   PERFORM GUARDAR-EN-SAL-TABLA

   PERFORM HAGO-OTRO-FETCH
END-PERFORM

Si salimos del bucle por la condición de SQLCODE = +100 informaremos el campo SAL-RECONSULTAR con una 'N' y el campo SAL-ULTIMA-CLAVE con espacios, pues ya hemos recuperado todos los registros que había en la tabla.

Si salimos del bucle por la condición de IND-REGISTROS > 5 informaremos el campo SAL-RECONSULTAR con una 'S' y el campo SAL-ULTIMA-CLAVE con los campos clave CAMPO1 y CAMPO2.

En cualquiera de los dos casos, cerraremos el cursor:

EXEC SQL
   CLOSE CUR_TABLA
END-EXEC

Controlamos el SQLCODE. Si es distinto de cero daremos un error.

IF SQLCODE NOT EQUAL ZEROES
   DISPLAY 'ERROR CERRANDO CURSOR. EL SQLCODE ES: 'SQLCODE
END-IF

Según la tabla de nuestro ejemplo, habríamos guardado en SAL-TABLA los registros:

Por lo que nos faltaría por recuperar un último registro. El campo SAL-ULTIMA-CLAVE valdría 'AAFF'.

Para finalizar devolvemos el control al programa llamante.
FINAL

GOBACK.

La siguiente vez que entrásemos al programa accederíamos al cursor con CAMPO1 = 'AA' y CAMPO2 = 'FF' y recuperaríamos el registro que falta:


Os dejo el código completo de un programa de ejemplo para descargar.

Nota: En respuesta a la sugerencia de Jose Antonio, hemos creado un nuevo artículo explicando el uso del ROWSET en los cursores.

12 comentarios:

José Antonio Romero dijo...

Sería interesante que ampliaras las explicaciones con el manejo del cursor con las opción 'ROWSET', muy interesante en ocasiones...

Tallian dijo...

Gracias por la idea!
[acento-tejano]
Estamous trabajando en ellou!
[/acento-tejano]

Anónimo dijo...

muy buena pagina felicitaciones por estos aportes a todos los que estamos incursionando al mundo del cobol

Tallian dijo...

Gracias por leernos!

Anónimo dijo...

tambien seria interesante un ejemplo con un cursor de Lista y Reposicionamiento. Accedientdo por clave a una tabla con 5 campos clave del indice (por ejemplo). Me gustaria ver la estructura.

Anónimo dijo...

super buena la página, cmuasg gracias

Bruno Hauregy dijo...

PERFORM GUARDAR-EN-SAL-TABLA

PERFORM HAGO-OTRO-FETCH

Hola estos perform donde los usas (osea sus instrucciones donde las realizas)
ya que me interesa saber n_n

Bruno Hauregy dijo...

la liga de descarga no funciona

Tallian dijo...

Bruno, he actualizado el enlace para que puedas ver el código completo del ejemplo.
Un saludo.

marco mateo dijo...

usando cursores las puedo meter directo a db2, osea que sea algo sistematizado, donde el usuario no teclee nada

Ernesto Santos Yung dijo...

Hola, necesito asesoria, en cuestión de performance y de rendimiento ¿es igual declarar los cursores en WORKING-STORAGE, o en la PROCEDURE DIVISIÓN?

Tallian dijo...

Hola Ernesto.
Efectivamente es igual donde lo declares. Digamos que el estándar es declararlo en la WS, por aquello de que en la PD tienes que tener cuidado de declararlo antes del resto de sentencias :-P
Pero no hay diferencia.