En cobol hay que diferenciar entre los programas que acceden a DB2 y los que no, pues se compilarán de maneras diferentes y se ejecutarán de forma diferente.
Enpezaremos por ver el programa sin DB2 más sencillo:
El programa más sencillo es aquel que recibe datos por SYSIN del JCL y los muestra por SYSOUT.
JCL:
//PROG1 EXEC PGM=PRUEBA1
//SYSOUT DD SYSOUT=*
//SYSIN DD *
JOSE LOPEZ VAZQUEZ HUGO CASILLAS DIAZ JAVIER CARBONERO PACO GONZALEZ
/*
donde EXEC PGM= indica el programa SIN DB2 que vamos a ejecutar
SYSOUT DD SYSOUT=* indica que la información "displayada" se quedará en la cola del SYSOUT (no lo vamos a guardar en un fichero)
en SYSIN DD * metemos la información que va a recibir el programa
Fijaos en las posiciones de los nombres de la SYSIN, para entender bien el programa:
----+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8
JOSE LOPEZ VAZQUEZ HUGO CASILLAS DIAZ JAVIER CARBONERO PACO GONZALEZ
Como veis, la longitud máxima que se puede pasar a un programa a través de la SYSIN es de 80. En nuestro caso la hemos dividido en 4 trozos de 20 posiciones, cada uno con un nombre.
PROGRAMA:
IDENTIFICATION DIVISION.
PROGRAM-ID. PRUEBA1.
*==========================================================*
* PROGRAMA QUE LEE DE SYSIN Y ESCRIBE EN SYSOUT
*==========================================================*
*
ENVIRONMENT DIVISION.
*
CONFIGURATION SECTION.
*
SPECIAL-NAMES.
DECIMAL-POINT IS COMMA.
*
DATA DIVISION.
*
WORKING-STORAGE SECTION.
*
01 WI-INDICE PIC 9(4) COMP.
01 WX-SYSIN PIC X(80).
01 WX-TABLA-NOMBRES.
05 WX-NOMBRE PIC X(20) OCCURS 4 TIMES.
*
************************************************************
PROCEDURE DIVISION.
************************************************************
* | 0000 - PRINCIPAL
*--|------------------+----------><----------+-------------*
* 1| EJECUTA EL INICIO DEL PROGRAMA
* 2| EJECUTA EL PROCESO DEL PROGRAMA
* 3| EJECUTA EL FINAL DEL PROGRAMA
************************************************************
00000-PRINCIPAL.
*
PERFORM 10000-INICIO
*
PERFORM 20000-PROCESO
*
PERFORM 30000-FINAL
.
************************************************************
* | 10000 - INICIO
*--|------------+----------><----------+-------------------*
* | SE REALIZA EL TRATAMIENTO DE INICIO:
* 1| Inicialización de Áreas de Trabajo
* 2| Primera lectura de SYSIN
************************************************************
10000-INICIO.
*
INITIALIZE WX-SYSIN
ACCEPT WX-SYSIN FROM SYSIN
.
************************************************************
* | 20000 - PROCESO
*--|------------------+----------><------------------------*
* | SE REALIZA EL TRATAMIENTO DE LOS DATOS:
* 1| Realiza el tratamiento de cada registro leido
************************************************************
20000-PROCESO.
*
MOVE WX-SYSIN TO WX-TABLA-NOMBRES
MOVE 1 TO WI-INDICE
PERFORM UNTIL WI-INDICE GREATER 4
DISPLAY 'WX-NOMBRE:'WX-NOMBRE(WI-INDICE)
ADD 1 TO WI-INDICE
END-PERFORM
.
************************************************************
* | 30000 - FINAL
*--|------------------+----------><----------+-------------*
* | FINALIZA LA EJECUCION DEL PROGRAMA
************************************************************
30000-FINAL.
*
STOP RUN
.
En el programa podemos ver las siguientes divisiones/secciones:
IDENTIFICATION DIVISION: existirá siempre.
ENVIRONMENT DIVISION: existirá siempre.
CONFIGURATION SECTION: existirá siempre.
En este caso no existirá la INPUT-OUTPUT SECTION, pues nuestro programa no utiliza ficheros.
DATA DIVISION: existirá siempre.
En este caso no existirá la FILE SECTION, pues nuestro programa no utiliza ficheros.
WORKING-STORAGE SECTION: exisitirá siempre.
En este caso no exisistirá la LINKAGE SECTION pues el programa no se comunica con otros programas.
PROCEDURE DIVISION: exisitirá siempre.
En el programa podemos ver las siguientes sentencias:
PERFORM: llamada a párrafo
INITIALIZE: para inicializar variable
ACCEPT: esta sentencia recoge la información del campo indicado en el "FROM". En este caso recoge la información almacenada en "SYSIN"; la que nosotros hemos introducido en el JCL.
MOVE/TO: movemos la información de un campo a otro
PERFORM UNTIL: bucle
DISPLAY: escribe el contenido del campo indicado en la SYSOUT del JCL.
ADD:Operador de adición (suma)
STOP RUN: sentencia de finalización de ejecución.
Descripción del programa:
En el párrafo de inicio, inicializamos WX-SYSIN para guardar posteriormente mediante un ACCEPT la información que hemos escrito en la SYSIN del JCL.
En el párrafo de proceso, informamos la tabla interna WX-TABLA-NOMBRES, donde el campo WX-NOMBRES se repetirá 4 veces (occurs 4 times).
Informamos el campo WI-INDICE con un 1, pues vamos a utilizar los campos de la tabla interna:
Para utilizar un campo que pertenezca a una tabla interna (tiene occurs), debemos acompañar el campo de un "índice" entre paréntesis. De tal forma que indiquemos a que "ocurrencia" de la tabla nos estamos refiriendo. Por ejemplo, WX-NOMBRE(1) sería el primer nombre guardado (JOSE LOPEZ VAZQUEZ).
Como queremos displayar todas las ocurrencias de la tabla, haremos que el índice sea una variable que va aumentando.
A continuación montamos un bucle (perform until) con la condición WI-INDICE mayor (greater) de 4, pues la primera vez WI-INDICE valdrá 1, y necesitamos que el bucle se repita 4 veces:
WI-INDICE = 1: WX-NOMBRE(1) = JOSE LOPEZ VAZQUEZ
WI-INDICE = 2: WX-NOMBRE(2) = HUGO CASILLAS DIAZ
WI-INDICE = 3: WX-NOMBRE(3) = JAVIER CARBONERO
WI-INDICE = 4: WX-NOMBRE(4) = PACO GONZALEZ
WI-INDICE = 5: salimos del bucle porque se cumple WI-INDICE GREATER 4
NOTA: el índice de una tabla interna NUNCA puede ser cero, pues no existe la ocurrencia cero. Si no informásemos WI-INDICE con 1, el DISPLAY de WX-NOMBRE(0) nos daría un estupendo casque, de este estilo:
IGZ0006S The reference to table WX-NOMBRE by verb number 01 on line 001099 addressed an area outside the region of the table.
From compile unit R2BCTAN1 at entry point R2BCTAN1 at compile unit offset +00001122 at entry offset +00001122 at address 1965BBAA.
Aunque ahora ya sabemos cómo encontrar la línea que nos da el OFFSET :D
RESULTADO:
WX-NOMBRE: JOSE LOPEZ VAZQUEZ
WX-NOMBRE: HUGO CASILLAS DIAZ
WX-NOMBRE: JAVIER CARBONERO
WX-NOMBRE: PACO GONZALEZ
Mostrando entradas con la etiqueta DISPLAY. Mostrar todas las entradas
Mostrando entradas con la etiqueta DISPLAY. Mostrar todas las entradas
miércoles, 9 de febrero de 2011
miércoles, 19 de enero de 2011
ICETOOL(II): REPORT; informes.
Continuando con la serie de artículos sobre ICETOOL, en esta ocasión vamos a presentar un artículo de como a partir de un fichero de entrada podemos presentar sus datos mediante un REPORT, con cabecera, roturas, detalle (en columnas), y totales.
Report de un Fichero usando el comando DISPLAY.
Fichero RECS:
----+----1----+----2--
JCL que realiza el cruce:
//PASO010 EXEC PGM=ICETOOL
//TOOLMSG DD SYSOUT=*
//DFSMSG DD SYSOUT=*
//TOOLIN DD *
SORT FROM(RECS) TO(TMP) USING(CNTL1)
DISPLAY FROM(TMP) LIST(REPORT) -
DATE TITLE('Facturacion XXXXXXX de la empresa') PAGE -
BTITLE('Factura:') BREAK(1,10,CH) -
HEADER('Documento') ON(12,10,CH) -
HEADER('Importe') ON(22,3,ZD) -
BTOTAL('Total x Factura:') -
BMINIMUM('Minimo x Factura:') -
BMAXIMUM('Maximo x Factura:') -
BAVERAGE('Media x Factura:') -
BLANK
//RECS DD DISP=SHR,
// DSN=USER.ICETOOL.IN
//TMP DD DSN=&&TEMP,DISP=(MOD,PASS),
// SPACE=(TRK,(10,10)),UNIT=SYSDA
//REPORT DD SYSOUT=*
//CNTL1CNTL DD *
SORT FIELDS=(1,10,CH,A,12,9,CH,A)
/*
Para hacer un informe a partir de un fichero usaremos la sentencia DISPLAY con varios complementos que iremos viendo.
En este informe utilizamos una ruptura por el campo "Factura", por lo que será importante ordenar el fichero por ese campo, para poder ver la información agrupada por Factura.
Para este paso previo usamos la sentencia SORT con la sentencia USING (en este caso sólo hemos hecho un SORT FIELDS pero podría acompañarse de otras sentencias como INCLUDE COND para seleccionar un determinado número de registros):
SORT FROM(RECS) TO(TMP) USING(CNTL1)
...
//CNTL1CNTL DD *
SORT FIELDS=(1,10,CH,A,12,9,CH,A)
/*
Para escribir el informe hacemos varios pasos:
Mostramos el informe a partir del fichero temporal TMP (que está ordenado para nuestro propósito).
DISPLAY FROM(TMP) LIST(REPORT) -
Podemos tanto enviarlo a la cola de ejecución, como a un fichero. En [//REPORT DD SYSOUT=*] especificamos el tipo de salida que queramos.
Mostramos la cabecera del informe.
DATE TITLE('Facturación XXXXXXX de la empresa') PAGE -
En este caso se va componer por:
Mostramos el detalle del informe.
BTITLE('Factura:') BREAK(1,10,CH) -
HEADER('Documento') ON(12,10,CH) -
HEADER('Importe') ON(22,3,ZD) -
En este caso se va a componer por:
Mostramos los totales por factura (total, mínimo, máximo y media de los importes de detalle por cada factura).
BTOTAL('Total x Factura:') -
BMINIMUM('Minimo x Factura:') -
BMAXIMUM('Maximo x Factura:') -
BAVERAGE('Media x Factura:') -
Finalmente tenemos la sentencia BLANK cuya función es eliminar del informe espacios en blanco (es opcional).
BLANK
Esta es una manera sencilla de presentación de los datos de un fichero.
Se podría ampliar con más cosas como totales generales, formatos de campos, etc.
Report de un Fichero usando el comando DISPLAY.
Fichero RECS:
----+----1----+----2--
| 1111000021 | CCCCCCCCC | 010 |
| 1111000021 | CCCCCCCCC | 090 |
| 1111000021 | QQQQQQQQQ | 020 |
| 1111000022 | VVVVVVVVV | 050 |
JCL que realiza el cruce:
//PASO010 EXEC PGM=ICETOOL
//TOOLMSG DD SYSOUT=*
//DFSMSG DD SYSOUT=*
//TOOLIN DD *
SORT FROM(RECS) TO(TMP) USING(CNTL1)
DISPLAY FROM(TMP) LIST(REPORT) -
DATE TITLE('Facturacion XXXXXXX de la empresa') PAGE -
BTITLE('Factura:') BREAK(1,10,CH) -
HEADER('Documento') ON(12,10,CH) -
HEADER('Importe') ON(22,3,ZD) -
BTOTAL('Total x Factura:') -
BMINIMUM('Minimo x Factura:') -
BMAXIMUM('Maximo x Factura:') -
BAVERAGE('Media x Factura:') -
BLANK
//RECS DD DISP=SHR,
// DSN=USER.ICETOOL.IN
//TMP DD DSN=&&TEMP,DISP=(MOD,PASS),
// SPACE=(TRK,(10,10)),UNIT=SYSDA
//REPORT DD SYSOUT=*
//CNTL1CNTL DD *
SORT FIELDS=(1,10,CH,A,12,9,CH,A)
/*
Para hacer un informe a partir de un fichero usaremos la sentencia DISPLAY con varios complementos que iremos viendo.
En este informe utilizamos una ruptura por el campo "Factura", por lo que será importante ordenar el fichero por ese campo, para poder ver la información agrupada por Factura.
Para este paso previo usamos la sentencia SORT con la sentencia USING (en este caso sólo hemos hecho un SORT FIELDS pero podría acompañarse de otras sentencias como INCLUDE COND para seleccionar un determinado número de registros):
SORT FROM(RECS) TO(TMP) USING(CNTL1)
...
//CNTL1CNTL DD *
SORT FIELDS=(1,10,CH,A,12,9,CH,A)
/*
Para escribir el informe hacemos varios pasos:
Mostramos el informe a partir del fichero temporal TMP (que está ordenado para nuestro propósito).
DISPLAY FROM(TMP) LIST(REPORT) -
Podemos tanto enviarlo a la cola de ejecución, como a un fichero. En [//REPORT DD SYSOUT=*] especificamos el tipo de salida que queramos.
Mostramos la cabecera del informe.
DATE TITLE('Facturación XXXXXXX de la empresa') PAGE -
En este caso se va componer por:
- La fecha (a la izquierda).
- El título (en el centro).
- El número de página (a la derecha).
Mostramos el detalle del informe.
BTITLE('Factura:') BREAK(1,10,CH) -
HEADER('Documento') ON(12,10,CH) -
HEADER('Importe') ON(22,3,ZD) -
En este caso se va a componer por:
- Una ruptura (código de factura BREAK(posición incial, longitud, tipo).
- Por cada factura dos columnas de detalle:
- Documento (ON(posicion inicial, longitud, tipo).
- Importe.
Mostramos los totales por factura (total, mínimo, máximo y media de los importes de detalle por cada factura).
BTOTAL('Total x Factura:') -
BMINIMUM('Minimo x Factura:') -
BMAXIMUM('Maximo x Factura:') -
BAVERAGE('Media x Factura:') -
Finalmente tenemos la sentencia BLANK cuya función es eliminar del informe espacios en blanco (es opcional).
BLANK
Esta es una manera sencilla de presentación de los datos de un fichero.
Se podría ampliar con más cosas como totales generales, formatos de campos, etc.
Suscribirse a:
Comentarios (Atom)