jueves, 31 de marzo de 2011

CONSULTIRAS 3: "Jcl Failed"

Seguro que alguna vez, a todos los que trabajamos o hemos trabajado en un entorno HOST nos ha pasado algo similar:


(Pincha en la imagen para verla más grande)

miércoles, 30 de marzo de 2011

Esquema de un programa pl/i

Un programa pl/i no tiene una estructura "fija", es decir, el programa compilará aunque no sigas un esquema determinado. Pero para facilitar la comprensión del código, siempre es mejor seguir algunas pautas : )

INICIO DEL PROGRAMA
Todo programa principal empezará con la sentencia:
PRUEBA1: PROCEDURE OPTIONS(MAIN);

Donde:
PRUEBA1 es el identificador del programa. No es necesario que coincida con el nombre externo del programa. Tendrá una longitud máxima de 7 caracteres.
PROCEDURE o PROC es la palabra clave que le indica al compilador que el programa es un procedimiento.
OPTIONS(MAIN): son las palabras clave que indican al compilador que se trata de un programa principal.
Para rutinas y programas online ya lo iremos viendo.
Los comentarios empezarán con /* y terminarán con */ y podrán codificarse en cualquier parte del código. No hay que colocar el inicio en ninguna columna en concreto.

PROCESO DEL PROGRAMA
En el proceso del programa incluiremos tanto la declaración de variables, como llamadas a otros procedimientos, como la funcionalidad del programa en sí.
Para las declaraciones utilizaremos la sentencia DECLARE o DCL para abreviar.

Declaración de funciones del sistema:
DCL (DATE,TIME) BUILTIN;

Donde DATE y TIME serían la fecha y hora del sistema respectivamente.

Declaración de ficheros:
DCL SYSPRINT FILE PRINT;
DCL SYSIN    FILE STREAM INPUT;
DCL SALIDA   FILE STREAM OUTPUT;
DCL SALIDA   FILE RECORD OUTPUT;
DCL REG_SALIDA CHAR(100);


Donde:
FILE indica que se trata de un fichero.
STREAM indica que los datos se guardarán como una cadena de caracteres. El acceso siempre será secuencial.
INPUT/OUTPUT indica que es un fichero de entrada/salida respectivamente.
RECORD indica que los datos se guardarán en un formato especifico (en nuestro caso REG_SALIDA). Existen diferentes tipos de acceso que iremos viendo en futuros ejemplos.

Declaración de variables:
Para entenderlo mejor, veamos un cuadro con la equivalencia entre declaraciones COBOL y PL/I:

01 ALFAN PIC X(N) VALUE SPACES.DCL ALFAN CHAR (N) INIT ' ';
01 NUMER PIC 9(N) VALUE ZEROS.DCL NUMER PIC '(N)9' INIT (0);
01 COMPR PIC S9(N)V9(M) COMP-3.DCL COMPR DEC FIXED (N+M,M);
01 HEXA1 PIC S9(4) COMP.DCL HEXA1 BIN FIXED (15) INIT(0);
01 HEXA2 PIC S9(9) COMP.DCL HEXA2 BIN FIXED (31) INIT (0);
01 ESTRUCTURA.DCL 1 ESTRUCTURA,
   05 ALFANUM PIC X.      5 ALFANUM CHAR,
   05 NUMERICO PIC 9(2).      5 NUMERICO PIC '(2)9',
   05 COM PIC 9(7)V9(2) COMP-3.      5 COM DEC FIXED (9,2),
   05 BINHEXA PIC S9(4) COMP.      5 BINHEXA BIN FIXED (15),
   05 BINHEXA2 PIC S9(9) COMP.      5 BINHEXA2 BIN FIXED (31);
01 EDITADO PIC ZZZ9,99.DCL EDITADO PIC 'ZZZ9V,99';
01 SWITCH PIC X VALUE 'S'.DCL BOOLEAN BIT;
   88 VERDAD    VALUE 'S'.DCL VERDAD  BIT INIT('1'B);
   88 FALSO     VALUE 'N'.DCL FALSO   BIT INIT('0'B);
01 TABLA PIC X OCCURS 3 TIMES.DCL TABLA(3) CHAR(1);


Donde:
CHAR indica campo alfanumérico.
INIT indica el valor que toma la variable (equivale a nuestro VALUE).
PIC (N)9 indica campo numérico.
DEC FIXED indica campo comprimido (equivale a COMP-3). En los campos con decimales hay que tener en cuenta que si en cobol indicábamos 9(N)V9(M), en pl/i se indica (N+M,M).
BIN FIXED indica campo binario (hexadecimal).
BIT indica variable booleana que tomará los valores de verdadera '1'B (SET campo a TRUE) y falsa '0'B (SET campo a FALSE).
Para definir tablas se indicará el número de filas entre paréntesis; como el ejemplo TABLA(3). Si fuese una tabla de dos dimensiones escribiríamos primero el número de filas y después el número de comumnas:
DCL TABLA_DOBLE(2,3) CHAR(3);

En PL/I no estamos obligados a declarar las variables, aunque es recomendable. Veamos que pasa cuando no declaramos variables, o no las declaramos "del todo":

Si sólo declaramos la base:
DEC = DEC FLOAT (6)
BIN = BIN FLOAT (21)


Si sólo declaramos la precisión:
FIXED = DEC FIXED (5,0)
FLOAT = DEC FLOAT (6)


Si declaramos base + precisión:
BIN FIXED = BIN FIXED (15,0)
DEC FIXED = DEC FIXED (5,0)
BIN FLOAT = BIN FLOAT (21)
DEC FLOAT = DEC FLOAT (6)


Sin declarar nada de nada:
COMIENZA POR: I-N                          = BIN FIXED (15,0)
COMIENZA POR: A-H O-Z Y CARACT. ESPECIALES = DEC FLOAT(6)



CALCULO DE LONGITUDES

Numéricos y alfanuméricos
Lo que venga indicado en el PIC (N)9 o en el CHAR(N).
DCL ALFANUM CHAR(5);
DCL NUMERICO PIC (5)9;


longitud = 5

Comprimidos
Si tenemos un campo DEC FIXED (N,M), la longitud será N+1/2. Si el resultado tiene decimales redondeamos hacia arriba. El máximo permitido es N = 15.
DCL COMPRIMIDO DEC FIXED (7,2);

longitud = 7+1/2 = 4

Binarios
Si tenemos un campo BIN FIXED (N,M), el número de bytes que se reservan en memoria para cada campo es:
2 posiciones para N entre 1 y 15.
4 posiciones para N ente 16 y 31(máximo permitido).

FINAL DEL PROGRAMA
Para finalizar el programa escribiremos la sentencia:
END PRUEBA1;

Donde PRUEBA1 es el nombre del proceso principal que habíamos definido al inicio del programa.

La parte de informar variables, condiciones, bucles, etc. lo dejaremos para el siguiente artículo : )

¿Qué es PL/I?

PL/I (Programming Language 1) es un lenguaje de programación de IBM (al igual que COBOL) que os podréis encontrar en muchos entornos HOST. Incluso si trabajáis en COBOL, es probable que alguna aplicación tenga código en PL/I y seguro que tenéis acceso al compilador.

