Para este ejemplo crearemos un programa que lea un fichero de entrada, formatee la información y escriba en un fichero de salida.
JCL:
//******************************************************
//******************** BORRADO *************************
//BORRADO EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
DEL FICHERO.DE.SALIDA
SET MAXCC = 0
//******************************************************
//*********** EJECUCION DEL PROGRAMA PRUEBA3 ***********
//PROG3 EXEC PGM=PRUEBA3
//SYSOUT DD SYSOUT=*
//ENTRADA DD DSN=FICHERO.DE.ENTRADA,DISP=SHR
//SALIDA DD DSN=FICHERO.DE.SALIDA,
// DISP=(NEW, CATLG, DELETE),SPACE=(TRK,(50,10))
/*
En este caso volvemos a utilizar el IDCAMS para borrar el fichero de salida que se genera en el segundo paso. Sigue siendo un programa sin DB2, así que utilizamos el EXEC PGM.
Para definir el fichero de entrada "ENTRADA" indicaremos que es un fichero ya existente y compartida al indicar DISP=SHR.
En la SYSOUT veremos los mensajes de error en caso de que los haya.
Fichero de entrada:
----+----1----+
11111AA100A
22222BB100B
33333CC100K
44444DD100M
campo1: número de cliente
campo2: código de empresa
campo3: saldo
PROGRAMA:
IDENTIFICATION DIVISION.
PROGRAM-ID. PRUEBA3.
*==========================================================*
* PROGRAMA QUE LEE DE FICHERO Y ESCRIBE EN FICHERO
*==========================================================*
*
ENVIRONMENT DIVISION.
*
CONFIGURATION SECTION.
*
SPECIAL-NAMES.
DECIMAL-POINT IS COMMA.
*
INPUT-OUTPUT SECTION.
*
FILE-CONTROL.
*
SELECT ENTRADA ASSIGN TO ENTRADA
STATUS IS FS-ENTRADA.
SELECT SALIDA ASSIGN TO SALIDA
STATUS IS FS-SALIDA.
*
DATA DIVISION.
*
FILE SECTION.
*
* Fichero de entrada de longitud fija (F) igual a 11.
FD ENTRADA RECORDING MODE IS F
BLOCK CONTAINS 0 RECORDS
RECORD CONTAINS 11 CHARACTERS.
01 REG-ENTRADA PIC X(11).
*
* Fichero de salida de longitud fija (F) igual a 28.
FD SALIDA RECORDING MODE IS F
BLOCK CONTAINS 0 RECORDS
RECORD CONTAINS 28 CHARACTERS.
01 REG-SALIDA PIC X(28).
*
WORKING-STORAGE SECTION.
* FILE STATUS
01 FS-STATUS.
05 FS-ENTRADA PIC X(2).
88 FS-ENTRADA-OK VALUE '00'.
88 FS-FICHERO1-EOF VALUE '10'.
05 FS-SALIDA PIC X(2).
88 FS-SALIDA-OK VALUE '00'.
*
* VARIABLES
01 WB-FIN-ENTRADA PIC X(1) VALUE 'N'.
88 FIN-ENTRADA VALUE 'S'.
*
01 WB-SIGNOS PIC X.
88 SIGNO-MAS VALUE 'A', 'B',
'C', 'D',
'E', 'F',
'G', 'H',
'I', '{'.
88 SIGNO-MENOS VALUE 'J', 'K',
'L', 'M',
'N', 'O',
'P', 'Q',
'R', '}'.
01 WX-TABLA-EMPRESAS.
05 PIC X(11) VALUE 'AAEMPRESA 1'.
05 PIC X(11) VALUE 'BBEMPRESA 2'.
05 PIC X(11) VALUE 'CCEMPRESA 3'.
05 PIC X(11) VALUE 'DDEMPRESA 4'.
01 REDEFINES WX-TABLA-EMPRESAS.
05 WX-ELEMENTOS OCCURS 4 TIMES
INDEXED BY WI-ELEM.
10 WX-CODIGO-EMPRESA PIC X(2).
10 WX-NOMBRE-EMPRESA PIC X(9).
*
01 WX-REGISTRO-ENTRADA.
05 WX-ENT-CLIENTE PIC 9(5).
05 WX-ENT-COD-EMPRESA PIC X(2).
05 WX-ENT-SALDO PIC S9(4).
*
01 WX-REGISTRO-SALIDA.
05 WX-SAL-CLIENTE PIC 9(5).
05 WX-SAL-COD-EMPRESA PIC X(2).
05 WX-SAL-NOMBRE-EMPRESA PIC X(9).
05 WX-SAL-SALDO PIC 9(4).
05 WX-SAL-SIGNO PIC X(8).
* CONSTANTES
01 WK-POSITIVO PIC X(8) VALUE 'positivo'.
01 WK-NEGATIVO PIC X(8) VALUE 'negativo'.
*
************************************************************
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
UNTIL FIN-ENTRADA
*
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-REGISTRO-SALIDA
PERFORM 11000-ABRIR-FICHERO
PERFORM LEER-ENTRADA
IF FIN-ENTRADA
DISPLAY 'FICHERO DE ENTRADA VACIO'
PERFORM 30000-FINAL
END-IF
.
*
************************************************************
* 11000 - ABRIR FICHEROS
*--|------------------+----------><----------+-------------*
* Abrimos los ficheros del programa
************************************************************
11000-ABRIR-FICHEROS.
*
OPEN INPUT ENTRADA
OUTPUT SALIDA
*
IF NOT FS-ENTRADA-OK
DISPLAY 'ERROR EN OPEN DEL FICHERO DE ENTRADA:'FS-ENTRADA
END-IF
IF NOT FS-SALIDA-OK
DISPLAY 'ERROR EN OPEN DEL FICHERO DE SALIDA:'FS-SALIDA
END-IF
.
*
************************************************************
* | 20000 - PROCESO
*--|------------------+----------><------------------------*
* | SE REALIZA EL TRATAMIENTO DE LOS DATOS:
* 1| Realiza el tratamiento de cada registro recuperado de
* | la ENTRADA
************************************************************
20000-PROCESO.
*
PERFORM 21000-BUSCAR-NOMBRE-EMPRESA
PERFORM 22000-BUSCAR-SIGNO-SALDO
PERFORM 23000-INFORMAR-SALIDA
PERFORM ESCRIBIR-SALIDA
PERFORM LEER-ENTRADA
.
*
************************************************************
* 21000 - BUSCAR NOMBRE EMPRESA
*--|------------------+----------><----------+-------------*
* BUSCAMOS EL CODIGO DE EMPRESA DEL FICHERO DE ENTRADA EN
* NUESTRA TABLA INTERNA PARA RECUPERAR EL NOMBRE
************************************************************
21000-BUSCAR-NOMBRE-EMPRESA.
*
*Ponemos el índice WI-ELEM a 1 y se irá incrementando de 1 en 1
SET WI-ELEM TO 1
*Buscamos en WX-TABLA-EMPRESAS el nombre de empresa que tenga
*el mismo código que el del fichero de entrada.
*Si no lo encuentra, movemos espacios al nombre de la empresa
SEARCH WX-ELEMENTOS
AT END
MOVE SPACES TO WX-SAL-NOMBRE-EMPRESA
WHEN WX-CODIGO-EMPRESA(WI-ELEM) EQUAL WX-ENT-COD-EMPRESA
MOVE WX-NOMBRE-EMPRESA(WI-ELEM)
TO WX-SAL-NOMBRE-EMPRESA
END-SEARCH
.
*
************************************************************
* 22000-BUSCAR-SIGNO-SALDO
*--|------------------+----------><----------+-------------*
* COMPROBAMOS EL SIGNO DEL SALDO E INFORMAMOS EL CAMPO:
* WX-SAL-SIGNO
************************************************************
22000-BUSCAR-SIGNO-SALDO.
*
*El signo viene dado por la última posición. La movemos al
*switch WB-SIGNOS. Según su valor informará positivo o negativo
MOVE WX-ENT-SALDO(4:1) TO WB-SIGNOS
EVALUATE TRUE
WHEN SIGNO-MAS
MOVE WK-POSITIVO TO WX-SAL-SIGNO
WHEN SIGNO-MENOS
MOVE WK-NEGATIVO TO WX-SAL-SIGNO
WHEN OTHER
MOVE SPACES TO WX-SAL-SIGNO
END-EVALUATE
.
*
************************************************************
* 23000-INFORMAR-SALIDA
*--|------------------+----------><----------+-------------*
* INFORMAMOS EL RESTO DE CAMPOS DEL FICHERO DE SALIDA
************************************************************
23000-INFORMAR-SALIDA.
*
MOVE WX-ENT-CLIENTE TO WX-SAL-CLIENTE
MOVE WX-ENT-COD-EMPRESA TO WX-SAL-COD-EMPRESA
MOVE WX-ENT-SALDO TO WX-SAL-SALDO
.
*
************************************************************
* LEER ENTRADA
*--|------------------+----------><----------+-------------*
* Leemos del fichero de entrada
************************************************************
LEER-ENTRADA.
*
READ ENTRADA INTO WX-REGISTRO-ENTRADA
EVALUATE TRUE
WHEN FS-ENTRADA-OK
CONTINUE
WHEN FS-ENTRADA-EOF
SET FIN-ENTRADA TO TRUE
WHEN OTHER
DISPLAY 'ERROR EN READ DE ENTRADA:'FS-ENTRADA
END-EVALUATE
.
*
************************************************************
* - ESCRIBIR SALIDA
*--|------------------+----------><----------+-------------*
* ESCRIBIMOS EN EL FICHERO DE SALIDA LA INFORMACION GUARDADA
* WX-REGISTRO-SALIDA
************************************************************
ESCRIBIR-SALIDA.
*
WRITE REG-SALIDA FROM WX-REGISTRO-SALIDA
IF FS-SALIDA-OK
INITIALIZE WX-REGISTRO-SALIDA
ELSE
DISPLAY 'ERROR EN WRITE DEL FICHERO:'FS-SALIDA
END-IF
.
*
************************************************************
* | 30000 - FINAL
*--|------------------+----------><----------+-------------*
* | FINALIZA LA EJECUCION DEL PROGRAMA
************************************************************
30000-FINAL.
*
PERFORM 31000-CERRAR-FICHEROS
STOP RUN
.
*
************************************************************
* | 31000 - CERRAR FICHEROS
*--|------------------+----------><----------+-------------*
* | CERRAMOS LOS FICHEROS DEL PROGRAMA
************************************************************
31000-CERRAR-FICHEROS.
*
CLOSE ENTRADA
SALIDA
IF NOT FS-ENTRADA-OK
DISPLAY 'ERROR EN CLOSE DE ENTRADA:'FS-ENTRADA
END-IF
IF NOT FS-SALIDA-OK
DISPLAY 'ERROR EN CLOSE DE SALIDA:'FS-SALIDA
END-IF
.
En el programa podemos ver las siguientes divisiones/secciones:
IDENTIFICATION DIVISION: existirá siempre.
ENVIRONMENT DIVISION: existirá siempre.
CONFIGURATION SECTION: existirá siempre.
INPUT-OUTPUT SECTION: en este ejemplo existirá porque utilizamos un fichero de entrada y uno de salida.
DATA DIVISION: existirá siempre.
FILE SECTION: en este ejemplo existirá pues utilizamos un fichero de entrada y uno de salida.
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
OPEN: "Abre" los ficheros del programa. Lo acompañaremos de "INPUT" para los ficheros de entrada y "OUTPUT" para los ficheros de salida.
DISPLAY: escribe el contenido del campo indicado en la SYSOUT del JCL.
MOVE/TO: movemos la información de un campo a otro.
SEARCH: esta sententencia se utiliza para buscar un dato dentro de una tabla interna, recorriéndola usándo un índice y comparando alguno de sus campos con el campo que buscamos.
PERFORM UNTIL: bucle
SET:Activa los niveles 88 de un campo tipo "switch".
READ: Lee cada registro del fichero de entrada. En el "INTO" le indicamos donde debe guardar la información.
WRITE: Escribe la información indicada en el "FROM" en el fichero indicado.
STOP RUN: sentencia de finalización de ejecución.
CLOSE: "Cierra" los ficheros del programa.
Descripción del programa:
En el párrafo de inicio, inicializamos el registro de salida: WX-REGISTRO-SALIDA
Abriremos los ficheros del programa (OPEN INPUT para la enrtada, y OUTPUT para la salida) y controlaremos el file-status. Si todo va bien el código del file-status valdrá '00'. Podéis ver la lista de los file-status más comunes.
Además comprobamos que el fichero de entrada no venga vacío (en caso de que así sea, finalizamos la ejecución).
En el párrafo de proceso, que se repetirá hasta que se termine el fichero de entrada (FIN-ENTRADA), tenemos varias llamadas a párrafos:
21000-BUSCAR-NOMBRE-EMPRESA:
Busca en la taba interna WX-TABLA-EMPRESAS utilizando la sentencia SEARCH.
22000-BUSCAR-SIGNO-SALDO:
La última posición del campo saldo (S9(4)) nos indica el signo. Podéis ver la referencia a los signos en campos numéricos.
Guardaremos esa última posición en el nivel superior de un campo tipo switch (WB-SIGNOS). Si el valor se corresponde con alguno de los indicados en los niveles 88, ese nivel se activará a "TRUE".
En el "EVALUATE TRUE", el programa entrará por la sentencia "WHEN" que esté activa (que sea "TRUE").
23000-INFORMAR-SALIDA:
Informamos el resto de campos.
ESCRIBIR-SALIDA:
Escribimos nuestro registro ya informado en el fichero de salida.
LEER-ENTRADA:
Leemos el siguiente registro del fichero de entrada.
Fichero de salida:
----+----1----+----2----+----3
11111AAEMPRESA 11001positivo
22222BBEMPRESA 21002positivo
33333CCEMPRESA 31002negativo
44444DDEMPRESA 41004negativo
campo1: número de cliente
campo2: código de empresa
campo3: nombre de empresa
campo4: saldo
campo5: signo del saldo
En este programa además de ver como crear un PROCESO que trate todos los registros de un fichero de entrada, hemos visto varias sentencias como el EVALUATE y el SEARCH. Así aprovechamos para ir introduciendo más sentencias útiles del COBOL en forma de ejemplos.
Y si os queda cualquier duda, estamos aquí para resolverlas : )
lunes, 28 de febrero de 2011
sábado, 26 de febrero de 2011
Lo más leído: lunes 21 a viernes 25 de Febrero.
Según el señor Google Analytics, estas son las 10 páginas más consultadas durante esta semana:
Hay que decir que los artículos de errores son siempre lo más consultado, y si los descontamos, aparecerían en los puestos 8, 9 y 10:
Y hasta aquí la semana. Volvemos el lunes con más, pero no mejor, porque es imposible. Aquí, en el Consultorio Cobol.
: )
P.D.No dejéis de leer las viñetas de La abuela Manuela!!
- Errores de JCL.
- Errores DB2.
- Programas con DB2 I: SELECT, INSERT, UPDATE y DELETE.
- JCL básico:IDCAMS, IEFBR14, IEBGENER.
- Programas con DB2 III: COUNT, MAX y FOR UPDATE.
- Errores de ficheros.
- SQL en JCL.
- Sort vol.5: SUM.
- Programas con DB2 II: CURSOR.
- XML PARSE: de XML a fichero plano con COBOL.
Hay que decir que los artículos de errores son siempre lo más consultado, y si los descontamos, aparecerían en los puestos 8, 9 y 10:
Y hasta aquí la semana. Volvemos el lunes con más, pero no mejor, porque es imposible. Aquí, en el Consultorio Cobol.
: )
P.D.No dejéis de leer las viñetas de La abuela Manuela!!
viernes, 25 de febrero de 2011
BBVA + HP = CPD de máxima seguridad

Esta tolerancia ante posibles fallos se consigue con múltiples vías de distribución, que consiguen que las labores de mantenimiento en una de las vías no afecten al resto y asegurar así el funcionamiento continuado de la red de oficinas.
Otro detalle a destacar es la flexibilidad de la infraestructura, ya que está preparada para una posible ampliación futura de 500 metros cuadrados de superficie.
Según estimaciones, este nuevo CPD garantiza un tiempo máximo de interrupción del servicio de 52,5 minutos al año.
El centro es el primero de España que recibe la certificación TIER IV del Uptime Institute. Esta certificación establece y promueve los conocimientos y exigencias necesarios que debe cumplir un CPD para garantizar la disponibilidad y continuidad del mismo. El TIER IV es el máximo nivel definido, y algunas de las características que debe cumplir una infraestructura de estas características son los siguientes :
- Componentes redundantes
- Multiples vías de distribución activas y redundantes
- No susceptible a interrupciones por un evento no planeado
- Se puede realizar un mantenimiento de los componentes hardware durante un evento planeado sin generar interrupciones en el sistema
La disponibilidad de estos centros tendría que ser igual o superior al 99.995% y el tiempo de caidas anuales debería ser inferior a 0.8 hrs.
Habrá que seguir de cerca los resultados de este nuevo CPD, pero es una gran noticia que centros de tales características sean la apuesta de futuro de las entidades financieras, y posiblemente futuros lugares de trabajo de profesionales host (en este caso quizás más enfocado a gente de sistemas).
Noticia Networkworld
miércoles, 23 de febrero de 2011
Natural/ADABAS para coboleros: parte I.
¿Qué es Natural? Es un lenguaje de programación de la empresa Software AG que también se utiliza en entornos mainframe. La base de datos que lo acompaña es ADABAS. Del mismo modo que Natural podría tener similitudes con COBOL, ADABAS no las tiene en absoluto con DB2.
Hoy día no tengo conocimiento de que quede algún banco en España que lo utilice, pues el único que conocí de primera mano lo estaba migrando a COBOL. Creo que telefónica todavía tenía una parte en Natural/ADABAS...
En este artículo nos haremos una idea de cómo codificar en Natural asociándolo a un programa COBOL. Tomaremos como ejemplo el "Ejemplo 1" del manual de COBOL para principiantes.
Vamos a construir un programa que recogerá datos de la SYSIN de un JCL, y displayará la inforamción contenida en él.
JCL:
//**************EJECUCION NATURAL*******************
//**************************************************
//PROG1 EXEC NATBAT
//CMPRINT DD SYSOUT=*
//CMSYNIN DD *
LOGON SYSEXBAT
EXECUTE PGMNAT1
FIN
//SYSIN DD *
JOSE LOPEZ VAZQUEZ %
HUGO CASILLAS DIAZ %
JAVIER CARBONERO %
PACO GONZALEZ
/*
Donde EXEC NATBAT indica que vamos a ejecutar un programa natural en modo batch(puede cambiar según instalación).
CMPRINT DD SYSOUT=* indica que la información "displayada" se quedará en la cola CMPRINT (no lo vamos a guardar en un fichero).
En CMSYNIN DD * hace logon a la librería e indicamos el programa q vamos a ejecutar (PGMNAT1).
En SYSIN incluimos la información que va a recibir el programa. El '%' indica que la información continúa en la siguiente línea.
Programa:
DEFINE DATA LOCAL
1 WI-INDICE(P4)
1 #WX-NOMBRE1(A20)
1 #WX-NOMBRE2(A20)
1 #WX-NOMBRE3(A20)
1 #WX-NOMBRE4(A20)
1 WX-TABLA-NOMBRES
2 WX-NOMBRE(A20/4)
END-DEFINE
INPUT #WX-NOMBRE1 #WX-NOMBRE2 #WX-NOMBRE3 #WX-NOMBRE4
MOVE #WX-NOMBRE1 TO WX-NOMBRE(1)
MOVE #WX-NOMBRE2 TO WX-NOMBRE(2)
MOVE #WX-NOMBRE3 TO WX-NOMBRE(3)
MOVE #WX-NOMBRE4 TO WX-NOMBRE(4)
FOR WI-INDICE = 1 TO 4
WRITE 'WX-NOMBRE:'WX-NOMBRE(WI-INDICE)
END
Donde el área "DEFINE DATA LOCAL" equivaldría a nuestra WORKING-STORAGE; ahí definiríamos las variables/copys que necesitásemos para el programa. En nuestro caso:
WI-INDICE: empaquetado(P) de 4 posiciones. Equivaldría a un 9(4) COMP-3. Los índices para bucles en natural deben ser siempre numéricos (N) o empaquetados (P).
#WX-NOMBRE1: alfanumérico (A) de 20. Equivaldría a nuestro PIC X(20). La almohadilla indica que es una variable que se recibe por INPUT.
WX-TABLA-NOMBRES: sería una tabla interna donde el campo WX-NOMBRE es un alfanumérico (A) de 20 y se repite 4 veces. Equivaldría a nuestro OCCURS 4 TIMES.
A continuación codificaríamos el "PROCESO" del programa. En nuestro caso:
INPUT: recoge los datos introducidos por SYSIN.
MOVE/TO: para informar variables existen otras opciones:
WX-NOMBRE(1) := #WX-NOMBRE1 ó
ASSIGN WX-NOMBRE(1) = #WX-NOMBRE1
FOR: es uno de los bucles de natural. Las sentencias se repetirán "PARA" WI-INDICE = 1, =2, =3 e =4.
WRITE: escribe en el CMPRINT, que equivaldría a nuestra SYSOUT. La sentencia WRITE equivaldría a nuestro DISPLAY. En natural también existe la sentencia DISPLAY. La diferencia con el WRITE está en que el DISPLAY da formato a los datos, crea columnas con cabeceras por ejemplo.
El resultado usando WRITE sería:
WX-NOMBRE: JOSE LOPEZ VAZQUEZ
WX-NOMBRE: HUGO CASILLAS DIAZ
WX-NOMBRE: JAVIER CARBONERO
WX-NOMBRE: PACO GONZALEZ
Y usando DISPLAY sería:
WX-NOMBRE:
--------------------
JOSE LOPEZ VAZQUEZ
HUGO CASILLAS DIAZ
JAVIER CARBONERO
PACO GONZALEZ
Esto es un ejemplo muuuy sencillo, lo he probado con la versión de demo que se puede descargar desde la página de Software AG. Eso sí, sólo el código que los JCLs no se puede.
Para la próxima veremos el ejemplo de acceso a ADABAS más sencillo, y así tener una idea de como funciona esta base de datos : )
Hoy día no tengo conocimiento de que quede algún banco en España que lo utilice, pues el único que conocí de primera mano lo estaba migrando a COBOL. Creo que telefónica todavía tenía una parte en Natural/ADABAS...
En este artículo nos haremos una idea de cómo codificar en Natural asociándolo a un programa COBOL. Tomaremos como ejemplo el "Ejemplo 1" del manual de COBOL para principiantes.
Vamos a construir un programa que recogerá datos de la SYSIN de un JCL, y displayará la inforamción contenida en él.
JCL:
//**************EJECUCION NATURAL*******************
//**************************************************
//PROG1 EXEC NATBAT
//CMPRINT DD SYSOUT=*
//CMSYNIN DD *
LOGON SYSEXBAT
EXECUTE PGMNAT1
FIN
//SYSIN DD *
JOSE LOPEZ VAZQUEZ %
HUGO CASILLAS DIAZ %
JAVIER CARBONERO %
PACO GONZALEZ
/*
Donde EXEC NATBAT indica que vamos a ejecutar un programa natural en modo batch(puede cambiar según instalación).
CMPRINT DD SYSOUT=* indica que la información "displayada" se quedará en la cola CMPRINT (no lo vamos a guardar en un fichero).
En CMSYNIN DD * hace logon a la librería e indicamos el programa q vamos a ejecutar (PGMNAT1).
En SYSIN incluimos la información que va a recibir el programa. El '%' indica que la información continúa en la siguiente línea.
Programa:
DEFINE DATA LOCAL
1 WI-INDICE(P4)
1 #WX-NOMBRE1(A20)
1 #WX-NOMBRE2(A20)
1 #WX-NOMBRE3(A20)
1 #WX-NOMBRE4(A20)
1 WX-TABLA-NOMBRES
2 WX-NOMBRE(A20/4)
END-DEFINE
INPUT #WX-NOMBRE1 #WX-NOMBRE2 #WX-NOMBRE3 #WX-NOMBRE4
MOVE #WX-NOMBRE1 TO WX-NOMBRE(1)
MOVE #WX-NOMBRE2 TO WX-NOMBRE(2)
MOVE #WX-NOMBRE3 TO WX-NOMBRE(3)
MOVE #WX-NOMBRE4 TO WX-NOMBRE(4)
FOR WI-INDICE = 1 TO 4
WRITE 'WX-NOMBRE:'WX-NOMBRE(WI-INDICE)
END
Donde el área "DEFINE DATA LOCAL" equivaldría a nuestra WORKING-STORAGE; ahí definiríamos las variables/copys que necesitásemos para el programa. En nuestro caso:
WI-INDICE: empaquetado(P) de 4 posiciones. Equivaldría a un 9(4) COMP-3. Los índices para bucles en natural deben ser siempre numéricos (N) o empaquetados (P).
#WX-NOMBRE1: alfanumérico (A) de 20. Equivaldría a nuestro PIC X(20). La almohadilla indica que es una variable que se recibe por INPUT.
WX-TABLA-NOMBRES: sería una tabla interna donde el campo WX-NOMBRE es un alfanumérico (A) de 20 y se repite 4 veces. Equivaldría a nuestro OCCURS 4 TIMES.
A continuación codificaríamos el "PROCESO" del programa. En nuestro caso:
INPUT: recoge los datos introducidos por SYSIN.
MOVE/TO: para informar variables existen otras opciones:
WX-NOMBRE(1) := #WX-NOMBRE1 ó
ASSIGN WX-NOMBRE(1) = #WX-NOMBRE1
FOR: es uno de los bucles de natural. Las sentencias se repetirán "PARA" WI-INDICE = 1, =2, =3 e =4.
WRITE: escribe en el CMPRINT, que equivaldría a nuestra SYSOUT. La sentencia WRITE equivaldría a nuestro DISPLAY. En natural también existe la sentencia DISPLAY. La diferencia con el WRITE está en que el DISPLAY da formato a los datos, crea columnas con cabeceras por ejemplo.
El resultado usando WRITE sería:
WX-NOMBRE: JOSE LOPEZ VAZQUEZ
WX-NOMBRE: HUGO CASILLAS DIAZ
WX-NOMBRE: JAVIER CARBONERO
WX-NOMBRE: PACO GONZALEZ
Y usando DISPLAY sería:
WX-NOMBRE:
--------------------
JOSE LOPEZ VAZQUEZ
HUGO CASILLAS DIAZ
JAVIER CARBONERO
PACO GONZALEZ
Esto es un ejemplo muuuy sencillo, lo he probado con la versión de demo que se puede descargar desde la página de Software AG. Eso sí, sólo el código que los JCLs no se puede.
Para la próxima veremos el ejemplo de acceso a ADABAS más sencillo, y así tener una idea de como funciona esta base de datos : )
lunes, 21 de febrero de 2011
Ejemplo 2: leer de SYSIN y escribir en fichero.
En este ejemplo vamos a ejecutar un programa SIN DB2, que recoge información de la SYSIN del JCL del mismo modo que vimos en el ejemplo 1. La diferencia estará en que en esta ocasión vamos a escribir la información en un fichero en lugar de en la SYSOUT.
JCL:
//******************************************************
//**************************** BORRADO *****************
//BORRADO EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
DEL FICHERO.CON.NOMBRES
SET MAXCC = 0
//******************************************************
//*********** EJECUCION DEL PROGRAMA PRUEBA2 ***********
//PROG1 EXEC PGM=PRUEBA2
//SYSOUT DD SYSOUT=*
//FICHERO DD DSN=FICHERO.CON.NOMBRES,
// DISP=(NEW, CATLG, DELETE),
// SPACE=(TRK,(50,10))
//SYSIN DD *
JOSE LOPEZ VAZQUEZ HUGO CASILLAS DIAZ JAVIER CARBONERO PACO GONZALEZ
/*
En este ejemplo añadimos un nuevo paso, el IDCAMS. Este programa sirve para borrar ficheros que se crean a lo largo del JCL. En nuestro caso borrará el fichero que se va a crear en el programa. Así podremos ejecutarlo tantas veces como queramos sin generar el mismo fichero una y otra vez.
El paso de ejecución del programa PRUEBA2 es similar al del ejemplo 1, sólo le hemos añadido la definición del fichero de salida "FICHERO".
PROGRAMA:
IDENTIFICATION DIVISION.
PROGRAM-ID. PRUEBA2.
*==========================================================*
* PROGRAMA QUE LEE DE SYSIN Y ESCRIBE EN FICHERO
*==========================================================*
*
ENVIRONMENT DIVISION.
*
CONFIGURATION SECTION.
*
SPECIAL-NAMES.
DECIMAL-POINT IS COMMA.
*
INPUT-OUTPUT SECTION.
*
FILE-CONTROL.
*
SELECT FICHERO ASSIGN TO FICHERO
STATUS IS FS-FICHERO.
*
DATA DIVISION.
*
FILE SECTION.
*
* Fichero de salida de longitud fija (F) igual a 20.
FD FICHERO RECORDING MODE IS F
BLOCK CONTAINS 0 RECORDS
RECORD CONTAINS 20 CHARACTERS.
01 REG-FICHERO PIC X(20).
*
WORKING-STORAGE SECTION.
* FILE STATUS
01 FS-STATUS.
05 FS-FICHERO PIC X(2).
88 FS-FICHERO-OK VALUE '00'.
*
* VARIABLES
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.
01 WX-REGISTRO-SALIDA PIC X(20).
*
************************************************************
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
WX-REGISTRO-SALIDA
WX-TABLA-NOMBRES
ACCEPT WX-SYSIN FROM SYSIN
PERFORM 11000-ABRIR-FICHERO
.
*
************************************************************
* 11000 - ABRIR FICHEROS
*--|------------------+----------><----------+-------------*
* Abrimos el fichero de salida
************************************************************
11000-ABRIR-FICHERO.
*
OPEN OUTPUT FICHERO
*
IF NOT FS-FICHERO-OK
DISPLAY 'ERROR EN OPEN DEL FICHERO:'FS-FICHERO
END-IF
.
*
************************************************************
* | 20000 - PROCESO
*--|------------------+----------><------------------------*
* | SE REALIZA EL TRATAMIENTO DE LOS DATOS:
* 1| Realiza el tratamiento de cada registro recuperado de
* la SYSIN
************************************************************
20000-PROCESO.
*
MOVE WX-SYSIN TO WX-TABLA-NOMBRES
MOVE 1 TO WI-INDICE
PERFORM UNTIL WI-INDICE GREATER 4
PERFORM 21000-INFORMAR-REGISTRO
PERFORM 22000-ESCRIBIR-FICHERO
ADD 1 TO WI-INDICE
END-PERFORM
.
*
************************************************************
* 21000 - INFORMAR REGISTRO
*--|------------------+----------><----------+-------------*
* MOVEMOS LA INFORMACION DESDE NUESTRA TABLA INTERNA A LA
* VARIABLE WX-REGISTRO-SALIDA
************************************************************
21000-INFORMAR-REGISTRO.
*
MOVE WX-NOMBRE(WI-INDICE) TO WX-REGISTRO-SALIDA
.
*
************************************************************
* 22000 - ESCRIBIR FICHERO
*--|------------------+----------><----------+-------------*
* ESCRIBIMOS EN EL FICHERO DE SALIDA LA INFORMACION GUARDADA
* WX-REGISTRO-SALIDA
************************************************************
22000-ESCRIBIR-FICHERO.
*
WRITE REG-FICHERO FROM WX-REGISTRO-SALIDA
IF NOT FS-FICHERO-OK
DISPLAY 'ERROR EN WRITE DEL FICHERO:'FS-FICHERO
END-IF
.
*
************************************************************
* | 30000 - FINAL
*--|------------------+----------><----------+-------------*
* | FINALIZA LA EJECUCION DEL PROGRAMA
************************************************************
30000-FINAL.
*
PERFORM 31000-CERRAR-FICHERO
STOP RUN
.
*
************************************************************
* | 31000 - CERRAR FICHERO
*--|------------------+----------><----------+-------------*
* | CERRAMOS EL FICHERO DE SALIDA
************************************************************
31000-CERRAR-FICHERO.
*
CLOSE FICHERO
IF NOT FS-FICHERO-OK
DISPLAY 'ERROR EN CLOSE DEL FICHERO:'FS-FICHERO
END-IF
.
En el programa podemos ver las siguientes divisiones/secciones:
IDENTIFICATION DIVISION: existirá siempre.
ENVIRONMENT DIVISION: existirá siempre.
CONFIGURATION SECTION: existirá siempre.
INPUT-OUTPUT SECTION: en este ejemplo existirá porque utilizamos un fichero de salida.
DATA DIVISION: existirá siempre.
FILE SECTION: en este ejemplo existirá pues utilizamos un fichero de salida.
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.
OPEN: "Abre" los ficheros del programa. Lo acompañaremos de "INPUT" para los ficheros de entrada y "OUTPUT" para los ficheros de salida.
DISPLAY: escribe el contenido del campo indicado en la SYSOUT del JCL.
MOVE/TO: movemos la información de un campo a otro
PERFORM UNTIL: bucle
ADD:Operador de adición (suma)
WRITE: Escribe la información indicada en el "FROM" en el fichero indicado.
STOP RUN: sentencia de finalización de ejecución.
CLOSE: "Cierra" los ficheros del programa.
Descripción del programa:
En el párrafo de inicio, inicializamos las variables que utilizaremos a lo largo del programa:
WX-SYSIN: donde guardaremos posteriormente mediante un ACCEPT la información que hemos escrito en la SYSIN del JCL.
WX-TABLA-NOMBRES: donde moveremos la información de la SYSIN para dividirla en los diferentes nombres.
WX-REGISTRO-SALIDA: donde guardaremos la información que vamos a escribir en el fichero de salida.
Abriremos el fichero de salida (OPEN OUTPUT) y controlaremos el file-status. Si todo va bien el código del file-status valdrá '00'. Podéis ver la lista de los file-status más comunes.
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). Recordad que el tratamiento de las tablas internas está explicado en detalle en el ejemplo 1.
Montaremos un bucle para escribir cada uno de los nombres guardados en la tabla interna en el fichero de salida.
Informaremos la variable WX-REGISTRO-SALIDA con cada uno de los nombres de la tabla.
Escribiremos el fichero de salida. Para ellos indicaremos a continuación del WRITE la variable definida en la FILE-SECTION, y a continuación del FROM la variable donde tenemos guardada la información que queremos escribir.
Controlaremos el file-status.
Nota: es importante controlar el FILE-STATUS en los accesos a ficheros (OPEN, READ, WRITE, CLOSE...) para, en caso de fallo en la ejecución, tener información sobre la causa del error.
JCL:
//******************************************************
//**************************** BORRADO *****************
//BORRADO EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
DEL FICHERO.CON.NOMBRES
SET MAXCC = 0
//******************************************************
//*********** EJECUCION DEL PROGRAMA PRUEBA2 ***********
//PROG1 EXEC PGM=PRUEBA2
//SYSOUT DD SYSOUT=*
//FICHERO DD DSN=FICHERO.CON.NOMBRES,
// DISP=(NEW, CATLG, DELETE),
// SPACE=(TRK,(50,10))
//SYSIN DD *
JOSE LOPEZ VAZQUEZ HUGO CASILLAS DIAZ JAVIER CARBONERO PACO GONZALEZ
/*
En este ejemplo añadimos un nuevo paso, el IDCAMS. Este programa sirve para borrar ficheros que se crean a lo largo del JCL. En nuestro caso borrará el fichero que se va a crear en el programa. Así podremos ejecutarlo tantas veces como queramos sin generar el mismo fichero una y otra vez.
El paso de ejecución del programa PRUEBA2 es similar al del ejemplo 1, sólo le hemos añadido la definición del fichero de salida "FICHERO".
PROGRAMA:
IDENTIFICATION DIVISION.
PROGRAM-ID. PRUEBA2.
*==========================================================*
* PROGRAMA QUE LEE DE SYSIN Y ESCRIBE EN FICHERO
*==========================================================*
*
ENVIRONMENT DIVISION.
*
CONFIGURATION SECTION.
*
SPECIAL-NAMES.
DECIMAL-POINT IS COMMA.
*
INPUT-OUTPUT SECTION.
*
FILE-CONTROL.
*
SELECT FICHERO ASSIGN TO FICHERO
STATUS IS FS-FICHERO.
*
DATA DIVISION.
*
FILE SECTION.
*
* Fichero de salida de longitud fija (F) igual a 20.
FD FICHERO RECORDING MODE IS F
BLOCK CONTAINS 0 RECORDS
RECORD CONTAINS 20 CHARACTERS.
01 REG-FICHERO PIC X(20).
*
WORKING-STORAGE SECTION.
* FILE STATUS
01 FS-STATUS.
05 FS-FICHERO PIC X(2).
88 FS-FICHERO-OK VALUE '00'.
*
* VARIABLES
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.
01 WX-REGISTRO-SALIDA PIC X(20).
*
************************************************************
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
WX-REGISTRO-SALIDA
WX-TABLA-NOMBRES
ACCEPT WX-SYSIN FROM SYSIN
PERFORM 11000-ABRIR-FICHERO
.
*
************************************************************
* 11000 - ABRIR FICHEROS
*--|------------------+----------><----------+-------------*
* Abrimos el fichero de salida
************************************************************
11000-ABRIR-FICHERO.
*
OPEN OUTPUT FICHERO
*
IF NOT FS-FICHERO-OK
DISPLAY 'ERROR EN OPEN DEL FICHERO:'FS-FICHERO
END-IF
.
*
************************************************************
* | 20000 - PROCESO
*--|------------------+----------><------------------------*
* | SE REALIZA EL TRATAMIENTO DE LOS DATOS:
* 1| Realiza el tratamiento de cada registro recuperado de
* la SYSIN
************************************************************
20000-PROCESO.
*
MOVE WX-SYSIN TO WX-TABLA-NOMBRES
MOVE 1 TO WI-INDICE
PERFORM UNTIL WI-INDICE GREATER 4
PERFORM 21000-INFORMAR-REGISTRO
PERFORM 22000-ESCRIBIR-FICHERO
ADD 1 TO WI-INDICE
END-PERFORM
.
*
************************************************************
* 21000 - INFORMAR REGISTRO
*--|------------------+----------><----------+-------------*
* MOVEMOS LA INFORMACION DESDE NUESTRA TABLA INTERNA A LA
* VARIABLE WX-REGISTRO-SALIDA
************************************************************
21000-INFORMAR-REGISTRO.
*
MOVE WX-NOMBRE(WI-INDICE) TO WX-REGISTRO-SALIDA
.
*
************************************************************
* 22000 - ESCRIBIR FICHERO
*--|------------------+----------><----------+-------------*
* ESCRIBIMOS EN EL FICHERO DE SALIDA LA INFORMACION GUARDADA
* WX-REGISTRO-SALIDA
************************************************************
22000-ESCRIBIR-FICHERO.
*
WRITE REG-FICHERO FROM WX-REGISTRO-SALIDA
IF NOT FS-FICHERO-OK
DISPLAY 'ERROR EN WRITE DEL FICHERO:'FS-FICHERO
END-IF
.
*
************************************************************
* | 30000 - FINAL
*--|------------------+----------><----------+-------------*
* | FINALIZA LA EJECUCION DEL PROGRAMA
************************************************************
30000-FINAL.
*
PERFORM 31000-CERRAR-FICHERO
STOP RUN
.
*
************************************************************
* | 31000 - CERRAR FICHERO
*--|------------------+----------><----------+-------------*
* | CERRAMOS EL FICHERO DE SALIDA
************************************************************
31000-CERRAR-FICHERO.
*
CLOSE FICHERO
IF NOT FS-FICHERO-OK
DISPLAY 'ERROR EN CLOSE DEL FICHERO:'FS-FICHERO
END-IF
.
En el programa podemos ver las siguientes divisiones/secciones:
IDENTIFICATION DIVISION: existirá siempre.
ENVIRONMENT DIVISION: existirá siempre.
CONFIGURATION SECTION: existirá siempre.
INPUT-OUTPUT SECTION: en este ejemplo existirá porque utilizamos un fichero de salida.
DATA DIVISION: existirá siempre.
FILE SECTION: en este ejemplo existirá pues utilizamos un fichero de salida.
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.
OPEN: "Abre" los ficheros del programa. Lo acompañaremos de "INPUT" para los ficheros de entrada y "OUTPUT" para los ficheros de salida.
DISPLAY: escribe el contenido del campo indicado en la SYSOUT del JCL.
MOVE/TO: movemos la información de un campo a otro
PERFORM UNTIL: bucle
ADD:Operador de adición (suma)
WRITE: Escribe la información indicada en el "FROM" en el fichero indicado.
STOP RUN: sentencia de finalización de ejecución.
CLOSE: "Cierra" los ficheros del programa.
Descripción del programa:
En el párrafo de inicio, inicializamos las variables que utilizaremos a lo largo del programa:
WX-SYSIN: donde guardaremos posteriormente mediante un ACCEPT la información que hemos escrito en la SYSIN del JCL.
WX-TABLA-NOMBRES: donde moveremos la información de la SYSIN para dividirla en los diferentes nombres.
WX-REGISTRO-SALIDA: donde guardaremos la información que vamos a escribir en el fichero de salida.
Abriremos el fichero de salida (OPEN OUTPUT) y controlaremos el file-status. Si todo va bien el código del file-status valdrá '00'. Podéis ver la lista de los file-status más comunes.
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). Recordad que el tratamiento de las tablas internas está explicado en detalle en el ejemplo 1.
Montaremos un bucle para escribir cada uno de los nombres guardados en la tabla interna en el fichero de salida.
Informaremos la variable WX-REGISTRO-SALIDA con cada uno de los nombres de la tabla.
Escribiremos el fichero de salida. Para ellos indicaremos a continuación del WRITE la variable definida en la FILE-SECTION, y a continuación del FROM la variable donde tenemos guardada la información que queremos escribir.
Controlaremos el file-status.
Nota: es importante controlar el FILE-STATUS en los accesos a ficheros (OPEN, READ, WRITE, CLOSE...) para, en caso de fallo en la ejecución, tener información sobre la causa del error.
viernes, 18 de febrero de 2011
IBM y Bankinter hasta 2020

Este contrato, firmado en el mes de septiembre de 2010, renueva y amplía otro anterior suscrito entre ambas compañías en 2002.
A diferencia del contrato anterior, en el que IBM se encargaba solamente de la gestión del mainframe, ahora se encargará también del entorno distribuido.
Noticia Telcommunity
miércoles, 16 de febrero de 2011
JCL Básico I: ¿Qué es JCL?
¿Qué es JCL?
Vamos con un poco de teoría sobre este lenguaje.
JCL (Job Control Language) - Es un lenguaje de programación que permite codificar las instrucciones necesarias para la ejecución de un proceso batch. Estas instrucciones o sentencias son interpretadas y ejecutadas por el Gestor de trabajos (JES).
JCL (Job Control Language) - Es un lenguaje de programación que permite codificar las instrucciones necesarias para la ejecución de un proceso batch. Estas instrucciones o sentencias son interpretadas y ejecutadas por el Gestor de trabajos (JES).
La codificación de éste, se realiza con el editor ISPF.
Ésta introducción igual es un poco complicada porque solo hace referencia a la definición de términos generales del lenguaje. En los siguientes volúmenes se entra más al detalle, no os preocupéis si no os quedáis con nada de lo que se dice. No me entero ni yo... :-)
Sentencias JOB / EXEC / DD
- JOB - Identifica el trabajo a realizar. Es procesada por la parte de lectura del JES(Reader). Aporta un registro contable para el trabajo.
- EXEC - Identifica el programa o procedimiento que se va a ejecutar. Se procesa por parte del JES en el apartado (Converter) destinado a modificar el JCL por rutinas y sentencias estándar del sistema . Se abre un registro contable que identifica el paso de trabajo.
- DD - Identifica los recursos que va a utilizar el programa (por ejemplo, ficheros). Como la sentencia anterior la ejecución de la sentencia es procesada en primer lugar por el apartado (Converter) del JES. En algunos casos si ello fuese preciso se acude al Gestor de Recursos del Sistema (SRM)
lunes, 14 de febrero de 2011
Sentencia SORT en un programa COBOL
Hemos visto en otros artículos como hacer un SORT en un JCL. En este artículo veremos como hacerlo en un programa cobol.
¿Utilidad? Depende. Lo cierto es que pudiendo hacerlo por JCL, no tiene caso hacerlo en un programa. Pero quién sabe! Tal vez alguno de los lectores puedan darnos una idea de su uso práctico : D
SORT:
La sentencia SORT en cobol sirve para ordenar registros por un campo clave que le indiquemos. Podremos elegir varias claves y definir si el orden será ascendente o descendente.
Para el ejemplo, hemos creado un fichero temporal en nuestro programa que utilizaremos para generar en él la información ordenada. Como registros a ordenar, utilizaremos una tabla interna, de tal forma que no necesitaremos utilizar ficheros en ningún momento.
La información ordenada se cargará desde el fichero temporal al registro de salida, que podría utilizarse a lo largo de la ejecución.
Nosotros nos pararemos cuando tengamos nuestra información ordenada, y la "displayaremos" mostrándola por SYSOUT.
IDENTIFICATION DIVISION.
PROGRAM-ID. PRGSORT.
*
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
SPECIAL-NAMES.
DECIMAL-POINT IS COMMA.
*
INPUT-OUTPUT SECTION.
FILE-CONTROL.
* Fichero temporal donde guardaremos la información ordenada
SELECT TABLA-SORT ASSIGN TO DISK "SORTWORK".
*
DATA DIVISION.
FILE SECTION.
* Definición del fichero temporal
SD TABLA-SORT
DATA RECORD IS ELEMENTO-SORT.
01 ELEMENTO-SORT.
05 SORT-CLAVE1 PIC X.
05 SORT-CLAVE2 PIC X(3).
05 SORT-CAMPO PIC X(10).
05 SORT-INDICADOR PIC X.
*
WORKING-STORAGE SECTION.
* Formato del registro de salida
01 VARIABLES.
05 WA-REGISTRO.
10 WA-SORT-CLAVE1 PIC X.
10 WA-SORT-CLAVE2 PIC X(3).
10 WA-SORT-CAMPO PIC X(10).
10 WA-SORT-INDICADOR PIC X.
* Switches que utilizaremos en los bucles
01 SWITCHES.
05 SW-FIN-TABLA-SORT PIC X(1).
88 SI-FIN-TABLA-SORT VALUE 'S'.
88 NO-FIN-TABLA-SORT VALUE 'N'.
* Tabla interna con los datos a ordenar
01 TABLA.
05 WT-TBL-LISTA.
10 PIC X(15) VALUE 'F216CAMPO02802S'.
10 PIC X(15) VALUE 'M144CAMPO17114N'.
10 PIC X(15) VALUE 'Q651CAMPO24536S'.
10 PIC X(15) VALUE 'F217CAMPO03312N'.
10 PIC X(15) VALUE 'T487CAMPO44914S'.
10 PIC X(15) VALUE 'O372CAMPO52113N'.
10 PIC X(15) VALUE 'F457CAMPO61224N'.
10 PIC X(15) VALUE 'L547CAMPO73985N'.
10 PIC X(15) VALUE 'L354CAMPO89173N'.
10 PIC X(15) VALUE 'W516CAMPO92815N'.
05 REDEFINES WT-TBL-LISTA.
10 WT-TBL-ELEMENTO OCCURS 15
INDEXED BY WI-ELEM.
15 WT-TBL-CLAVE1 PIC X.
15 WT-TBL-CLAVE2 PIC X(3).
15 WT-TBL-CAMPO PIC X(10).
15 WT-TBL-INDICADOR PIC X.
* Formato del registro a ordenar
01 WR-ELEMENTO-SORT.
05 WR-SORT-CLAVE1 PIC X.
05 WR-SORT-CLAVE2 PIC X(3).
05 WR-SORT-CAMPO PIC X(10).
05 WR-SORT-INDICADOR PIC X.
*
PROCEDURE DIVISION.
*
PERFORM 1000-INICIO
PERFORM 2000-PROCESO
PERFORM 9000-FINAL
.
*
1000-INICIO.
*
INITIALIZE VARIABLES
.
*
2000-PROCESO.
* Proceso de SORT
* Indicamos las claves por las que ordenaremos ON ASCENDING ó
* ON DESCENDING
SORT TABLA-SORT
ON ASCENDING KEY SORT-CLAVE1
ON DESCENDING KEY SORT-CLAVE2
INPUT PROCEDURE 2100-PROCESO-ENTRADA
OUTPUT PROCEDURE 2200-PROCESO-SALIDA
* En la INPUT PROCEDURE cargamos los datos a ordenar
* En la OUTPUT PROCEDURE guardamos la información ordenada
* en variables del programa
* Controlamos el retorno del SORT con SORT-RETURN
IF SORT-RETURN NOT = ZEROS
DISPLAY 'ERROR EN EL SORT:' SORT-RETURN
END-IF
.
*
2100-PROCESO-ENTRADA.
* Cargamos los datos a ordenar(los de la tabla interna) en el
* fichero temporal donde se realizará la ordenación
* (ELEMENTO-SORT) con la sentencia RELEASE.
PERFORM VARYING WI-ELEM
FROM 1 BY 1
UNTIL WT-TBL-CLAVE1(WI-ELEM) = LOW-VALUES
OR WI-ELEM = 16
*
MOVE WT-TBL-CLAVE1(WI-ELEM) TO WR-SORT-CLAVE1
MOVE WT-TBL-CLAVE2(WI-ELEM) TO WR-SORT-CLAVE2
MOVE WT-TBL-CAMPO(WI-ELEM) TO WR-SORT-CAMPO
MOVE WT-TBL-INDICADOR(WI-ELEM) TO WR-SORT-INDICADOR
*
RELEASE ELEMENTO-SORT FROM WR-ELEMENTO-SORT
DISPLAY 'ELEMENTO-SORT:'WR-ELEMENTO-SORT
END-PERFORM
.
*
2200-PROCESO-SALIDA.
* Recuperamos los datos ordenados del fichero temporal con la
* sentencia RETURN y los displayamos
SET NO-FIN-TABLA-SORT TO TRUE
*
PERFORM UNTIL SI-FIN-TABLA-SORT
RETURN TABLA-SORT INTO WR-ELEMENTO-SORT
AT END
SET SI-FIN-TABLA-SORT TO TRUE
NOT AT END
MOVE WR-SORT-CLAVE1 TO WA-SORT-CLAVE1
MOVE WR-SORT-CLAVE2 TO WA-SORT-CLAVE2
MOVE WR-SORT-CAMPO TO WA-SORT-CAMPO
MOVE WR-SORT-INDICADOR TO WA-SORT-INDICADOR
*
DISPLAY 'REGISTRO->' WA-REGISTRO
*
END-RETURN
END-PERFORM
.
*
9000-FINAL.
*
STOP RUN
.
*
RESULTADO:
REGISTRO->F457CAMPO61224N
REGISTRO->F217CAMPO03312N
REGISTRO->F216CAMPO02802S
REGISTRO->L547CAMPO73985N
REGISTRO->L354CAMPO89173N
REGISTRO->M144CAMPO17114N
REGISTRO->O372CAMPO52113N
REGISTRO->Q651CAMPO24536S
REGISTRO->T487CAMPO44914S
REGISTRO->W516CAMPO92815N
¿Utilidad? Depende. Lo cierto es que pudiendo hacerlo por JCL, no tiene caso hacerlo en un programa. Pero quién sabe! Tal vez alguno de los lectores puedan darnos una idea de su uso práctico : D
SORT:
La sentencia SORT en cobol sirve para ordenar registros por un campo clave que le indiquemos. Podremos elegir varias claves y definir si el orden será ascendente o descendente.
Para el ejemplo, hemos creado un fichero temporal en nuestro programa que utilizaremos para generar en él la información ordenada. Como registros a ordenar, utilizaremos una tabla interna, de tal forma que no necesitaremos utilizar ficheros en ningún momento.
La información ordenada se cargará desde el fichero temporal al registro de salida, que podría utilizarse a lo largo de la ejecución.
Nosotros nos pararemos cuando tengamos nuestra información ordenada, y la "displayaremos" mostrándola por SYSOUT.
IDENTIFICATION DIVISION.
PROGRAM-ID. PRGSORT.
*
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
SPECIAL-NAMES.
DECIMAL-POINT IS COMMA.
*
INPUT-OUTPUT SECTION.
FILE-CONTROL.
* Fichero temporal donde guardaremos la información ordenada
SELECT TABLA-SORT ASSIGN TO DISK "SORTWORK".
*
DATA DIVISION.
FILE SECTION.
* Definición del fichero temporal
SD TABLA-SORT
DATA RECORD IS ELEMENTO-SORT.
01 ELEMENTO-SORT.
05 SORT-CLAVE1 PIC X.
05 SORT-CLAVE2 PIC X(3).
05 SORT-CAMPO PIC X(10).
05 SORT-INDICADOR PIC X.
*
WORKING-STORAGE SECTION.
* Formato del registro de salida
01 VARIABLES.
05 WA-REGISTRO.
10 WA-SORT-CLAVE1 PIC X.
10 WA-SORT-CLAVE2 PIC X(3).
10 WA-SORT-CAMPO PIC X(10).
10 WA-SORT-INDICADOR PIC X.
* Switches que utilizaremos en los bucles
01 SWITCHES.
05 SW-FIN-TABLA-SORT PIC X(1).
88 SI-FIN-TABLA-SORT VALUE 'S'.
88 NO-FIN-TABLA-SORT VALUE 'N'.
* Tabla interna con los datos a ordenar
01 TABLA.
05 WT-TBL-LISTA.
10 PIC X(15) VALUE 'F216CAMPO02802S'.
10 PIC X(15) VALUE 'M144CAMPO17114N'.
10 PIC X(15) VALUE 'Q651CAMPO24536S'.
10 PIC X(15) VALUE 'F217CAMPO03312N'.
10 PIC X(15) VALUE 'T487CAMPO44914S'.
10 PIC X(15) VALUE 'O372CAMPO52113N'.
10 PIC X(15) VALUE 'F457CAMPO61224N'.
10 PIC X(15) VALUE 'L547CAMPO73985N'.
10 PIC X(15) VALUE 'L354CAMPO89173N'.
10 PIC X(15) VALUE 'W516CAMPO92815N'.
05 REDEFINES WT-TBL-LISTA.
10 WT-TBL-ELEMENTO OCCURS 15
INDEXED BY WI-ELEM.
15 WT-TBL-CLAVE1 PIC X.
15 WT-TBL-CLAVE2 PIC X(3).
15 WT-TBL-CAMPO PIC X(10).
15 WT-TBL-INDICADOR PIC X.
* Formato del registro a ordenar
01 WR-ELEMENTO-SORT.
05 WR-SORT-CLAVE1 PIC X.
05 WR-SORT-CLAVE2 PIC X(3).
05 WR-SORT-CAMPO PIC X(10).
05 WR-SORT-INDICADOR PIC X.
*
PROCEDURE DIVISION.
*
PERFORM 1000-INICIO
PERFORM 2000-PROCESO
PERFORM 9000-FINAL
.
*
1000-INICIO.
*
INITIALIZE VARIABLES
.
*
2000-PROCESO.
* Proceso de SORT
* Indicamos las claves por las que ordenaremos ON ASCENDING ó
* ON DESCENDING
SORT TABLA-SORT
ON ASCENDING KEY SORT-CLAVE1
ON DESCENDING KEY SORT-CLAVE2
INPUT PROCEDURE 2100-PROCESO-ENTRADA
OUTPUT PROCEDURE 2200-PROCESO-SALIDA
* En la INPUT PROCEDURE cargamos los datos a ordenar
* En la OUTPUT PROCEDURE guardamos la información ordenada
* en variables del programa
* Controlamos el retorno del SORT con SORT-RETURN
IF SORT-RETURN NOT = ZEROS
DISPLAY 'ERROR EN EL SORT:' SORT-RETURN
END-IF
.
*
2100-PROCESO-ENTRADA.
* Cargamos los datos a ordenar(los de la tabla interna) en el
* fichero temporal donde se realizará la ordenación
* (ELEMENTO-SORT) con la sentencia RELEASE.
PERFORM VARYING WI-ELEM
FROM 1 BY 1
UNTIL WT-TBL-CLAVE1(WI-ELEM) = LOW-VALUES
OR WI-ELEM = 16
*
MOVE WT-TBL-CLAVE1(WI-ELEM) TO WR-SORT-CLAVE1
MOVE WT-TBL-CLAVE2(WI-ELEM) TO WR-SORT-CLAVE2
MOVE WT-TBL-CAMPO(WI-ELEM) TO WR-SORT-CAMPO
MOVE WT-TBL-INDICADOR(WI-ELEM) TO WR-SORT-INDICADOR
*
RELEASE ELEMENTO-SORT FROM WR-ELEMENTO-SORT
DISPLAY 'ELEMENTO-SORT:'WR-ELEMENTO-SORT
END-PERFORM
.
*
2200-PROCESO-SALIDA.
* Recuperamos los datos ordenados del fichero temporal con la
* sentencia RETURN y los displayamos
SET NO-FIN-TABLA-SORT TO TRUE
*
PERFORM UNTIL SI-FIN-TABLA-SORT
RETURN TABLA-SORT INTO WR-ELEMENTO-SORT
AT END
SET SI-FIN-TABLA-SORT TO TRUE
NOT AT END
MOVE WR-SORT-CLAVE1 TO WA-SORT-CLAVE1
MOVE WR-SORT-CLAVE2 TO WA-SORT-CLAVE2
MOVE WR-SORT-CAMPO TO WA-SORT-CAMPO
MOVE WR-SORT-INDICADOR TO WA-SORT-INDICADOR
*
DISPLAY 'REGISTRO->' WA-REGISTRO
*
END-RETURN
END-PERFORM
.
*
9000-FINAL.
*
STOP RUN
.
*
RESULTADO:
REGISTRO->F457CAMPO61224N
REGISTRO->F217CAMPO03312N
REGISTRO->F216CAMPO02802S
REGISTRO->L547CAMPO73985N
REGISTRO->L354CAMPO89173N
REGISTRO->M144CAMPO17114N
REGISTRO->O372CAMPO52113N
REGISTRO->Q651CAMPO24536S
REGISTRO->T487CAMPO44914S
REGISTRO->W516CAMPO92815N
viernes, 11 de febrero de 2011
Hasta el infinito y más allá

¿El motivo? Los clientes lo siguen demandando.
Sorprendentemente para muchos, los ingresos provenientes de estos sistemas han crecido un 70% en el último cuatrimestre de 2010 con respecto al mismo periodo del año anterior.
Desde este humilde consultorio, deseamos que la vida del mainframe no tenga fecha de caducidad en 2018, y como diría nuestro admirado Buz Lightyear, dure "hasta el infinito y más allá" :)
Artículo eWEEK Europe
miércoles, 9 de febrero de 2011
Ejemplo 1: Leer de SYSIN y escribir en SYSOUT.
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
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
lunes, 7 de febrero de 2011
Sentencia MERGE en un programa COBOL
Hemos visto en otros artículos como hacer un MERGE en un JCL. En este artículo veremos como hacerlo en un programa cobol.
¿Utilidad? Depende. Lo cierto es que pudiendo hacerlo por JCL, no veo la razón de hacerlo en un programa. Pero quién sabe! Tal vez alguno de los lectores pueda darnos una idea de su uso práctico : D
MERGE:
La sentencia MERGE en cobol sirve para unir dos ficheros teniendo en cuenta la clave por la que están ordenados. Es decir, no podemos hacer un MERGE de ficheros desordenados.
Lo que hará será "colocar" las claves que coincidan, juntas en el fichero de salida.
Para el ejemplo, utilizaremos:
2 ficheros de entrada con los datos a unir.
1 fichero temporal donde se realizará el MERGE.
Los datos de los ficheros de entrada para nuestro ejemplo serán:
FICHERO1:
A
B
C
FICHERO2:
B
C
E
Programa:
IDENTIFICATION DIVISION.
PROGRAM-ID.PRGMERGE.
*
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
SPECIAL-NAMES.
DECIMAL-POINT IS COMMA.
*
INPUT-OUTPUT SECTION.
FILE-CONTROL.
*Definición de ficheros
SELECT TABLA-MERGE ASSIGN TO DISK 'SORTWORK'.
SELECT TABLA-FICH1 ASSIGN TO FICHERO1.
SELECT TABLA-FICH2 ASSIGN TO FICHERO2.
*
DATA DIVISION.
FILE SECTION.
*Ficheros físicos
FD TABLA-FICH1
DATA RECORD IS FICHERO1.
01 FICHERO1.
05 FILLER PICTURE X.
FD TABLA-FICH2
DATA RECORD IS FICHERO2.
01 FICHERO2.
05 FILLER PICTURE X.
* Ficheros temporales
SD TABLA-MERGE
DATA RECORD IS ELEMENTO-MERGE.
01 ELEMENTO-MERGE.
05 MERGE-CLAVE1 PIC X.
*
WORKING-STORAGE SECTION.
* Variable donde guardaremos el resultado del MERGE
01 VARIABLES.
05 WA-REGISTRO.
10 WA-AUX-CLAVE1 PIC X.
* Switches para el bucle
01 SWITCHES.
05 SW-FIN-TABLA-MERGE PIC X(1).
88 SI-FIN-TABLA-MERGE VALUE 'S'.
88 NO-FIN-TABLA-MERGE VALUE 'N'.
* Registro para los datos después del MERGE
01 WR-ELEMENTO-MERGE.
05 WR-MERGE-CLAVE1 PIC X.
*
PROCEDURE DIVISION.
*
PERFORM 1000-INICIO
PERFORM 2000-PROCESO
PERFORM 9000-FINAL
.
*
1000-INICIO.
*
INITIALIZE VARIABLES
.
*
2000-PROCESO.
* Sentencia MERGE
* Juntamos FICHERO1 y FICHERO2 en TABLA-MERGE por clave
* MERGE-CLAVE1
MERGE TABLA-MERGE ASCENDING KEY MERGE-CLAVE1
USING FICHERO1 FICHERO2
OUTPUT PROCEDURE 2100-PROCESO-SALIDA
* En la OUTPUT PROCEDURE usaremos la información ya unida
IF SORT-RETURN NOT = ZEROS
DISPLAY 'ERROR EN EL MERGE:' SORT-RETURN
END-IF
.
*
2100-PROCESO-SALIDA.
* Movemos del fichero temporal al registro de salida con la
* sentencia RETURN
* Displayamos la información desde una variable definida
* en el programa
SET NO-FIN-TABLA-UNION TO TRUE
*
PERFORM UNTIL SI-FIN-TABLA-UNION
RETURN TABLA-MERGE INTO WR-ELEMENTO-MERGE
AT END
SET SI-FIN-TABLA-MERGE TO TRUE
NOT AT END
MOVE WR-MERGE-CLAVE1 TO WA-AUX-CLAVE1
DISPLAY 'REGISTRO->' WA-REGISTRO
END-RETURN
END-PERFORM
.
*
9000-FINAL.
*
STOP RUN
.
*
RESULTADO:
REGISTRO->A
REGISTRO->B
REGISTRO->B
REGISTRO->C
REGISTRO->C
REGISTRO->E
Nota: aunque no veais nigún párrafo de "abrir fichero", "leer fichero", etc., no significa que se nos haya ido la olla, es que no es necesario!
¿Utilidad? Depende. Lo cierto es que pudiendo hacerlo por JCL, no veo la razón de hacerlo en un programa. Pero quién sabe! Tal vez alguno de los lectores pueda darnos una idea de su uso práctico : D
MERGE:
La sentencia MERGE en cobol sirve para unir dos ficheros teniendo en cuenta la clave por la que están ordenados. Es decir, no podemos hacer un MERGE de ficheros desordenados.
Lo que hará será "colocar" las claves que coincidan, juntas en el fichero de salida.
Para el ejemplo, utilizaremos:
2 ficheros de entrada con los datos a unir.
1 fichero temporal donde se realizará el MERGE.
Los datos de los ficheros de entrada para nuestro ejemplo serán:
FICHERO1:
A
B
C
FICHERO2:
B
C
E
Programa:
IDENTIFICATION DIVISION.
PROGRAM-ID.PRGMERGE.
*
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
SPECIAL-NAMES.
DECIMAL-POINT IS COMMA.
*
INPUT-OUTPUT SECTION.
FILE-CONTROL.
*Definición de ficheros
SELECT TABLA-MERGE ASSIGN TO DISK 'SORTWORK'.
SELECT TABLA-FICH1 ASSIGN TO FICHERO1.
SELECT TABLA-FICH2 ASSIGN TO FICHERO2.
*
DATA DIVISION.
FILE SECTION.
*Ficheros físicos
FD TABLA-FICH1
DATA RECORD IS FICHERO1.
01 FICHERO1.
05 FILLER PICTURE X.
FD TABLA-FICH2
DATA RECORD IS FICHERO2.
01 FICHERO2.
05 FILLER PICTURE X.
* Ficheros temporales
SD TABLA-MERGE
DATA RECORD IS ELEMENTO-MERGE.
01 ELEMENTO-MERGE.
05 MERGE-CLAVE1 PIC X.
*
WORKING-STORAGE SECTION.
* Variable donde guardaremos el resultado del MERGE
01 VARIABLES.
05 WA-REGISTRO.
10 WA-AUX-CLAVE1 PIC X.
* Switches para el bucle
01 SWITCHES.
05 SW-FIN-TABLA-MERGE PIC X(1).
88 SI-FIN-TABLA-MERGE VALUE 'S'.
88 NO-FIN-TABLA-MERGE VALUE 'N'.
* Registro para los datos después del MERGE
01 WR-ELEMENTO-MERGE.
05 WR-MERGE-CLAVE1 PIC X.
*
PROCEDURE DIVISION.
*
PERFORM 1000-INICIO
PERFORM 2000-PROCESO
PERFORM 9000-FINAL
.
*
1000-INICIO.
*
INITIALIZE VARIABLES
.
*
2000-PROCESO.
* Sentencia MERGE
* Juntamos FICHERO1 y FICHERO2 en TABLA-MERGE por clave
* MERGE-CLAVE1
MERGE TABLA-MERGE ASCENDING KEY MERGE-CLAVE1
USING FICHERO1 FICHERO2
OUTPUT PROCEDURE 2100-PROCESO-SALIDA
* En la OUTPUT PROCEDURE usaremos la información ya unida
IF SORT-RETURN NOT = ZEROS
DISPLAY 'ERROR EN EL MERGE:' SORT-RETURN
END-IF
.
*
2100-PROCESO-SALIDA.
* Movemos del fichero temporal al registro de salida con la
* sentencia RETURN
* Displayamos la información desde una variable definida
* en el programa
SET NO-FIN-TABLA-UNION TO TRUE
*
PERFORM UNTIL SI-FIN-TABLA-UNION
RETURN TABLA-MERGE INTO WR-ELEMENTO-MERGE
AT END
SET SI-FIN-TABLA-MERGE TO TRUE
NOT AT END
MOVE WR-MERGE-CLAVE1 TO WA-AUX-CLAVE1
DISPLAY 'REGISTRO->' WA-REGISTRO
END-RETURN
END-PERFORM
.
*
9000-FINAL.
*
STOP RUN
.
*
RESULTADO:
REGISTRO->A
REGISTRO->B
REGISTRO->B
REGISTRO->C
REGISTRO->C
REGISTRO->E
Nota: aunque no veais nigún párrafo de "abrir fichero", "leer fichero", etc., no significa que se nos haya ido la olla, es que no es necesario!
viernes, 4 de febrero de 2011
¿Cambiaría su casa de sólidos muros de piedra...?

Según estudios recientes, 75% de los datos generados por negocios son procesados por programas creados en Cobol. Hoy por hoy, la programación en Cobol es uno de los negocios más rentables del mundo de la informática.
El principal problema al que se enfrentan todas las grandes empresas que implementan sus procesos de negocio en Cobol es la desaparición de especialistas debido a la falta de profesionales de nueva generación con conocimientos más profundos en Cobol.
Hay dos principales soluciones ante este problema. Una sería la que plantea Miguel Fito, no muy novedosa, soluciones de migración que permitan un ahorro de costos y mayor flexibilidad. "Casualmente", este artículo del Director de Desarrollo de Negocios de Micro Focus para Iberia y Latinoamérica, coindice con el lanzamiento de Visual Cobol R3.
Otra, planteada desde nuestra humilde opinión como trabajadores del sector, sería que el ámbito de la formación profesional (como bien indica su nombre) y el mundo universitario se acercasen a la realidad profesional que espera a sus alumnos, dotando el contenido de carga lectiva de las principales tecnologías existente en el actual mercado, entre las que ocupa un lugar principal Cobol. Por poner cifras, en un portal como Infojobs, hay 423 ofertas en Cobol y 1280 en Java. ¿No sería lógico que dicha proporción se aplicase al contenido de materias educativas en el ámbito de informática? Pues parece que no... Y mientras tanto, diversas compañías intentan vender soluciones de migración amparadas en la falta de profesionales en la materia.
Además nadie usaría su coche para cruzar el Pacífico ni un barco para atravesar España. (por muchos más conductores de coches que capitanes de barco exista)
Artículo Infochannel
miércoles, 2 de febrero de 2011
OFFSET y S0C7, ¿Cómo localizar el error?
Nos casca un jcl por un S0C7, ¡vaya! a ver como sé yo ahora que línea de las mil que tengo me lo ha dado!. Se acabó el proceso engorroso de buscar qué maldito move me está causando el problema.
En algunas plataformas existen programas REXX que automáticamente te dicen la línea del programa que te ha provocado el offset. Intentaré crear uno común y lo cuelgo en el blog.
Pero para todos los demás lugares donde no existe esta utilidad, aquí os dejo como hacer para saber la línea:
- Provocar el offset. (Por ejemplo, mover un alfanumérico con símbolos raros a un comprimido)
- Encontrar el númerito del OFFSET. Desde la cola ST, entramos con ‘?’ en el jcl fallido. Entramos con ‘S’ en el apartado SYSOUT:
- Pondrá algo parecido a esto:
CEE3207S The system detected a data exception (System Completion Code=0C7). From compile unit NOMBRE_PGM at entry point NOMBRE_PGM at compile unit offsset +00001460 at entry offset +00001460 at address 1965BBE0. - Nos apuntamos el número del offset (1460). El + y los ceros podemos olvidarlos. No os asustéis si el número trae letras ya que se trata de un número hexadecimal.
- Necesitamos tener la compilación del programa que nos da el offset. Volvemos a la cola ST y entramos con una S en la compilación. Es importante que sea la misma compilación con la que hemos ejecutado o al menos que la compilación no haya cambiado con respecto a la de la ejecución.
- Una vez dentro buscamos la palabra “HEXLOC”. (F HEXLOC).
- Aparecerá una lista parecida a esta:
LINE Ñ HEXLOC VERB LINE Ñ HEXLOC VERB000954 000BEC PERFORM 000958 000C04 PERFORM000976 000C40 PERFORM 000978 000C60 INITIALIZE000984 000D38 DISPLAY 000986 000D42 PERFORM001110 001408 PERFORM 001122 00142A INITIALIZE001125 001456 MOVE 001126 001468 MOVE001128 001478 MOVE 001129 001482 MOVE001144 001498 PERFORM 001146 0014B4 PERFORM001150 0014EA STOP 001158 00150C CLOSE001163 001554 INITIALIZE 001165 001606 SET001168 001616 MOVE 001169 001622 MOVE001174 001644 IF 001175 001652 INITIALIZE - Nos fijamos en las columnas HEXLOC(generalmente tendremos 3, hubiera puesto la tercera pero no me cabe), y vemos que tiene unos número hexadecimales. Ahora lo que tenemos que hacer es buscar nuestro offset en estas 3 columnas. Están ordenadas de menor a mayor y de izquierda a derecha, si no lo encontramos como en el caso del ejemplo, hemos de buscar el rango donde se encuentra y quedarnos con el primero. En el ejemplo buscamos el 1460, que está entre el 1456 y el 1478. Nos quedamos siempre con el primero (1456) y miramos el valor de la columna LINE de éste. En este caso es 1125. ¡Esta es la línea del programa que nos está dando el offset!
Os dejo el enlace para descargaros el código REXX para localizar el OFFSET:
Descargar OFFSET
Suscribirse a:
Entradas (Atom)