Documentos de Académico
Documentos de Profesional
Documentos de Cultura
DB2 Completo
DB2 Completo
1º) En primer lugar, en el comando CREATE tenemos que indicar el nombre de la tabla que va a ser creada
(obviamente).
2º) A continuación, se enumerarán todos los campos que van a estar incluidos en la misma. Para cada
campo se indicará el formato asociado. Por ejemplo:
3º) También se deberá indicar cuál es la clave principal de la tabla creada. Esto se establecerá con el
subcomando PRIMARY KEY.
PRIMARY KEY(CLIENTE)
4º) Adicionalmente, al final se puede indicar el TABLESPACE en el que deseamos que se incluya la tabla que
vamos a crear. Esto se realizará con el comando IN.
IN TSCLIE
Si, como en el ejemplo inicial, no se incluye esta cláusula IN al final, entonces la ejecución del comando
CREATE creará automáticamente un TABLESPACE que albergará únicamente a la tabla creada (se tratará
de un Tablespace unitario que no podrá contener más de una tabla).
Una vez ejecutada la función CREATE, nuestra tabla quedará dada de alta. El comando puede ser ejecutado,
por ejemplo, mediante la herramienta QMF (Query Management Facility) del DB2I, que se encuentra en el
ISPF de cualquier entorno CICS.
Tras crear nuestra tabla, podríamos acceder a los campos de la misma mediante un simple comando
SELECT, indicando el nombre que le hemos asociado a la misma.
En estos casos, para añadir el nuevo campo a la tabla podremos recurrir a la sentencia ALTER de SQL. Este
comando nos permite alterar la estructura de la tabla, ya sea para añadir o para eliminar una columna
determinada.
En particular, para crear un nuevo campo tendremos que incluir la cláusula ADD y, a continuación, indicar
tanto el nombre de la columna como el formato asociado a la misma. Finalmente, podremos incluir (o no) la
cláusula NOT NULL, según convenga.
En el ejemplo indicado, tras ejecutar la sentencia ALTER, se puede comprobar en una consulta que ya queda
dada de alta una nueva columna denominada FECHA (con formato TIMESTAMP). Además, en este caso
incluso ya hemos insertado un valor para dicho campo en uno de los registros.
CLIENTE FECHA
------------- --------------------------
1 2013-07-24-10.50.22.217174
Para ello, únicamente tendremos que entrar en la herramienta QMF del DB2I y emplear el
comando ALTER para la tabla afectada. Esta sentencia permite modificar la estructura de una tabla DB2 sin
más que indicar el nombre de la misma.
Posteriormente, se precisará incluir la cláusula ALTER COLUM y el nombre del campo afectado por la
modificación de formato que queremos llevar a cabo.
Esta cláusula incluirá también el comando SET DATA TYPE y, a continuación, el nuevo formato que va a
asumir el campo afectado. En el ejmplo, el campo "PESO" pasará a tener el formato NUMERIC (5,2).
Tras ejecutar la sentencia ALTER, una simple consulta SQL lanzada desde la herramienta QMF nos mostrará
la tabla con el nuevo formato del campo modificado. El campo PESO tiene una longitud de 8 posiciones,
correspondiente al formato Numeric(5,2).
La herramienta DCLGEN sirve para realizar la generación de objetos DCL. El DCL (Data Control Language)
es la parte del lenguaje SQL destinada al control de los datos. Así que, dicho de otro modo, el DCLGEN nos
permite realizar la generación de la parte del código SQL que contiene las variables necesarias para el
tratamiento de las tablas DB2 desde los programas Cobol.
Obviamente, estas sentencias DCL también se podrían generar manualmente, pero siempre será más
cómodo realizar la tarea mediante un generador automático como el DCLGEN. Una vez hayamos aprendido
cómo se opera con él, lo más probable es que nunca dejemos de usarlo.
Los pasos a seguir para realizar la generación de un objeto con código DCL son los siguientes, suponiendo
que nos encontramos en el menú principal POM (Primary Option Menu) del ISPF.
1º) Seleccionamos la opción 12 - Subsystems - DB/DC Subsystems. Se nos abrirá una ventana mostrando
las diversas opciones disponibles.
2º) Opción D - DB2I: Seleccionamos la opción D - DB2I - Interactive Database 2. Así entraremos en el menú
principal (POM) de la herramienta DB2I.
3º) Opción 2 - DCLGEN: Seleccionamos la opción 2 - DCLGEN - Generate SQL and source language
declarations.
4º) En el panel DCLGEN tendremos que introducir los parámetros requeridos para la generación del objeto
DCL.
- SOURCE TABLE NAME: Aquí tendremos que introducir el nombre de la tabla para la cual deseamos que se
genere su objeto DCL asociado. Por ejemplo, nosotros especificamos la tabla EQUIPOS.
- TABLE OWNER: Aquí habrá que especificar el nombre del usuario propietario de la tabla.
- DATA SET NAME: En este campo se debe especificar el nombre (y la ubicación) del objeto DCL que va a
generar la herramienta DCLGEN. Por ejemplo, nosotros queremos un objeto llamado SEQUIPOS que deberá
crearse en la librería JJ00.FACTURAS.DCLGEN.DATA.
- ACTION: Se provisionará el comando ADD si vamos a generar un nuevo objeto DCL o el comando
REPLACE si se va a cambiar la estructura de un objeto ya existente.
- INDICATOR VARS: Si queremos que nuestro objeto DCL incluya la declaración de las Variables de
Indicador Nulo para los campos de la tabla que lo requieran, entonces tendremos que indicar YES en este
parámetro.
5º) Tras especificar todos los parámetros anteriores y pulsar INTRO, nos aparecerá un mensaje indicando
que la generación se ha llevado a cabo correctamente.
A continuación, iríamos a la librería especificada en DATA SET NAME (en nuestro ejemplo, la
JJ00.FACTURAS.DCLGEN.DATA) y podríamos verificar la existencia del nuevo objeto DCL generado.
Si nos asomamos al interior del objeto, veremos que su contenido tendrá un aspecto similar al siguiente:
******************************************************************
* DCLGEN TABLE(SYSTEM1.EQUIPOS) *
* LIBRARY(JJ00.FACTURAS.DCLGEN.DATA(SEQUIPOS)) *
* LANGUAGE(COBOL) *
* QUOTE *
* INDVAR(YES) *
* ... IS THE DCLGEN COMMAND THAT MADE THE FOLLOWING STATEMENTS *
******************************************************************
EXEC SQL DECLARE EQUIPOS TABLE
( EQUINO CHAR(3) NOT NULL,
EQUINAME VARCHAR(36) NOT NULL,
ENTNO CHAR(6),
EMPREQUI CHAR(3) NOT NULL,
CITYNAME VARCHAR(16)
) END-EXEC.
******************************************************************
* COBOL DECLARATION FOR TABLE SYSTEM1.EQUIPOS *
******************************************************************
01 DCLEQUIPOS.
10 EQUINO PIC X(3).
10 EQUINAME.
49 EQUINAME-LEN PIC S9(4) USAGE COMP.
49 EQUINAME-TEXT PIC X(36).
10 ENTNO PIC X(6).
10 EMPREQUI PIC X(3).
10 CITYNAME.
49 CITYNAME-LEN PIC S9(4) USAGE COMP.
49 CITYNAME-TEXT PIC X(16).
******************************************************************
* INDICATOR VARIABLE STRUCTURE *
******************************************************************
01 IEQUIPOS.
10 INDSTRUC PIC S9(4) USAGE COMP OCCURS 5 TIMES.
******************************************************************
* THE NUMBER OF COLUMNS DESCRIBED BY THIS DECLARATION IS 5 *
******************************************************************
El próximo día seguiremos viendo el procedimiento que hay que seguir para poder hacer uso del contenido del
objeto DCL desde el programa Cobol en el que vamos a operar con el fichero DB2. Recordemos que, al fin y
al cabo, la finalidad del DCL es que nos facilite el intercambio de información entre el entorno SQL DB2 y el
entorno Host Cobol.
Tal y como estuvimos viendo, siguiendo el procedimiento indicado, la herramienta DCLGEN nos generaba un
objeto en una librería de nuestra elección. Al final del anterior post llegamos a mostrar un ejemplo de cómo
era el contenido de un objeto DCL que habíamos generado con el nombre SEQUIPOS.
Como se podía observar en dicho ejemplo, el código de un objeto DCL está dividido en 3 partes:
A) Estructura de la Tabla DB2. En esta primera parte nos aparece el nombre de la tabla DB2 y de las
columnas que la componen. Estos son los nombres de columna que tendremos que usar en las sentencias
SQL que usemos en nuestro programa Cobol.
B) Estructura de Variable Host. En esta segunda parte nos aparece la definición de todas las variables Host
que podríamos utilizar en el programa Cobol. Básicamente, el DCLGEN habrá creado una variable Host
equivalente a cada una de las columnas de la tabla DB2. La idea es que podamos usar cada una de estas
variables de la agrupación DCLEQUIPOS para recoger la información de la columna de nombre análogo de la
tabla DB2.
C) Estructura de Variables Indicador. En la tercera y última parte se habrán creado las Variables de
Indicador Nulo asociadas a cada una de las variables Host que lo requieran. Se da de alta un array
INDSTRUC cuyo tamaño será igual al número de columnas de la tabla DB2 (en el ejemplo, se crea un
INDSTRUC de 5 ocurrencias debido a que la tabla EQUIPOS tiene 5 columnas).
Para que toda la estructura de datos creada está disponible desde Cobol, en el programa tendremos que
incluir la correspondiente sentencia INCLUDE haciendo referencia al objeto DCL. En nuestro ejemplo,
en la WORKING-STORAGE SECTION tendríamos que incluir tanto las del área de comunicaciones estándar
SQLCA como las de nuestro objeto SEQUIPOS.
Con la definición anterior, un ejemplo de cómo se codificaría, en un programa Cobol, una declaración SELECT
contra la tabla EQUIPOS empleando el objeto DCL sería la siguiente. En primer lugar, ponemos la definición
del Cursor de la tabla (que estaría en la WORKING-STORAGE SECTION) y, a continuación, la sentencia
FETCH que carga la información en las variables Host (ubicada en la PROCEDURE DIVISION).
...
...
...
Como vemos, en el FETCH se está haciendo referencia a las variables Host que aparecen en el objeto DCL
definidas bajo la agrupación DCLEQUIPOS (en el DECLARE CURSOR que aparece justo encima, por contra,
se hace referencia a los nombres de las columnas de la tabla DB2). Una vez recuperadas, ahora ya
podremos trabajar normalmente con estas variables Host en el código de nuestro programa Cobol. Por
ejemplo, podríamos hacer un DISPLAY así.
Como se puede observar, el empleo de la herramienta DCLGEN nos ahorra mucho trabajo a la hora de
definir, en nuestros programas Cobol, las estructuras de las tablas DB2 y las estructuras de sus
variables Host asociadas. Esto mismo se podría declarar de forma manual, pero es indudable que
haciéndolo mediante la herramienta nos ahorraremos quebraderos de cabeza.
Siguiendo los pasos que se han ido indicando en el post, no deberíamos tener ningún problema con la
generación de los objetos DCL a través de la herramienta DCLGEN. Quizás os parezca un poco complicado la
primera vez, pero os podemos asegurar que enseguida dominaréis el procedimiento y que agradeceréis el
tiempo economizado.
En principio, con todo lo comentado, por nuestra parte quedaría completado el tema de la herramienta
DCLGEN. De todas formas, no dudéis en consultarnos cualquier duda que os surja respecto a ella, si lo
consideráis oportuno.
El área SQLCA es una estructura que contiene una serie de variables estándar que van a ser empleadas
para que nuestro programa reciba información acerca de cómo se han ejecutado nuestras sentencias SQL
sobre una tabla DB2.
Este área contiene las siguientes variables: SQLCAID, SQLCABC, SQLCODE, SQLERRM, SQLERRP,
SQLERRD, SQLWARN y SQLSTATE. De ellas, la más importante es la SQLCODE, ya que este código de
retorno nos indicará si el acceso a la tabla DB2 ha sido correcto o no.
* AREA SQLCA
01 SQLCA SYNC.
05 SQLCAID PIC X(8) VALUE "SQLCA ".
05 SQLCABC PIC S9(9) COMP-5 VALUE 136.
05 SQLCODE PIC S9(9) COMP-5.
05 SQLERRM.
05 SQLERRP PIC X(8).
05 SQLERRD OCCURS 6 TIMES PIC S9(9) COMP-5.
05 SQLWARN.
10 SQLWARN0 PIC X.
10 SQLWARN1 PIC X.
10 SQLWARN2 PIC X.
10 SQLWARN3 PIC X.
10 SQLWARN4 PIC X.
10 SQLWARN5 PIC X.
10 SQLWARN6 PIC X.
10 SQLWARN7 PIC X.
10 SQLWARN8 PIC X.
10 SQLWARN9 PIC X.
10 SQLWARNA PIC X.
05 SQLSTATE PIC X(5).
*
Para declarar esta área en nuestro programa Cobol bastará con incluir una cláusula INCLUDE en la sección
WORKING-STORAGE. Si no hacemos esta declaración, la compilación del programa fallará, ya que el gestor
de BBDD no tendrá definida el área de comunicaciones.
******************************************************************
* WORKING-STORAGE SECTION
******************************************************************
WORKING-STORAGE SECTION.
*
* AREA COMUNICACIONES SQL
* -----------------------
EXEC SQL
INCLUDE SQLCA
END-EXEC.
*
Como vemos, la cláusula es muy sencilla. Únicamente hay que escribir INCLUDE SQLCA entre las cláusulas
EXEC SQL y END-EXEC de SQL. De esta forma, en nuestro programa ya estarán disponibles para su
uso todas las variables existentes en la estructura SQLCA.
Y eso es todo. Si la cláusula INCLUDE SQLCA se define de la forma indicada en el fragmento de código de
más arriba, entonces no deberíamos tener ningún problema con nuestro proceso de compilación (en lo que
respecta al área de comunicaciones SQLCA, obviamente).
Para finalizar, vamos a ver cuál es el tratamiento que se suele realizar sobre la variable SQLCODE (del área
SQLCA) cada vez que realizamos un acceso a una tabla DB2. Este tratamiento se debe incluir a continuación
de la sentencia que accede a la tabla.
En nuestro programa, tras el acceso a DB2, deberemos recoger el valor de la variable SQLCODE para
saber si el acceso se ha realizado de forma correcta o no. Un ejemplo de cómo se haría esto es el siguiente
(este código se añadiría tras la ejecución de una sentencia SELECT, por ejemplo).
*
* VALIDACION DEL ACCESO DB2
* -------------------------
EVALUATE TRUE
WHEN SQLCODE = ZEROES
DISPLAY 'ACCESO DB2 OK'
WHEN SQLCODE = +100
DISPLAY 'NO EXISTE REGISTRO'
WHEN OTHER
DISPLAY 'ERROR ACCESO DB2. SQLCODE: ' SQLCODE
PERFORM 9980-GENERAR-ERROR
THRU 9980-GENERAR-ERROR-EXIT
END-EVALUATE
*
Como vemos en el código, lo que se hace es validar si la variable SQLCODE es igual a algo de lo siguiente:
- Valor Cero: En ese caso, el acceso DB2 ha sido correcto y podemos continuar con nuestro proceso en el
programa Cobol.
- Valor +100: aquí nos está indicando que no existe el registro buscado.
- Cualquier otro valor: significa que se ha producido un error en el acceso DB2. Por tanto, en nuestro
programa deberemos indicar que la ejecución ha sido errónea.
Si establecemos así el tratamiento de SQLCODE tras cada acceso a una tabla DB2, entonces nuestro
programa nos informará perfectamente de si dicho acceso se ha ejecutado correctamente o si, por contra, se
ha producido un error.
Para poder ejecutar una sentencia sobre una tabla DB2 necesitaremos en primer lugar realizar
su declaración mediante la cláusula DECLARE. En ella se deberá especificar el nombre de la tabla DB2 y
los nombres de los campos de la misma que van a ser empleados en el programa.
******************************************************************
* WORKING-STORAGE SECTION
******************************************************************
WORKING-STORAGE SECTION.
*
* DECLARE JJCLIEM0
* ----------------
EXEC SQL
DECLARE JJCLIEM0 TABLE(
CLIENTE NUMERIC(10),
NOMBRE CHAR(20),
APELLIDO1 CHAR(20),
APELLIDO2 CHAR(20),
ALTURA NUMERIC(3,2),
PESO NUMERIC(3,2),
IMC NUMERIC(3,2),
OBSERVACIONES CHAR(50),
FECHA TIMESTAMP)
END-EXEC.
*
Como vemos, la cláusula es bastante simple. Los pasos a seguir son los siguientes:
1º) Abrir un párrafo de SQL con las cláusulas EXEC SQL y END-EXEC. Entre ellas se declarará la
información de la tabla DB2.
2º) Especificar, con DECLARE TABLE, el nombre de la tabla DB2.
3º) Finalmente, enumerar la lista de campos de la tabla DB2 (junto con su formato) que van a ser utilizados
en el programa Cobol.
No hay mucho más que decir. El funcionamiento de esta cláusula es así de sencillo y no debería darnos
ningún problema en compilación. Por supuesto, el único problema sería que se nos olvidara especificarla en
nuestro programa.
La sentencia INSERT nos permitirá incorporar un nuevo registro en nuestra tabla DB2 mediante la
indicación de los campos que queremos que tengan algún contenido. También tendremos que indicar los
valores que deberán tener dichos campos.
1º) Cláusula INSERT INTO: donde tendremos que indicar el nombre del fichero DB2 y la denominación de
todos los campos en los que queremos almacenar información para el nuevo registro.
2º) Cláusula VALUES: donde tendremos que especificar los valores que quedarán contenidos en cada uno
de los campos especificados anteriormente. Dichos valores podrán proceder de constantes o de variables.
Tras la ejecución de la sentencia INSERT tendremos que establecer, como siempre, un control de la variable
SQLCODE. De este modo verificaremos si la inserción se ha realizado correctamente o si, por contra, se ha
detectado algún error.
Una vez controlado el SQLCODE, pasaremos a indicar si la ejecución ha sido correcta, si el registro ya existía
en el fichero DB2 o si se ha producido algún otro tipo de error. No debemos olvidar establecer este tipo de
controles, ya que es muy frecuente que se obtenga el retorno 803 al intentar dar de alta un registro que ya
existe en la tabla.
Para la implementación de la sentencia DELETE de una tabla DB2 no tenemos que especificar demasiada
información. Bastará con indicar el nombre del dichero DB2 y el nombre del campo clave por el que se debe
buscar el registro que queremos eliminar.
2º) WHERE: en esta cláusula habrá que incluir un campo clave de la tabla DB2. Esta clave nos servirá para
buscar e identificar el registro que debe ser borrado.
La validación del SQLCODE nos deberá decir si todo ha ido correcto, si no existe el registro que se quiere
eliminar o si se ha producido algún otro error. Obviamente, si la clave buscada no existe en el DB2,
entonces la sentencia no puede eliminar ningún registro de la tabla.
Para ejecutar el UPDATE sobre el fichero DB2 tendremos que indicar, en primer lugar, el nombre del fichero.
A continuación, especificaremos los nombres de los campos que queremos modificar y los valores que
deseamos que sustituyan a los que están actualmente contenidos en el registro.
1º) Cláusula UPDATE: aquí tenemos que hacer referencia a la tabla DB2 que va a ser actualizada. En el
ejemplo, su nombre es JJCLIEM0.
2º) Cláusula SET: aquí debemos especificar todos los campos que van a ser actualizados y, a continuación,
los nuevos valores que van a ser almacenados en el registro. Estos valores pueden indicarse como una
constante o como una variable (en este último caso, la variable tendrá que estar precedida por el literal ":").
3º) Cláusula WHERE: finalmente, aquí indicaremos el campo clave por el que se va a buscar el registro (o
registros, si son varios) que debe ser modificado. En el ejemplo, se actualizarán todos los registros cuyo
campo CLIENTE coincida con el valor de la variable CLAVE-CLIENTE.
A continuación, como siempre que accedemos a un fichero DB2, tendremos que proceder a controlar el
estado de la variable SQLCODE.
Este control de SQLCODE nos dirá si el acceso a la tabla DB2 se ha efectuado correctamente, si el registro
buscado no existe en la base de datos o si se ha producido algún otro tipo de error. Por supuesto, la
ejecución no puede realizar ninguna modificación si no encuentra ningún registro con las características
requeridas.
En principio, ejecutando las líneas de código indicadas más arriba no deberíamos tener ningún problema y
nuestro programa Cobol tendría que actualizar los registros DB2 sin problema alguno.
El comando SELECT es el más sencillo que se puede usar cuando estamos trabajando con un programa
Cobol DB2. No es necesario extraer listas de información ya que lo único que estamos buscando son los
datos del registro sobre el que nos vamos a posicionar.
A continuación, mostramos un ejemplo de cómo sería el acceso a un registro de la tabla DB2 denominada
JJCLIEM0 mediante la sentencia SELECT.
Los pasos que estamos siguiendo en este fragmento de código son los siguientes:
1º) Cargamos la clave por la que vamos a acceder a la tabla DB2 JJCLIEM0. En este caso, se trata del
campo CLAVE-CLIENTE.
2º) A continuación, ejecutamos la sentencia SQL SELECT indicando los nombres de los campos que
queremos extraer de la tabla JJCLIEM0. En este caso son CLIENTE, NOMBRE, APELLIDO1, APELLIDO2,
ALTURA, PESO, IMC, OBSERVACIONES y FECHA.
3º) Con el comando INTO especificamos los nombres de las variables que van a recibir los datos
extraídos de los campos de la tabla JJCLIEM0.
4º) Finalmente, tras la ejecución de la sentencia SELECT, evaluamos el resultado del acceso mediante el
parámetro SQLCODE. Si es cero, entonces la operación de acceso ha sido correcta.
Y esos son, en resumen, los pasos que habría que seguir para implementar correctamente una sentencia
SELECT en nuestro programa Cobol DB2. Si lo codificamos tal y como se indica en el ejemplo, no
deberíamos tener problema alguno posteriormente en el proceso de compilación.
La sentencia FETCH se emplea normalmente cuando queremos implementar un programa Cobol cuyo
objetivo es extraer un listado de información o mostrar por pantalla una lista de datos. En esos casos,
con dicha sentencia podremos ir recuperando registros de forma iterativa a partir de una clave inicial de
lectura.
Aquí tenemos un ejemplo de cómo se debería implementar una sentencia FETCH con acceso, mediante
Cursor, a una tabla DB2.
EXEC SQL
FETCH CUR_JJCLIEM0
INTO
:S-CLIENTE,
:S-NOMBRE,
:S-APELLIDO1,
:S-APELLIDO2,
:S-ALTURA,
:S-PESO,
:S-IMC,
:S-OBSERVACIONES INDICATOR :I-OBS,
:S-FECHA
END-EXEC
La estructura de la tabla DB2 (cuya denominación es JJCLIEM0) a la que se accede mediante el cursor
indicado se compone de los siguientes campos:
CLIENTE numeric(10)
NOMBRE char(20)
APELLIDO1 char(20)
APELLIDO2 char(20)
ALTURA numeric(3,2)
PESO numeric(3,2)
IMC numeric(3,2)
OBSERVACIONES char(50)
FECHA timestamp
1º) En primer lugar, se incluye el comando FETCH y, a continuación, se indica el nombre del Cursor con el
que se accederá a la tabla DB2 (en este caso, se trata del cursor CUR_JJCLIEM0, que accede a la tabla DB2
JJCLIEM0).
2º) Posteriormente, tras la cláusula INTO, se deben añadir los nombres de las variables en las que se irán
cargando los valores recogidos de los campos correspondientes de la tabla DB2. Los nombres de las
variables del programa Cobol deberán estar precedidos por el literal ":".
En el caso del ejemplo, las variables que recogen la información están definidas de la siguiente forma en el
programa Cobol.
01 PAR-SALIDA.
05 S-CLIENTE PIC S9(10) COMP.
05 S-NOMBRE PIC X(20).
05 S-APELLIDO1 PIC X(20).
05 S-APELLIDO2 PIC X(20).
05 S-ALTURA PIC S999V99 COMP-3.
05 S-PESO PIC S999V99 COMP-3.
05 S-IMC PIC S999V99 COMP-3.
05 S-OBSERVACIONES PIC X(50).
05 S-FECHA PIC X(26).
01 VAR-INDICADOR-NULO.
05 I-OBS PIC S9(4) COMP.
Como vemos, para poder trabajar con la sentencia FETCH previamente tendremos que haber definido un
CURSOR para la tabla DB2 sobre la que queremos acceder. En un futuro post veremos cómo se debe realizar
la declaración de un cursor en un programa Cobol.
Obviamente, y al igual que ocurría con la sentencia SELECT, en la sección WORKING-STORAGE tendremos
que haber realizado el DECLARE TABLE de la tabla JJCLIEM0 y el INCLUDE SQLCA correspondientes. En
caso contrario, se producirá un error en la fase de compilación.
De hecho, tras la sentencia FETCH deberíamos incluir un control de la variable SQLCODE (de la estructura
SQLCA), para verificar si el acceso a DB2 ha sido correcto o no. El acceso podría realizarse de forma similar a
la indicada a continuación.
EVALUATE TRUE
WHEN SQLCODE = ZEROES
ADD 1 TO CO-LEIDOS-ENTRADA1
DISPLAY 'ACCESO DB2 OK'
WHEN SQLCODE = +100
SET FIN-ENTRADA1 TO TRUE
DISPLAY '---> NO EXISTE REGISTRO'
WHEN OTHER
MOVE SQLCODE TO SQLCODE-DIS
DISPLAY 'ERROR ACCESO DB2. SQLCODE: ' SQLCODE-DIS
MOVE SQLCODE-DIS TO WS-FILE-ERROR
MOVE 'ERROR ACCESO DB2' TO WS-DESCRIPCION
PERFORM 9980-GENERAR-ERROR
THRU 9980-GENERAR-ERROR-EXIT
END-EVALUATE.
Finalmente, también hay que mencionar que, antes de la ejecución del FETCH, deberá realizarse la apertura
del CURSOR. Del mismo modo, una vez finalizado el acceso FETCH, habrá que realizar el cierre del
CURSOR.
En un programa Cobol, el acceso a una tabla DB2 con la sentencia Fetch se realiza mediante un Cursor. Por
tanto, en primer lugar tendremos que haber definido dicho Cursor, mediante la sentencia DECLARE
CURSOR, indicando cuáles son los campos que queremos que sean recuperados de la tabla DB2 en el
momento de su acceso.
Como siempre, lo veremos mejor con el siguiente ejemplo, donde se está declarando el Cursor denominado
CUR_JJCLIEM0, que posteriormente nos servirá para acceder a la tabla JJCLIEM0.
1º) Sentencia DECLARE CURSOR FOR: en ella deberá indicarse el nombre que le queremos dar al cursor.
Con ese nombre es con el que se hará referencia a él en el programa Cobol.
2º) A continuación, se incluye una cláusula SELECT donde se especificarán todos los campos de la tabla
DB2 que van a formar parte del Cursor.
3º) Cláusula FROM: aquí es donde especificaremos el nombre de la tabla DB2 a la que accederá nuestro
Cursor (en este caso, JJCLIEM0).
4º) Cláusula WHERE: para especificar las condiciones que deben cumplir los registros de la tabla DB2 para
ser extraidos por el Cursor.
5º) Finalmente, con el comando ORDER BY indicaremos el orden en que deben ser leídos los datos de la
tabla DB2.
Una vez definido el Cursor, antes de que pueda ser empleado para acceder a una tabla DB2, deberemos
proceder a la apertura del mismo. Esto se realizará mediante una sencilla sentencia de OPEN CURSOR. Si
seguimos con el ejemplo anterior, la apertura sería así.
A continuación, como siempre, habrá que validar el estado de la variable SQLCODE para saber si la apertura
del Cursor ha sido correcta o no.
Del mismo modo, tras la finalización del acceso a la tabla DB2, deberemos proceder a realizar el cierre del
Cursor. Esto lo realizaremos mediante la sentencia CLOSE CURSOR. El código quedaría así.
* CERRAMOS EL CURSOR
* ------------------
EXEC SQL
CLOSE CUR_JJCLIEM0
END-EXEC
*
Aquí también tendremos que validar el estado del SQLCODE, para asegurarnos de que el cierre del Cursor se
ha ejecutado correctamente.
* VALIDAMOS SQLCODE DEL CLOSE CURSOR
* ----------------------------------
EVALUATE TRUE
WHEN SQLCODE = ZEROES
DISPLAY 'ACCESO DB2 OK'
INITIALIZE WS-FILE-ERROR
WHEN OTHER
MOVE SQLCODE TO SQLCODE-DIS
MOVE SQLCODE-DIS TO WS-FILE-ERROR
MOVE 'ERROR CLOSE CURSOR DB2' TO WS-DESCRIPCION
PERFORM 9980-GENERAR-ERROR
THRU 9980-GENERAR-ERROR-EXIT
END-EVALUATE.
*
El acceso a la tabla DB2 mediante el Cursor ya lo vimos en el post dedicado a la sentencia FETCH.
Refrescaremos la memoria aquí, poniendo cómo quedaría el código para el ejemplo que hemos ido siguiendo
a lo largo de todo el post.
EXEC SQL
FETCH CUR_JJCLIEM0
INTO
:S-CLIENTE,
:S-NOMBRE,
:S-APELLIDO1,
:S-APELLIDO2,
:S-ALTURA,
:S-PESO,
:S-IMC,
:S-OBSERVACIONES INDICATOR :I-OBS,
:S-FECHA
END-EXEC
Por último, mencionar que, antes de comenzar a operar, tendremos que inicializar también las claves por
las que va a ir leyendo el Cursor. Por ejemplo, podríamos hacer lo siguiente:
En primer lugar, necesitaremos un proceso en el que se vaya recuperando toda la información del fichero
DB2 y cargándola en una tabla. Una vez finalizado dicho proceso, incluiremos una rutina que vaya
procediendo a leer los datos de la tabla y los vaya mostrando por pantalla.
PERFORM 1000-INICIO
THRU 1000-INICIO-EXIT
*
PERFORM 2000-PROCESO
THRU 2000-PROCESO-EXIT
UNTIL FIN-ENTRADA1
*
PERFORM 3000-LISTADO
THRU 3000-LISTADO-EXIT
Rutina de Inicio
Aquí tendremos que hacer un primer acceso al fichero DB2 para recuperar el primer registro y almacenarlo en
la variable tabla. Si la rutina que hace eso acceso se denomina, por ejemplo, LEER-ENTRADA, entonces la
invocación en la rutina de inicio sería la siguiente.
PERFORM 9100-LEER-ENTRADA1
THRU 9100-LEER-ENTRADA1-EXIT.
*
Lectura de Fichero DB2
En la rutina 2000-PROCESO tendremos que incluir la misma invocación a LEER-ENTRADA que se hizo en la
rutina Inicio, con la diferencia de que aquí se ejecutará varias veces, hasta que se llegue al último registro
disponible en el fichero DB2.
Este subproceso LEER-ENTRADA tendrá que incluir un acceso a la tabla DB2 mediante la sentencia FETCH.
La estructura sería algo similar a la siguiente.
9100-LEER-ENTRADA1.
*
* CARGAMOS REGISTRO DE LA TABLA EN LA VARIABLE DE TRABAJO
* ------------------------------------------------------
- INITIALIZE PAR-SALIDA
EXEC SQL
FETCH CUR_JJCLIEM0
INTO
:S-CLIENTE,
:S-NOMBRE,
:S-APELLIDO1,
:S-APELLIDO2,
:S-ALTURA,
:S-PESO,
:S-IMC,
:S-OBSERVACIONES INDICATOR :I-OBS,
:S-FECHA
END-EXEC
*
* VERIFICAMOS SI LAS OBSERVACIONES SON NULAS (NULL)
* -------------------------------------------------
IF I-OBS < 0
MOVE '-' TO S-OBSERVACIONES
END-IF
*
* GESTION DEL SQLCODE
* -------------------
EVALUATE TRUE
WHEN SQLCODE = ZEROES
ADD 1 TO CO-TAB
ADD 1 TO CO-LEIDOS-ENTRADA1
WHEN SQLCODE = +100
SET FIN-ENTRADA1 TO TRUE
WHEN OTHER
MOVE SQLCODE TO SQLCODE-DIS
MOVE SQLCODE-DIS TO WS-FILE-ERROR
MOVE 'ERROR ACCESO DB2' TO WS-DESCRIPCION
PERFORM 9980-GENERAR-ERROR
THRU 9980-GENERAR-ERROR-EXIT
END-EVALUATE
*
* CARGAMOS REGISTRO EN LA TABLA DE DATOS
* --------------------------------------
IF SQLCODE = ZEROS
MOVE S-CLIENTE TO T-CLIENTE(CO-TAB)
MOVE S-NOMBRE TO T-NOMBRE(CO-TAB)
MOVE S-APELLIDO1 TO T-APELLIDO1(CO-TAB)
MOVE S-APELLIDO2 TO T-APELLIDO2(CO-TAB)
MOVE S-ALTURA TO T-ALTURA(CO-TAB)
MOVE S-PESO TO T-PESO(CO-TAB)
MOVE S-IMC TO T-IMC(CO-TAB)
MOVE S-OBSERVACIONES TO T-OBSERVACIONES(CO-TAB)
MOVE S-FECHA TO T-FECHA (CO-TAB)
END-IF.
*
9100-LEER-ENTRADA1-EXIT.
EXIT.
*
2º) Validación del SQLCODE del acceso al fichero DB2 y actuación en función de su resultado. Hay que
observar cómo, cada vez que el acceso es correcto, se incrementa en 1 unidad el valor del contador de la
variable Tabla (CO-TAB).
3º) Finalmente, carga de los datos de la variable auxiliar a la posición correspondiente de la variable Tabla,
especificada por CO-TAB (esta tabla nos servirá para almacenar todos los registros del fichero DB2).
Rutina de Listado
Por último, tendremos una rutina que irá accediendo a la variable Tabla donde se almacenaron los datos del
fichero DB2 e irá mostrando la información por pantalla. El display de registros se ejecutará hasta que se
alcance el número máximo de registros leídos del fichero DB2 (MAX-REG).
3000-LISTADO.
MOVE 1 TO CO-TAB
*
* MOSTRAMOS EL LISTADO DE CLIENTES
* --------------------------------
PERFORM 3100-DISPLAY-REGISTRO
THRU 3100-DISPLAY-REGISTRO-EXIT
UNTIL CO-TAB > MAX-REG.
*
3000-LISTADO-EXIT.
EXIT.
*
La rutina de Display podrá implementarse de muchas formas. Desde una simple sentencia DISPLAY a una
carga de datos sobre un MAPA, o incluso a un volcado de datos sobre un fichero secuencial. Eso ya
dependerá del objetivo específico del programa Cobol implementado.
3100-DISPLAY-REGISTRO.
*
* MOSTRAMOS LA INFORMACION DEL CLIENTE
* ------------------------------------
DISPLAY CO-TAB ' > '
T-CLIENTE(CO-TAB) ' / '
T-NOMBRE(CO-TAB) ' / '
T-APELLIDO1(CO-TAB) ' / '
T-APELLIDO2(CO-TAB) ' / '
T-ALTURA(CO-TAB) ' / '
T-PESO(CO-TAB) ' / '
T-IMC(CO-TAB) ' / '
T-OBSERVACIONES(CO-TAB) ' / '
T-FECHA(CO-TAB) ' / '
*
ADD 1 TO CO-TAB.
*
3100-DISPLAY-REGISTRO-EXIT.
EXIT.
*
Y nada más. Esto sería suficiente para acceder a un fichero DB2, obtener un listado de datos a partir de su
lectura y mostrar dicha información por pantalla.
Un problema típico que puede aparecer en estos casos se produce cuando las ocurrencias de la variable
Tabla no son suficientes para almacenar todos los registros que existen en el fichero DB2.
En esos casos habría que recurrir a lo que se denomina proceso de Paginación, que consiste en llenar la
variable Tabla con datos del fichero DB2, mostrarlos por pantalla, vaciar dicha Tabla y volver a llenarla con los
siguientes datos recuperados del DB2 (y así iterativamente hasta llegar al final del fichero DB2).
Para implementar una paginación necesitaremos una rutina que acceda al fichero DB2 mediante la sentencia
FETCH y obtenga registros del mismo hasta que se produzca el llenado de la variable Tabla, momento en el
que se incrementará el contador de páginas. A continuación, se mostrá la información por pantalla y se
reseteará la variable Tabla. Así podremos volver a proceder a su llenado una vez más, lo que sería algo así
como la siguiente página del proceso. Todo esto se repetirá hasta que ya no queden más registros que
recuperar del DB2.
2000-PROCESO.
*
* MOSTRAMOS DATOS POR PANTALLA
* ----------------------------
IF CO-PAG > 0
INITIALIZE CO-TAB
INITIALIZE TAB-SALIDA
SET NO-FIN-PAGINA1 TO TRUE
END-IF
*
* LEEMOS EL SIGUIENTE REGISTRO DEL FICHERO
* ----------------------------------------
PERFORM 9100-LEER-ENTRADA1
THRU 9100-LEER-ENTRADA1-EXIT
UNTIL FIN-PAGINA1
*
PERFORM 3000-LISTADO
THRU 3000-LISTADO-EXIT.
*
2000-PROCESO-EXIT.
EXIT.
*
A continuación, pasamos a detallar los procesos anteriores.
Rutina de Inicio
Aquí tendremos que hacer un primer acceso al fichero DB2 para recuperar el primer registro y almacenarlo en
la variable tabla. Si la rutina que hace eso acceso se denomina, por ejemplo, LEER-ENTRADA, entonces la
invocación en la rutina de inicio sería la siguiente.
PERFORM 9100-LEER-ENTRADA1
THRU 9100-LEER-ENTRADA1-EXIT.
*
En la rutina 2000-PROCESO tendremos que incluir la misma invocación a LEER-ENTRADA que se hizo en la
rutina Inicio, con la diferencia de que aquí se ejecutará varias veces, hasta que se llegue al final de la
página actual (es decir, hasta que se produzca el llenado de la variable Tabla que recoge la información del
DB2).
Este subproceso LEER-ENTRADA tendrá que incluir un acceso a la tabla DB2 mediante la sentencia FETCH.
La estructura sería algo similar a la siguiente.
9100-LEER-ENTRADA1.
*
* CARGAMOS REGISTRO DE LA TABLA EN LA VARIABLE DE TRABAJO
* -------------------------------------------------------
INITIALIZE PAR-SALIDA
EXEC SQL
FETCH CUR_JJCLIEM0
INTO
:S-CLIENTE,
:S-NOMBRE,
:S-APELLIDO1,
:S-APELLIDO2,
:S-ALTURA,
:S-PESO,
:S-IMC,
:S-OBSERVACIONES INDICATOR :I-OBS,
:S-FECHA
END-EXEC
*
* VERIFICAMOS SI LAS OBSERVACIONES SON NULAS (NULL)
* -------------------------------------------------
IF I-OBS < 0
MOVE '-' TO S-OBSERVACIONES
END-IF
*
* GESTION DEL SQLCODE
* -------------------
EVALUATE TRUE
WHEN SQLCODE = ZEROES
ADD 1 TO CO-TAB
ADD 1 TO CO-LEIDOS-ENTRADA1
WHEN SQLCODE = +100
SET FIN-ENTRADA1 TO TRUE
WHEN OTHER
MOVE SQLCODE TO SQLCODE-DIS
MOVE SQLCODE-DIS TO WS-FILE-ERROR
MOVE 'ERROR ACCESO DB2' TO WS-DESCRIPCION
PERFORM 9980-GENERAR-ERROR
THRU 9980-GENERAR-ERROR-EXIT
END-EVALUATE
*
* CONTROL DE PAGINACION
* ---------------------
IF SQLCODE = 100 OR CO-TAB = 3
SET FIN-PAGINA1 TO TRUE
ADD 1 TO CO-PAG
END-IF
*
* CARGAMOS REGISTRO EN LA TABLA DE DATOS
* --------------------------------------
IF SQLCODE = ZEROS
MOVE S-CLIENTE TO T-CLIENTE(CO-TAB)
MOVE S-NOMBRE TO T-NOMBRE(CO-TAB)
MOVE S-APELLIDO1 TO T-APELLIDO1(CO-TAB)
MOVE S-APELLIDO2 TO T-APELLIDO2(CO-TAB)
MOVE S-ALTURA TO T-ALTURA(CO-TAB)
MOVE S-PESO TO T-PESO(CO-TAB)
MOVE S-IMC TO T-IMC(CO-TAB)
MOVE S-OBSERVACIONES TO T-OBSERVACIONES(CO-TAB)
MOVE S-FECHA TO T-FECHA (CO-TAB)
END-IF.
*
9100-LEER-ENTRADA1-EXIT.
EXIT.
*
Como vemos, la estructura se divide en 4 partes:
1º) Acceso al fichero DB2 mediante FETCH y recogida de los datos en la variable auxiliar correspondiente
(incluyendo variables de indicador nulo, si hiciesen falta).
2º) Validación del SQLCODE del acceso al fichero DB2 y actuación en función de su resultado. Hay que
observar como, cada vez que el acceso es correcto, se incrementa en 1 unidad el valor del contador de la
variable Tabla (CO-TAB).
3º) Control de Paginación. Una vez se ha llenado la variable Tabla, procedemos a incrementar en 1 unidad
el contador de páginas y activamos el parámetro de Fin de Página actual. De esta forma, el proceso sabrá que
puede pasar a ejecutar la rutina que muestra la información y, posteriormente, repetir el proceso de lectura del
fichero DB2 para la siguiente página.
4º) Finalmente, carga de los datos de la variable auxiliar a la posición correspondiente de la variable Tabla,
especificada por CO-TAB (esta tabla nos servirá para almacenar todos los registros del fichero DB2).
Rutina de Listado
Por último, tendremos una rutina que irá accediendo a la variable Tabla donde se almacenaron los datos del
fichero DB2 e irá mostrando la información por pantalla. El display de registros se ejecutará hasta que se
alcance el número máximo de registros almacenados en la variable Tabla (MAX-PAG, que se
correspondería con el número de registros visualizables en una página).
3000-LISTADO.
MOVE 1 TO CO-TAB
*
* MOSTRAMOS EL LISTADO DE CLIENTES
* --------------------------------
PERFORM 3100-DISPLAY-REGISTRO
THRU 3100-DISPLAY-REGISTRO-EXIT
UNTIL CO-TAB > MAX-PAG.
*
3000-LISTADO-EXIT.
EXIT.
*
La rutina de Display podrá implementarse de muchas formas. Desde una simple sentencia DISPLAY a una
carga de datos sobre un MAPA, o incluso a un volcado de datos sobre un fichero secuencial. Eso ya
dependerá del objetivo específico del programa Cobol implementado.
3100-DISPLAY-REGISTRO.
* MOSTRAMOS LA INFORMACION DEL CLIENTE
DISPLAY CO-TAB ' > '
T-CLIENTE(CO-TAB) ' / '
T-NOMBRE(CO-TAB) ' / '
T-APELLIDO1(CO-TAB) ' / '
T-APELLIDO2(CO-TAB) ' / '
T-ALTURA(CO-TAB) ' / '
T-PESO(CO-TAB) ' / '
T-IMC(CO-TAB) ' / '
T-OBSERVACIONES(CO-TAB) ' / '
T-FECHA(CO-TAB) ' / '
*
ADD 1 TO CO-TAB.
*
3100-DISPLAY-REGISTRO-EXIT.
EXIT.
Esto sería suficiente para acceder a un fichero DB2 haciendo uso de un proceso de paginación. Con él se
podría obtener un listado de datos a partir de su lectura y mostrar dicha información por pantalla, aunque el
número de registros del DB2 supere al número de ocurrencias de la variable Tabla que los recoge.