Veamos las principales diferencias con COBOL:
  • No existe área A y área B. Existe un único área desde la columna 2 a la 72.
  • No hay divisiones ni secciones.
  • Acepta caracteres alfabéticos de la A a la Z (solo en mayúsuculas). Acepta los caracteres '@', '$', '#' ó 'Ñ' (según la instalación). Los números del 0 al 9. No acepta el guión '-' pero sí el guión bajo '_'.
  • Operadores lógicos: negación (¬), igualdad (=), conjunción (&), disyunción (|), mayor que (>), menor que (<).

  • No es necesario declarar las variables (aunque recomendable).

  • En pl/i no existen las palabras reservadas, sólo palabras CLAVE pero que pueden utilizarse como variables.

  • El final de una sentencia se termina con punto y coma (;) en lugar de con un punto (.), y además debe finalizarse cada sentencia, no vale con poner un ; al final de un procedimiento (como en COBOL que poníamos un . al final de párrafo).



Estas son a grandes rasgos las principales diferencias entre los dos lenguajes. En próximos artículos iremos viendo como codificar un programa PL/I, su estructura, como informar variables, como hacer bucles, etc.

Principiantes PL/I

Ahora que ya te habías hecho un experto en COBOL gracias al manual del Consultorio, resulta que a tu empresa se le ocurre cambiarte a un proyecto en PL/I.
¿¿¿Y ahora qué hago???

Que no cunda el pánico!! En el Consultorio Cobol estamos preparados para todo tipo de contratiempos. Con todos vosotros, el nuevo "manual de PL/I para principiantes"!

Manual de PL/I para principiantes

1. ¿Qué es PL/I?
  • Descripción del lenguaje de programación PL/I.
  • Diferencias principales con COBOL.

2. Esquema de un programa PL/I.
  • Esquema básico de un programa PL/I.
  • Declaración de variables en PL/I.

3. Proceso de un programa PL/I.
  • Informando variables: asignación de valores.
  • Inicializando variables: sentencia INIT.
  • Condiciones: IF/ELSE.
  • Bucles: DO WHILE, DO UNTIL, DO/TO.

4. Ficheros en PL/I.
  • Ficheros RECORD.
  • Ficheros STREAM.

5. Ejemplo 1: leer de SYSIN y escrbir en SYSPRINT (pl/i).
  • JCL que ejecuta un programa sin DB2.
  • Programa que realiza un GET de la SYSIN y un PUT de la información recibida para mostrarla por SYSPRINT.

Continuará...

lunes, 28 de marzo de 2011

Lo mejor de la semana: lunes 21 a domingo 27.

Y aquí estamos una vez más, para echar la vista atrás de lo que ha sido esta semana.
Una nueva Consultira, nueva noticia comentada por Loboc, nuevo ejemplo para el manual de Cobol para principiantes y record de visitas el miércoles día 23. No nos podemos quejar!
Y como estamos que lo tiramos, me voy a atrever a hacer una promesa/adelanto; empezar dos nuevas secciones: CICS y PL/I.

Dicho queda!
Os dejamos con lo mejor de la semana. Eso sí, definitivamente sin tener en cuenta los artículos de errores, porque no hay quien compita con ellos!!! ^^

  1. Sort vol.2: OUTREC.
  2. Esquema de un programa COBOL.
  3. Programas con DB2 I: SELECT, INSERT, UPDATE y DELETE.
  4. ICETOOL(I): SPLICE; Cruce de ficheros.
  5. LOAD y UNLOAD: carga y descarga.
  6. JCL básico: IDCAMS, IEFBR14, IEBGENER.
  7. Sort vol.1: SORT, INCLUDE.
  8. Sort vol.5: SUM.
  9. Ejemplo 4: generando un listado.
  10. Programas con DB2 III: COUNT, MAX y FOR UPDATE.

Enhorabuena a Alvarito que ha sido el ganador de esta semana con su artículo "Sort vol.2: OUTREC."!
Nos veremos las caras el lunes que viene!!!

JCL Básico IV: Sentencia DD (Parte I)

Sentencia que describe los ficheros con los que se va a trabajar (una sentencia DD por cada fichero). Identifica cada fichero lógico definido en la SELECT del programa con su fichero físico.
No puede existir mas de una sola DD identificada con el mismo nombre lógico.

//FICHERO1 DD DSN=xxxx.nombre.fichero
//            DISP=(NEW,CATLG,DELETE),VOL=SER=SYSWKl,
//            UNIT=3380,LABEL=3,SPACE=(TRK,(10,5),RLSE),
//            COPIES=4,DEST=RMT005,OUTLIM=1500,
//            RECFM=FB,LRECL=150,BLKSIZE=1500


La no especificación de nombre lógico en una ficha DD presupone la concatenación al fichero de la sentencia DD anterior. En ocasiones un paso puede precisar de mas de un fichero para una determinada entrada de datos y ello es posible por medio de la concatenación de DD.s. La forma en codificarse es:

//ENTRADA1 DD DSN=xxxx.nombre.fichero1,DISP=SHR
//         DD DSN=xxxx.nombre.fichero2,DISP=SHR
//SALIDA1  DD DSN=xxxx.nombre.fichero3,
//            DISP=(NEW,CATLG,DELETE),VOL=SER=SYSWKl,
//            UNIT=3380,LABEL=3,SPACE=(TRK,(10,5),RLSE),
//            COPIES=4,DEST=RMT005,OUTLIM=1500,
//            RECFM=FB,LRECL=150,BLKSIZE=1500


NOTA. En este caso ambos ficheros de entrada han de tener la misma longitud.

Anteriormente a este artículo he hablado de las líneas de la cabecera de un jcl y de las sentencias que ejecutan programas (EXEC). Podemos tener sentencias DD tanto en la cabecera como en cada uno de los pasos de nuestro jcl:

  • Sentencias DD en la cabecera del jcl.
    Las únicas DD asociadas a la ficha JOB son aquellas destinadas a definir librerías de acceso a las que deberán acudir los trabajos en tiempo de ejecución. El nombre lógico que las identifica es:
    • JOBLIB --> La ejecución de un programa se inicia en la busca del objeto (código en lenguaje maquina) en las librerías estandars de la instalación (SYS1.LINKLIB) pero en según que casos puede sernos de utilidad el desplazar esa búsqueda a otras librerías.
      La especificación de una o varias librerías no evita en ultimo caso el acudir a las estandars de la instalación si no se encontrase en ninguna de las referidas.
      Ha de codificarse después de la ficha JOB y antes de cualquier paso EXEC.
      No puede utilizarse en procedimientos catalogados.
      La codificación de JOBLIB predispone a los pasos EXEC posteriores a que todos acudan a esas librerías para la obtención del objeto a ejecutar. Será excepción de lo dicho los pasos EXEC que dispongan de una DD STEPLIB en cuyo caso serán esas las librerías de captura.
    • JOBCAT --> La diferencia de la JOBCAT con la JOBLIB radica en que mientras la anterior buscaba el objeto a ejecutar, esta marca el camino a seguir para la búsqueda y obtención del catalogo de ficheros. sigue las mismas pautas y en ultimo extremo acude a las estancadas de la instalación.

      Ha de codificarse después de la ficha JOB y de la JOBLIB y antes de cualquier paso EXEC.
      La codificación de JOBCAT predispone a los pasos EXEC posteriores a que todos acudan a esas librerías para la obtención del catalogo de ficheros. Será excepción de lo dicho los pasos EXEC que dispongan de una DD STEPCAT en cuyo caso serán esas las librerías de catalogo.
    • SYSCHK --> Define el fichero de grabación de CHEKPOINTS (puntos de control) de un programa que se guardan para rearranque posterior.
      Debe ser anterior a cualquier paso EXEC de un JOB puesto que en rearranque y especificando la identificación del punto de control se deberá acudir a este fichero antes que al paso para obtener la información del programa que se pretende arrancar.

  • Sentencias DD en cada paso (EXEC) del jcl.
    Destinadas a definir librerías de acceso a las que deberán acudir los pasos de un trabajos en tiempo de ejecución. El nombre lógico que las identifica es:
    • STEPLIB --> La ejecución de un programa se inicia en la busca del objeto (código en lenguaje maquina) en las librerías estándar de la instalación (SYS1.LINKLIB) pero en según que casos puede sernos de utilidad el desplazar esa búsqueda a otras librerías.
      La especificación de una o varias librerías no evita en ultimo caso el acudir a las stándar de la instalación si no se encontrase en ninguna de las referidas.
      Ha de codificarse después de la ficha EXEC aunque no tiene porque ser la primera DD
      A diferencia de la JOBLIB puede utilizarse en procedimientos catalogados.

      En el siguiente ejemplo, cuando se lance el job, éste buscará el ejecutable del programa PROGRAMA1 en la libreria1 y si no lo encuentra lo buscará en la librería2:

      //PASO01 EXEC PGM=PROGRAMA1
      //STEPLIB DD DSN=xxxx.nombre.libreria1,DISP=SHR
      //        DD DSN=xxxx.nombre.libreria2,DISP=SHR
      ...
      ...


      ¿Para que puede servir la STEPLIB? Son muchas las utilidades que tiene:
      Pongo un ejemplo:
      - Para pruebas en el entorno de explotación, imagina que quieres hacer una prueba de un programa que has creado pero que todavía no está subido al entorno de explotación, pero el cliente insiste en probarlo bien y no te queda otra que hacerlo con datos de explotación.
      SOLUCIÓN: Lo que debes hacer es dejar la compilación de tu programa en una librería a la que tengas acceso en el entorno de producción (una librería que hayas creado tú o alguna puente o de intercambio que exista, en muchas plataformas existe una librería preparada para este fin). Una vez copiado tu ejecutable en dicha librería, únicamente tienes que indicar en tu paso de jcl donde se ejecuta el programa, la STEPLIB del siguiente modo:

      //PASO01 EXEC PGM=MIPROGRAMA
      //STEPLIB DD DSN=xxxx.nombre.libreria1,DISP=SHR
      ...


      Cuando se ejecute el jcl, el programa "tirará" de la librería indicada en la STEPLIB.

      Pongo otro ejemplo:
      - Imagina que puntualmente se necesita modificar un programa durante un periodo en concreto. Por ejemplo, tienes un programa con información acerca de la velocidad máxima en las autovías (120 km/h). Pero resulta que de la noche a la mañana lo que creías que no iba a ocurrir ocurre, y es que la velocidad ha de ser bajada a 110 km/h durante un plazo determinado. Bien, lo normal sería modificar tu programa, implantarlo, y esperar a que dentro de X meses tengas que volver a modificar el programa para devolverlo a su estado original(120 km/h), con todo lo que eso conlleva, volver a bajar el programa, modificar, probar, implantar, etc....
      SOLUCIÓN: Modificas el programa para que admita los 110 km/h pero no lo implantas en producción sino que copias el ejecutable en una librería auxiliar(temporal, puente o como quieras llamarla). Luego, en el jcl que ejecuta tu programa, lo único que haces es añadir al paso una línea de STEPLIB donde indicas esa librería puente donde has dejado la compilación. De este modo durante los próximos meses tu jcl ejecutará la versión del programa que está preparada para los 110 km/h. Cuando la norma cambie y volvamos a poder "correr" en las carreteras a 120, únicamente has de quitar la línea del jcl, o bien borrar el programa modificado de la librería auxiliar (si el jcl no encuentra el programa en la librería indicada en la STEPLIB irá a buscarlo a la librería original).
    • STEPCAT --> La diferencia de la STEPCAT con la STEPLIB radica en que mientras la anterior buscaba el objeto a ejecutar, esta marca el camino a seguir para la búsqueda y obtención del catalogo de ficheros. sigue las mismas pautas y en ultimo extremo acude a las stándar de la instalación.

      Ha de codificarse después de la ficha EXEC aun que no tiene porque ser la primera DD.
      La sentencia STEPCAT solo puede referirse a catálogos de usuario del tipo VSAM.
    • SYSABEND --> Determina el fichero donde el sistema efectuara el vuelco de memoria por terminación anormal ABENDED. La información que aporta hace referencia a:
      Núcleo del sistema
      Área del programa problema
      Tabla de Trace
    • SYSUDUMP --> Determina el fichero donde el sistema efectuara el vuelco de memoria por terminación anormal ABENDED. A diferencia de la anterior tan solo facilita información del área del programa.
    • DUMMY --> representa un fichero ficticio, el programa lo abrirá, efectuará operaciones de E/S sobre el (ficticias), pero el sistema no dará error. Muy útil cuando no queremos que, por lo que sea, no queremos obtener el fichero de salida de un programa, ponemos el fichero a DUMMY y la ejecución será exactamente igual que si lo tuviéramos pero sin escribir nada en la salida.

La sentencia DD es bastante amplia, por lo tanto, continuaré con ella en el próximo artículo...(JCL Básico IV: Sentencia DD (Parte II)

viernes, 25 de marzo de 2011

Cobol para rato

Después de su nacimiento en 1960, COBOL (COmmon Business -Oriented Language o Lenguaje Común Orientado a Negocios) tiene actualmente una vigencia sorprendente en el mercado.

Según datos de la multinacional Micro Focus, desarrolladora de soluciones que facilitan la migración y modernización de aplicaciones, el 70% de las grandes empresas de todo el mundo siguen utilizando aplicaciones desarrolladas con el lenguaje de programación Cobol y se calcula que existen 220.000 millones de líneas de código activas escritas con este lenguaje creado para desarrollar aplicaciones de negocio y finanzas.

Alrededor de cinco millones de líneas de código Cobol se añaden a los sistemas vivos cada año.

El principal éxito de COBOL frente a otros lenguajes radica en sus origenes. Ángel Martínez Navarro, miembro de la Academia de Tecnología y del Comité de Expertos en Tecnología de IBM, asegura: “Cobol sigue viviendo en buena parte el tremendo impulso que recibió en el momento de su nacimiento." Según Martínez Navarro, con los años, Cobol “ha seguido demostrando atributos de sencillez de aprendizaje y uso, portabilidad entre plataformas, eficiencia en desarrollo y en ejecución..."

Siempre se ha dicho que las organizaciones con aplicaciones Cobol se enfrentan al hándicap de un elevado coste de mantenimiento, ¿pero que porcentaje de ese coste viene dado por el simple hecho del tamaño de las empresas que lo usan?, ya que el 70% de las grandes empresas de todo el mundo siguen utilizando aplicaciones desarrolladas con el lenguaje de programación Cobol. Posiblemente los costes de la tienda de ropa de la esquina de mi barrio tenga costes más bajos para mantenimiento de su sistema informático (si lo tiene...) que la de Zara por la simple razón del tamaño.

¿El futuro? Pues como siempre, depende desde donde se mire.

Para el analista de IDC España Rafael Achaerandio, “es inevitable que en las organizaciones se tienda a usar menos Cobol, frente a lenguajes como Java”. Eso sí, matiza, “a Cobol le quedan muchos años por delante, y no sólo por una cuestión de criterio tecnológico, sino de negocio”.

Para Martínez Navarro, “muchas organizaciones dependen de grandes aplicaciones Cobol críticas para sus operaciones, y los costes y riesgos de plantear su migración a otro lenguaje son inasumibles."

En lo que coinciden todos es que todavía tendremos Cobol para rato.

Artículo Computerworld

jueves, 24 de marzo de 2011

CONSULTIRAS 2: "Dresscode"

En muchas de las empresas consultiras, perdón, consultoras, hay unas normas de vestimenta, normas que algunas personas entienden a su manera...


(Pincha en la imagen para verla más grande)

miércoles, 23 de marzo de 2011

Ejemplo 4: generando un listado.

Un programa típico que nos encontraremos en cualquier aplicación es aquel que genera un listado. ¿Que a qué nos referimos con listado? Mejor verlo para hacernos una idea:

----+----1----+----2----+----3----+----4----+
 LISTADO DE EJEMPLO DEL CONSULTORIO COBOL
 DIA: 23-03-11                  PAGINA: 1
 ------------------------------------------

 NOMBRE         APELLIDO
 ---------   ---------------
 ANA         LOPEZ
 ANA         MARTINEZ

 ANA         PEREZ
 ANA         RODRIGUEZ

 TOTAL ANA      : 04


 LISTADO DE EJEMPLO DEL CONSULTORIO COBOL
 DIA: 23-03-11                  PAGINA: 2
 ------------------------------------------

 NOMBRE         APELLIDO
 ---------   ---------------
 BEATRIZ     GARCIA
 BEATRIZ     MOREDA
 BEATRIZ     OTERO

 TOTAL BEATRIZ  : 03
 TOTAL NOMBRES: 07


Aquí vemos un listado de 2 páginas. Ambas páginas tienen una parte común que se denomina cabecera y que, por lo general, será la misma en todas las páginas. Suele contener un título que describa al listado y la fecha en que ha sido generado. Lo único que cambia es el número de página en el que estamos^^

Después tenemos una "subcabecera" también común en cada página, en este caso la subcabecera incluye a "NOMBRE" y "APELLIDO".

En nuestro listado de ejemplo hemos querido que cada nombre salga en una página distinta. Al final de cada página de nombre escribimos un "subtotal" con el número de registros que hemos escrito para ese nombre.
Al final del listado escribiremos una linea de "totales" con el total de registros escritos.

El fichero de partida para este programa será el siguiente:
----+----1----+----2
ANA      LOPEZ
ANA      MARTINEZ
ANA      PEREZ
ANA      RODRIGUEZ
BEATRIZ  GARCIA
BEATRIZ  MOREDA
BEATRIZ  OTERO


Los ficheros que se usan en listados siempre están ordenados por algún campo. En nuestro caso por "nombre" y "apellido". En el JCL incluiremos el paso de SORT para ordenarlo.

Vamos allá!

Fichero de entrada desordenado:
----+----1----+----2

ANA      LOPEZ
BEATRIZ  MOREDA
ANA      PEREZ
BEATRIZ  OTERO
ANA      RODRIGUEZ
BEATRIZ  GARCIA
ANA      MARTINEZ


JCL:


//******************************************************
//******************** BORRADO *************************
//BORRADO EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
DEL FICHERO.NOMBRES.APELLIDO.ORDENADO
DEL FICHERO.CON.LISTADO
SET MAXCC = 0
//******************************************************
//* ORDENAMOS EL FICHERO POR NOMBRE Y APELLIDO *********
//SORT01 EXEC PGM=SORT
//SORTIN   DD DSN=FICHERO.NOMBRES.APELLIDO,DISP=SHR
//SORTOUT  DD DSN=FICHERO.NOMBRES.APELLIDO.ORDENADO,
//            DISP=(,CATLG),SPACE=(TRK,(50,10))
//SYSOUT   DD SYSOUT=*
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
  SORT FIELDS=(1,9,CH,A,10,10,CH,A)
//******************************************************
//*********** EJECUCION DEL PROGRAMA PRUEBA3 ***********
//PROG4 EXEC PGM=PRUEBA4
//SYSOUT  DD SYSOUT=*
//ENTRADA DD DSN=FICHERO.NOMBRES.APELLIDO.ORDENADO,DISP=SHR
//SALIDA  DD DSN=FICHERO.CON.LISTADO,
//           DISP=(NEW, CATLG, DELETE),SPACE=(TRK,(50,10)),
//           DCB=(RECFM=FBA,LRECL=133)
/*


En este JCL tenemos 3 pasos:
Paso 1: Borrado de ficheros que se generan durante la ejecución, visto ya en otros ejemplos.
Paso 2: Ordenación del fichero de entrada usando el SORT. Toda la información sobre el SORT la tenéis en SORT vol.1: SORT, INCLUDE.
Paso 3: Ejecución del programa que genera el listado. Tenemos como fichero de entrada el fichero de salida del SORT, y como fichero de salida ojito: indicaremos RECFM=FBA siempre para listados. Esto significa que el fichero contiene caracteres ASA, que son los que le indican a la impresora los saltos de línea que tiene que hacer al imprimir. Lo iremos viendo con el programa de ejemplo. La longitud del fichero(LRECL) suele ser 133, debido a que se imprimen en hojas A4 en formato apaisado.

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 LISTADO ASSIGN TO LISTADO
                 STATUS IS FS-LISTADO.
*
 DATA DIVISION.
*
 FILE SECTION.
*
* FICHERO DE ENTRADA DE LONGITUD FIJA (F) IGUAL A 20.
 FD ENTRADA RECORDING MODE IS F
            BLOCK CONTAINS 0 RECORDS
            RECORD CONTAINS 20 CHARACTERS.
 01 REG-ENTRADA PIC X(20).
*
* FICHERO DE LISTADO DE LONGITUD FIJA (F) IGUAL A 132.
 FD LISTADO RECORDING MODE IS F
            BLOCK CONTAINS 0 RECORDS
            RECORD CONTAINS 132 CHARACTERS.
 01 REG-LISTADO PIC X(132).
*
 WORKING-STORAGE SECTION.
*
* FILE STATUS
*
 01 FS-STATUS.
    05 FS-ENTRADA PIC X(2).
       88 FS-ENTRADA-OK  VALUE '00'.
       88 FS-ENTRADA-EOF VALUE '10'.
    05 FS-LISTADO PIC X(2).
       88 FS-LISTADO-OK  VALUE '00'.
*
* SWITCHES
*
 01 WB-FIN-ENTRADA PIC X(1) VALUE 'N'.
    88 FIN-ENTRADA VALUE 'S'.
*
* CONTADORES
*
 01 WC-LINEAS      PIC 9(2).
 01 WC-NOMBRES     PIC 9(2).
 01 WC-TOTALES     PIC 9(2).
*
* VARIABLES
*
 01 WX-REGISTRO-ENTRADA.
    05 WX-NOMBRE   PIC X(9).
    05 WX-APELLIDO PIC X(10).

 01 WX-NOMBRE-ANT  PIC X(9).

 01 WX-FEC-DDMMAA.
    05 WX-FEC-DD   PIC 9(2).
    05 FILLER      PIC X     VALUE '-'.
    05 WX-FEC-MM   PIC 9(2).
    05 FILLER      PIC X     VALUE '-'.
    05 WX-FEC-AA   PIC 9(2).

 01 WX-FECHA       PIC 9(6).
*
* REGISTRO LISTADO
*
 01 CABECERA1.
    05 FILLER      PIC X(40)
                   VALUE 'LISTADO DE EJEMPLO DEL CONSULTORIO
- 'COBOL'.
 01 CABECERA2.
    05 FILLER      PIC X(5)  VALUE 'DIA: '.
    05 LT-FECHA    PIC X(8).
    05 FILLER      PIC X(16) VALUE ALL SPACES.
    05 FILLER      PIC X(8)  VALUE 'PAGINA: '.
    05 LT-NUMPAG   PIC 9.
*
 01 CABECERA3.
    05 FILLER      PIC X(42) VALUE ALL '-'.
*
 01 SUBCABECERA1.
    05 FILLER      PIC X(6)  VALUE 'NOMBRE'.
    05 FILLER      PIC X(9)  VALUE ALL SPACES.
    05 FILLER      PIC X(8)  VALUE 'APELLIDO'.
*
 01 SUBCABECERA2.
    05 FILLER      PIC X(9)  VALUE ALL '-'.
    05 FILLER      PIC X(3)  VALUE ALL SPACES.
    05 FILLER      PIC X(15) VALUE ALL '-'.
*
 01 DETALLE.
    05 LT-NOMBRE   PIC X(9).
    05 FILLER      PIC X(3)  VALUE ALL SPACES.
    05 LT-APELLIDO PIC X(15).
*
 01 SUBTOTAL.
    05 FILLER      PIC X(6)  VALUE 'TOTAL '.
    05 LT-NOMTOT   PIC X(9).
    05 FILLER      PIC X(2)  VALUE ': '.
    05 LT-NUMNOM   PIC 9(2).
 01 TOTALES.
    05 FILLER      PIC X(15) VALUE 'TOTAL NOMBRES: '.
    05 LT-TOTALES  PIC 9(2).
*
************************************************************
 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 DEL FICHERO DE ENTRADA
* 3| INFORMAMOS CABECERA Y ESCRIBIMOS CABECERA
************************************************************
 10000-INICIO. 
*
     INITIALIZE DETALLE 
                WX-REGISTRO-ENTRADA

     PERFORM 11000-ABRIR-FICHEROS 
     PERFORM LEER-ENTRADA 

     IF FIN-ENTRADA 
        DISPLAY 'FICHERO DE ENTRADA VACIO' 
        
        PERFORM 30000-FINAL 
     END-IF 

     MOVE WX-NOMBRE TO WX-NOMBRE-ANT 

     PERFORM INFORMAR-CABECERA 
     PERFORM ESCRIBIR-CABECERAS 
     .

************************************************************ 
* 11000 - ABRIR FICHEROS 
*--|------------------+----------><----------+-------------* 
* ABRIMOS LOS FICHEROS DEL PROGRAMA ************************************************************
 11000-ABRIR-FICHEROS. 

     OPEN INPUT ENTRADA 
         OUTPUT LISTADO 

     IF NOT FS-ENTRADA-OK 
        DISPLAY 'ERROR EN OPEN DE ENTRADA:'FS-ENTRADA 
     END-IF 

     IF NOT FS-LISTADO-OK 
        DISPLAY 'ERROR EN OPEN DE LISTADO:'FS-LISTADO 
     END-IF 
     . 

************************************************************ 
* 12000 - INFORMAR CABECERA 
*--|------------------+----------><----------+-------------* 
* INFORMAMOS EL CAMPO FECHA DE LA CABECERA ************************************************************
 INFORMAR-CABECERA. 

     ACCEPT WX-FECHA FROM DATE 

     MOVE WX-FECHA(1:2) TO WX-FEC-AA 
     MOVE WX-FECHA(3:2) TO WX-FEC-MM 
     MOVE WX-FECHA(5:2) TO WX-FEC-DD 
     MOVE WX-FEC-DDMMAA TO LT-FECHA 
* Inicializamos el contador de páginas
     MOVE 1             TO LT-NUMPAG 
     . 

************************************************************ 
* | 20000 - PROCESO 
*--|------------------+----------><------------------------* 
* | SE REALIZA EL TRATAMIENTO DE LOS DATOS: 
* 1| ESCRIBIMOS LAS LINEAS DE DETALLE Y CONTROLAMOS LOS
* | SALTOS DE PAGINA
************************************************************
 20000-PROCESO. 

     INITIALIZE DETALLE 

     IF WC-LINEAS GREATER 64 
        ADD 1 TO LT-NUMPAG 

        PERFORM ESCRIBIR-CABECERAS 
     END-IF 

     IF WX-NOMBRE NOT EQUAL WX-NOMBRE-ANT
        PERFORM ESCRIBIR-SUBTOTAL 

        ADD 1       TO LT-NUMPAG 

        PERFORM ESCRIBIR-CABECERAS 

        MOVE ZEROES TO WC-NOMBRES 
     END-IF 

     PERFORM 21000-INFORMAR-DETALLE 
     PERFORM ESCRIBIR-DETALLE 

     MOVE WX-NOMBRE TO WX-NOMBRE-ANT 
     
     PERFORM LEER-ENTRADA 
     . 

************************************************************ 
* ESCRIBIR CABECERAS 
*--|------------------+----------><----------+-------------* 
* ESCRIBE LA CABECERA Y SUBCABECERA DEL LISTADO ************************************************************
 ESCRIBIR-CABECERAS. 

     WRITE REG-LISTADO FROM CABECERA1 AFTER ADVANCING PAGE 
     WRITE REG-LISTADO FROM CABECERA2 
     WRITE REG-LISTADO FROM CABECERA3 
     WRITE REG-LISTADO FROM SUBCABECERA1 AFTER ADVANCING 2 LINES 
     WRITE REG-LISTADO FROM SUBCABECERA2 

     MOVE 6            TO WC-LINEAS 
     . 

************************************************************ 
* ESCRIBIR SUBTOTAL 
*--|------------------+----------><----------+-------------* 
* ESCRIBIMOS LINEA DE SUBTOTAL ************************************************************
 ESCRIBIR-SUBTOTAL. 

     MOVE WX-NOMBRE-ANT TO LT-NOMTOT 
     MOVE WC-NOMBRES    TO LT-NUMNOM 

     WRITE REG-LISTADO FROM SUBTOTAL AFTER ADVANCING 2 LINES 
     . 

************************************************************ 
* 21000 INFORMAR DETALLE 
*--|------------------+----------><----------+-------------* 
* INFORMAMOS LOS CAMPOS DE LA LINEA DE DETALLE CON LA 
* INFORMACION DEL FICHERO DE ENTRADA ************************************************************
 21000-INFORMAR-DETALLE. 

     MOVE WX-NOMBRE   TO LT-NOMBRE 
     MOVE WX-APELLIDO TO LT-APELLIDO 
     . 

************************************************************ 
* ESCRIBIR DETALLE 
*--|------------------+----------><----------+-------------* 
* ESCRIBIMOS LA LINEA DE DETALLE ************************************************************
 ESCRIBIR-DETALLE. 

     WRITE REG-LISTADO FROM DETALLE 

     IF NOT FS-LISTADO-OK 
        DISPLAY 'ERROR AL ESCRIBIR DETALLE:'FS-LISTADO 
     END-IF 

     ADD 1       TO WC-LINEAS 
     ADD 1       TO WC-NOMBRES 
     ADD 1       TO WC-TOTALES 
     . 

************************************************************ 
* 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 
     . 

************************************************************ 
* | 30000 - FINAL 
*--|------------------+----------><----------+-------------* 
* | FINALIZA LA EJECUCION DEL PROGRAMA ************************************************************
 30000-FINAL. 

     IF WC-LINEAS GREATER 60 
        ADD 1     TO LT-NUMPAG 

        PERFORM ESCRIBIR-CABECERAS 
        PERFORM ESCRIBIR-SUBTOTAL 
        PERFORM ESCRIBIR-TOTALES 
     ELSE 
        PERFORM ESCRIBIR-SUBTOTAL 
        PERFORM ESCRIBIR-TOTALES 
     END-IF 

     PERFORM 31000-CERRAR-FICHEROS 

     STOP RUN 
     . 

************************************************************ 
* | ESCRIBIR TOTALES 
*--|------------------+----------><----------+-------------* 
* | ESCRIBIMOS LA LINEA DE TOTALES DEL LISTADO ************************************************************
 ESCRIBIR-TOTALES. 

     MOVE WC-TOTALES   TO LT-TOTALES 

     WRITE REG-LISTADO FROM TOTALES 
     . 

************************************************************ 
* | 31000 - CERRAR FICHEROS 
*--|------------------+----------><----------+-------------*
* | CERRAMOS LOS FICHEROS DEL PROGRAMA
************************************************************

 31000-CERRAR-FICHEROS.
*
     CLOSE ENTRADA
           LISTADO
 

     IF NOT FS-ENTRADA-OK
        DISPLAY 'ERROR EN CLOSE DE ENTRADA:'FS-ENTRADA
     END-IF
 

     IF NOT FS-LISTADO-OK
        DISPLAY 'ERROR EN CLOSE DE LISTADO:'FS-LISTADO
     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.
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.
EVALUATE TRUE: "Evalúa" si los niveles 88 por los que preguntamos en el "WHEN" están activados a "TRUE".
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 entrada, 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.
Leemos el primer registro del fichero de entrada y controlaremos el file-status.
Además comprobamos que el fichero de entrada no venga vacío (en caso de que así sea, finalizamos la ejecución). Si todo ha ido OK guardaremos el nombre leído del fichero de entrada en WX-NOMBRE-ANT para controlar posteriormente el momento en que cambiemos de nombre.
Informaremos la parte genérica de la cabecera e inicializamos el contador de páginas. Escribimos la cabecera por primera vez.

INFORMAR-CABECERA:
Recoge la fecha del sistema mediante un ACCEPT. La variable "DATE" es la fecha del sistema en formato 9(6) AAMMDD.
Para informar la fecha del listado formateamos la recibida del sistema a formato DD-MM-AA.

ESCRIBIR-CABECERAS:
Escribe las lineas de cabecera CABECERA1, CABECERA2, CABECERA3, SUBCABECERA1 y SUBCABECERA2.
En el WRITE de CABECERA1 vemos que utilizamos el "AFTER ADVANCING PAGE", esto significa que esta línea se escribirá en una página nueva. Lo podemos ver en el caracter ASA que aparece a la izquierda de esta linea en el fichero de salida que será un '1'.
En el WRITE de SUBCABECERA1 vemos que utilizamos "AFTER ADVANCING 2 LINES", esto significa que se escribirá una línea en blanco y después la línea de SUBCABECERA1. El caracter ASA que aparecerá será un '0'.
Informamos el contador de líneas a '6', pues la cabecera ocupa 6 líneas.

En el párrafo de proceso, que se repetirá hasta que se termine el fichero de entrada (FIN-ENTRADA), controlaremos:
Por un lado el número de líneas escritas, y en caso de superar un máximo (64 en nusetro caso) escribiremos otra vez las cabeceras en una página nueva (ver párrado ESCRIBIR-CABECERAS).
Por otro lado la variable WX-NOMBRE, pues queremos escribir cada nombre en una página distinta. Cuando cambie el nombre escribiremos la línea de subtotales y volveremos a escribir cabeceras (que nos hará el salto de página).

Informaremos la linea de detalle y escribiremos en el fichero del listado.
Guardamos el último nombre escrito en WX-NOMBRE-ANT para controlar el cambio de nombre.
Leemos el siguiente registro del fichero de entrada.

Llamadas a párrafos:
ESCRIBIR-SUBTOTAL:
Informará el campo LT-NOMTOT con el nombre que hemos estado escribiendo, y LT-NUMNOM con el contador de registros escritos para ese nombre.
Escribirá SUBTOTALES dejando antes una línea en blanco (AFTER ADVANCING 2 LINES).

21000-INFORMAR-DETALLE:
Informamos los campos de la línea de detalle LT-NOMBRE y LT-APELLIDO con los campos de lfichero de entrada WX-NOMBRE y WX-APELLIDO.

ESCRIBIR-DETALLE:
Escribe la linea de detalle en el fichero del listado. Controlamos file-status y añadimos uno a los contadores.

En el párrado de FINAL, finalizaremos el programa escribiendo la línea de subtotales que falta, y la línea de totales generales.
Controlaremos el número de líneas que llevamos escritas, pues para escribir la línea SUBTOTALES y TOTALES necesitamos 3 líneas.
En el párrafo de ESCRIBIR-TOTALES informaremos el campo LT-TOTALES con el contador de registros escritos en total.

Al inicio del artículo veiamos como quedaría el listado una vez "impreso", ya sea por pantalla o en papel. Vamos a ver como quedaría el fichero con los caracteres ASA que indican los saltos de linea.

Fichero de salida:
----+----1----+----2----+----3----+----4----+
1LISTADO DE EJEMPLO DEL CONSULTORIO COBOL
 DIA: 22-03-11                  PAGINA: 1
 ------------------------------------------
0NOMBRE         APELLIDO
 ---------   ---------------
 ANA         LOPEZ
 ANA         MARTINEZ
 ANA         PEREZ
 ANA         RODRIGUEZ
0TOTAL ANA : 04
1LISTADO DE EJEMPLO DEL CONSULTORIO COBOL
 DIA: 22-03-11                  PAGINA: 2
 ------------------------------------------
0NOMBRE         APELLIDO
 ---------   ---------------
 BEATRIZ     GARCIA
 BEATRIZ     MOREDA
 BEATRIZ     OTERO
0TOTAL BEATRIZ : 03
 TOTAL NOMBRES: 07


Donde:
1 = salto de página.
0 = deja una línea en blanco justo antes.
- = deja dos líneas en blanco.
espacio = escribe sin dejar lineas en blanco.

Y listo! Si veis que no me he parado mucho en alguna cosa y queréis que explique más en detalle, dejad un comentario y lo vemos : )

lunes, 21 de marzo de 2011

Lo mejor de la semana: lunes 14 a domingo 20.

En el Consultorio Cobol seguimos creciendo.
Por un lado en miembros, pues el viernes dimos la bienvenida a Trafuga, que nos trajo el toque de humor más friki^^
Por otro lado en cifras, pues el amigo google analytics nos dice que esta última semana ha sido la mejor desde que empezamos, y esperemos que siga subiendo!

Y ahí va lo más leido:

  1. Sort vol.2: OUTREC.
  2. Programas con DB2 I: SELECT, INSERT, UPDATE y DELETE.
  3. Errores de JCL.
  4. Errores DB2.
  5. Esquema de un programa cobol.
  6. Errores de ficheros.
  7. LOAD y UNLOAD: carga y descarga.
  8. WORKING-STORAGE: definiendo variables.
  9. JCL básico III: sentencia EXEC.
  10. SQL en JCL.

viernes, 18 de marzo de 2011

CONSULTIRAS 1: "El loco del Cobol"

Saludos amantes del COBOL (y entornos MAINFRAME en general).
Aqui estoy, como nuevo acoplado al Consultorio para presentaros esta nueva sección de la que me encargaré, se trata de las CONSULTIRAS.
Porque no todo es el trabajo y quebraderos de cabeza, procuraré arrancaros unas risas con las tiras cómicas, relacionadas con nuestro mundillo.

No quiero ser un pesado, por lo que os dejo con la primera tira, espero que os guste.

(Pincha en la imagen para verla más grande)

miércoles, 16 de marzo de 2011

EASYTRIEVE(I): Cruce ficheros 1:1

Este es le primero de una serie de artículos que presentaremos para explicar esta potente herramienta para el manejo de ficheros.

Empezaremos con un programa sencillo un cruce 1:1 de dos ficheros, para ello examinaremos el siguiente código EASYTRIEVE.

Supongamos el siguiente problema: Determinar que productos ha pedido al almacén un determinado empleado.

Para ellos contaremos con dos ficheros de información.
Fichero IN1 en el que tenemos la información de solicitudes de productos de empleados (producto viene especificado por su código):

FILE IN1
   IN1-NUMCLI          1    4 N 0
   IN1-NUMPROD         5    3 N 0
   IN1-FECALTA         8   10 A

Fichero IN2 en el que tenemos la información de las descripciones de los distintos productos:

FILE IN2
   IN2-NUMPROD         1    3 N 0
   IN2-DESPROD         4   14 A

Nuestro fichero de salida tendrá las decripciones de los productos de las solicitudes hechas por un empleado (lo que no se nos planteaba en el problema):

FILE OUT
   OUT-NUMCLI          1    4 N 0
   OUT-NUMPROD         5    3 N 0
   OUT-DESPROD         8   14 A

Vemos que se trata de un cruce 1:1 donde el campo clave (campo por el que se hace el cruce es el código del producto)
En el siguiente código se especifica los campos por los cuales se hace el enfrentamiento de los ficheros:

JOB INPUT (IN1 KEY (IN1-NUMPROD) +
          IN2 KEY (IN2-NUMPROD)) +
    FINISH PFINAL

Con la sentencia MATCHED tendremos todos aquellos registros donde el NUMPROD de IN1 = NUMPROD de IN2:

IF MATCHED
   OUT-NUMCLI = IN1-NUMCLI
   OUT-NUMPROD = IN1-NUMPROD
   OUT-DESPROD = IN2-DESPROD
   PUT OUT
END-IF
PFINAL . PROC
  DISPLAY '******************** FINAL ********************'
END-PROC

La sentencia PUT nos escribe en el fichero de salida

Nuestro problemas quedaría resuelto, pero vamos ir un poco mas allá, extrayendo un poco mas de información, para ver las posibilidades que nos ofrece EASYTRIEVE de una manera sencilla y muy rápida

IF NOT MATCHED Con esta sentencia tendremos todos aquellos registros donde el NUMPROD de IN1 <> NUMPROD de IN2

IF NOT MATCHED
   IF IN1 Con esta sentencia tendremos todos aquellos registros donde el NUMPROD de IN1 < NUMPROD de IN2
   IF IN2 Con esta sentencia tendremos todos aquellos registros donde el NUMPROD de IN1 > NUMPROD de IN2

Todo esto lo podemos ver mas claro viendo el siguiente ejemplo:
Supongamos el siguiente cruce de ficheros

 SECUEN.   IN1   IN2    

--------  ----- ------   
   1       1    N/A     
   2       2     2      
   3       3     3     
   4       4     4                
   5       5    N/A     
   6      N/A    6     
   7      N/A    7     
   8      N/A    8   
   9      N/A    9      
  10      N/A    10    
  11      11    N/A     
  12      12    N/A     
  13      13    N/A          
  14      14    N/A 

IF MATCHED Nos devolvería los registros cuyos secuenciales son: 2, 3, 4 

IF NOT MATCHED
   IF IN1 Nos devolvería los registros cuyos secuenciales son: 1, 5, 11, 12, 13, 14
   IF IN2 Nos devolvería los registros cuyos secuenciales son: 6, 7, 8, 9, 10

Esto es una manera sencilla y rápida de hacer un cruce de dos ficheros 1 a 1. En posteriores artículos veremos como tratar otro tipo de cruces 1:n y n:n.

En breve colgaremos para descargar el JCL, los ficheros de entrada, y el fichero de salida.

Y ahí lo tenéis:
JCL con easytrieve.

lunes, 14 de marzo de 2011

JCL Básico III: Sentencia EXEC

Sentencia EXEC


Determina el inicio de un paso de trabajo y el programa o procedimiento catalogado a ejecutar . El máximo de sentencias autorizado para un JOB es de 255
   //PASO001 EXEC PGM=NAMEPGM,REGION=10M,
   //    TIME=(30,00),COND=(0,NE),
   //     ACCT=(contabili.),DYNAMNBR=10,
   //     PARM=("DESCRIPCON"),RD=NC,
   //     ADDRSPC=REAL


Sentencia que indica el programa o procedimiento que se desea ejecutar.
  • Parametros Posicionales. Se considera parámetro posicional de la sentencia EXEC la especificación del nombre del programa o procedimiento a ejecutar:
    • PGM=Nombre_del_programa. Se aplica cuando el paso deberá ejecutar un programa, asociando el nombre del  programa a ejecutar.
    • PROC=Se aplica cuando el paso deberá ejecutar un procedimiento, asociando el nombre del  procedimiento a ejecutar.
  • Parametros de Palabra Clave. A diferencia de los parámetros PROC y PGM que decíamos que había de ser uno de los dos el primero en codificarse en la ficha EXEC los restantes pueden codificarse en cualquier orden  (Todos ellos son opcionales):
    • ACCT - especifica la información contable relativa al paso, exigida por la instalación.
    • ADDRSPC Especifica si debemos ejecutar el paso en memoria real o virtual 
    • COND  especifica bajo qué condiciones no se ejecutará el paso.Tiene algunas peculiaridades que no se aplican en la sentencia JOB y son:


      COND=
      {
      (0,EQ)
      (0,EQ,PAS01)
      }


      COND=
      {
      EVEN
      ONLY
      }






      ONLY - 
      Solo se ejecuta si alguno de los pasos anteriores finalizase de manera incorrecta.

      EVEN - 
      El paso se ha de ejecutar siempre, aunque los anteriores acaben de forma incorrecta
    • //PRUEBA1  JOB  PPPPP,’J.MIR’,MSGCLASS=V,CLASS=C,
      //                  NOTIFY=T515214
      //*
      //PAS01      EXEC PROC=AAAP000...
      //PAS02      EXEC PROC=FINMALO,COND=(0,EQ,PAS01),....
      //PAS03      EXEC PROC=FINMALO,COND=ONLY,...
      //PAS04      EXEC PROC=SIFIN,COND=EVEN,...
    • DPRTY Determina la prioridad de ejecución del JOB en relación a otros en cola de ejecución.  El parámetro consta de dos subparámetros posicionales numéricos, el primero con valores comprendidos entre 0 y 14 y el segundo entre 0 y 15. La prioridad final se obtiene del producto de 16x (valor 1)+(valor 2)

      //TRAB1   JOB    DEPEXT,PUIG,MSGCLASS=V,CLASS=C,NOTIFY=T515214,
      //                            DPRTY =(10,5),...
      SE RECOMIENDA NO UTILIZAR ESTE PARÁMETRO A NO SER POR CAUSAS JUSTIFICADAS Y ACEPTAR LA PRIORIDAD QUE ASIGNA EL JES 2 PARA PRIORIDADES SEGÚN LAS CLASES DE EJECUCIÓN.           
      EL PARÁMETRO PRTY PARA LA FICHA JOB ES EL EQUIVALENTE A ESTE Y SU CODIFICACIÓN ANULA LA DE LA FICHA EXEC.

    • DYNAMNBR - éste parámetro está asociado con la alocación dinámica de ficheros y no se debe codificar en otras circunstancias. Alocación dinámica es aquella que efectuamos por medio del programa en ejecución que marca la pauta de qué ficheros debe, o no, coger o crear . Cada fichero a crear será una unidad de asignación al parámetro. Debe de asociarse el paso al parámetro para que el JOB sepa en qué momento debe de reservar recursos para la asignación dinámica de ficheros. El número máximo de ficheros a poder asignar de forma dinámica es de 1635.
      //TRAB1   JOB    DEPEXT,PUIG,MSGCLASS=V,CLASS=C,NOTIFY=T515214
      //*
      //PAS01    EXE  PGM=AAAP000,DYNAMNBR=10,...

      La alocación dinámica suele especificarse en la sentencia EXEC o por medio de una DD (dynam).
      Este parámetro tiene equivalencia al de igual nombre para la ficha JOB.(la codificación del parámetro en JOB anula la especificada en EXEC).
    • PARM - se utiliza para pasar algun tipo de información al programa que está en proceso. Se pueden pasar como máximo 100 caracteres.
    • //TRAB1   JOB    DEPEXT,PUIG,MSGCLASS=V,CLASS=C,NOTIFY=T515214
      //*
      //PAS01    EXEC    PGM=AAAP000,PARM=‘/ &CADENA’
      CODIFICACIÓN EN PGM PL/I DE RECEPCIÓN DE PARÁMETROS:
      AAAP000:  PROC (PARM) OPTIONS(MAIN)
      DCL PARM CHAR (100) VAR;
      DCL 1 WPARM           BASED(ADDR(PARM)),
            3 LONGIT        BIN FIXED(15),   
            3 CADENA        CHAR(8);
    • PERFORM Grupo de rendimiento al que se aplica al paso
      PERFORM=numero con valores comprendidos entre 1 y 255. Por defecto asume 1 para pasos de JOB no TSO y 2 para JOBs de TSO
      //TRAB1   JOB    DEPEXT,PUIG,MSGCLASS=V,CLASS=C,
      //                            PERFORM=2
    • RD - controla las posibilidades de rearranque del paso en el que esta codificado.

      • RD=R Rearranque automático combinado con la macro CHKPT
      • RD=RNC Rearranque automático a nivel de paso (no de checkpoint)
      • RD=NC No se permiten rearranques automáticos
      • RD=NR No permite arranque automático, pero se puede tomar checkpoint para el arranque posterior

    • REGION - especifica la cantidad de memoria que puede utilizar el paso.
      En el caso de trabajar con memoria real ADDRSPC=REAL indica el total de memoria a utilizar
      En el caso de trabajar con memoria virtual ADDRSPC=VIRT evita el exceso de paginación cuando se codifican valores bajos
      La medida se expresa en valor superior a cero en número par y como unidad K(1024 octetos)
      //TRAB1   JOB    DEPEXT,PUIG,MSGCLASS=V,CLASS=C,NOTIFY=T515214
      //*
      //PAS01     EXEC  PGM=AAAP000,REGION=1024K

    • TIME - Especifica el tiempo máximo de CPU(tiempo en que el procesador utiliza recursos)  autorizado para la ejecución del  paso.

      TIME=([minutos|),[segundos|)

      Ambos requieren valores numéricos el primero en rango comprendido entre 1y 1439 y el segundo con valor entre 1 y 59 .Ejemplos TIME=1, TIME=(1,20), TIME=(,40)
      Time=1440 (24 horas) presupone que no hay limite de CPU para ese JOB              

      Para indicar 15 segundos:
      //TRAB1   JOB    DEPEXT,PUIG,MSGCLASS=V,CLASS=C
      //*
      //PAS01     EXEC  PGM=AAAP000,TIME=(,15)
      La ausencia de codificación de este parámetro presupone el trabajar con el tiempo limitado por la instalación.