Desarrollando en Cobol y Natural sobre Plataforma Mainframe

lunes, 28 de septiembre de 2015

Subquery para recuperar varios campos a la vez

Del mismo modo que podemos obtener un campo individual, las subqueries SQL también nos sirven para recuperar varios campos a la vez de las tablas DB2. Esta operativa nos será útil en numerosas ocasiones, ya que nos permitirá ahorrarnos gran parte del código que tendríamos que emplear para extraer los datos uno a uno.

Subquery para recuperar varios campos a la vez


En general, cuando codificamos una subquery (una query anidada dentro de otra query SQL), lo que solemos hacer es crear una sentencia que nos permita acceder a un dato concreto. Este tipo de acceso habrá sido utilizado varias veces por cualquier programador Cobol que lleve más de un año utilizando el QMF de SQL.


Un ejemplo del tipo de subquery SQL DB2 sencilla al que me refiero es el que os incluyo aquí a continuación, donde la utilizamos para recuperar el campo SITUACION.

SELECT * FROM JJTRAMT0 A
WHERE A.SITUACION IN                             
     (SELECT DISTINCT SITUACION                     
      FROM JJLOCAT0                                      
      WHERE LOCALIZACION = ‘VITORIA’) 


Pues bien, en algunas ocasiones nos vamos a encontrar con problemas más complejos en el mantenimiento de nuestra aplicación y, en esos casos, nos ahorraremos bastante trabajo si implementamos una query que sea capaz de recuperar más de un campo del fichero DB2. En el ejemplo anterior, este escenario podría ser el de intentar recuperar mediante la subquery los campos SITUACION y LOCALIZACION del fichero LOCA.

Es posible que, en vuestros clientes, nunca lleguéis a encontraros en una situación en la que tengáis la necesidad de hacer una subquery tan elaborada. A pesar de todo, nunca está de más saber cómo se hace. Quizás os sea útil en el futuro.

¿Cómo recuperar varios campos a la vez del DB2?


Implementar una subquery que recupere varios campos del fichero DB2 a la vez es un procedimiento que, una vez conocido, resulta bastante sencillo de replicar. Digamos que la complejidad de este tipo de subquery tiene su origen en que no suele utilizarse asiduamente y no en que sea algo intrínsecamente complicado de desarrollar.


Para elaborar este tipo de estructura simplemente tendremos que recurrir al uso del operador || en la subquery. Dicho operador sirve, precisamente, para combinar varios campos DB2 y permitir su recuperación conjunta desde una query hija hacia su query padre. A continuación, podemos ver un ejemplo de este tipo de codificación.

SELECT * FROM JJTRAMT0 A
WHERE A.SITUACION || A.LOCALIZACION IN              
    (SELECT SITUACION || LOCALIZACION

      FROM JJLOCAT0                                      
      WHERE LOCALIZACION = ‘VITORIA’)  


Como se puede observar, en el caso anterior estamos obteniendo los campos SITUACION y LOCALIZACION en la subquery. Estos datos se combinan mediante el operador || y, posteriormente, se transfieren a la query padre en la cláusula SITUACION || LOCALIZACION.

Esta implementación nos permitirá operar en la query padre con los campos SITUACION y LOCALIZACION cuyo contenido coincida con el de los campos SITUACION y LOCALIZACION existentes en la subquery. Dicho de otro modo, la query padre recuperará los registros del fichero TRAM cuyos valores en los campos SITUACION y LOCALIZACION coincidan con alguno de los valores recuperados del fichero LOCA mediante la subquery.

Obviamente, el código anterior se puede complicar tanto como se precise, pero la idea básica de utilización del operador || es la mostrada en las sencillas declaraciones anteriores. De todas formas, si tenéis alguna duda dejad la pregunta en los comentarios e intentaré responderos en cuanto tenga oportunidad.


Conclusiones acerca del operador ||


En general, en las subqueries que construyamos nos bastará con recuperar un único campo de los ficheros DB2. Pero, tal y como hemos visto en el post, siempre que sea necesario, el operador || nos dará la oportunidad de transferir varios campos a la vez desde una subquery hasta su query padre. Aunque hasta ahora no os hayáis encontrado con dicha situación, es posible que este conocimiento os sea útil en el futuro.

Pues nada, no queda mucho más que decir acerca de este tipo especial de subquery. Espero que lo comentado os haya servido para aprender algo nuevo y útil para vuestro trabajo.

Saludos.

lunes, 21 de septiembre de 2015

ICETOOL: Como cruzar Ficheros con SPLICE (y 2)

Hace algunas semanas comenzamos a ver cómo se podía realizar un cruce de ficheros en un JCL mediante el comando SPLICE de la herramienta ICETOOL. Hoy completaremos dicha revisión, de manera que, a la conclusión del post, ya deberíamos ser capaces de crear nuestro propio Job para realizar este tipo de procesamiento.

En la primera parte del artículo empezamos a examinar cuáles eran las fichas que había que incluir en el paso 1 del JCL, esto es, en el paso necesario para reformatear los ficheros de entrada (ver post ICETOOL - Como cruzar Ficheros con SPLICE - 1). En esta segunda parte nos concentraremos en revisar las fichas correspondientes al paso 2 del JCL, en el que se procederá a generar los ficheros de salida.

PASO 2: PASO ICETOOL PARA GENERAR SALIDAS


6º) Invocación a ICETOOL: Al iniciar el paso, como siempre, se debe realizar la invocación a la herramienta ICETOOL mediante el comando EXEC PGM.

//JJ0107   EXEC PGM=ICETOOL

7º) Ficheros de entrada: En esta ficha especificamos como entrada el fichero CONCAT, que será la unión de los dos ficheros indicados en el código: SIST.JJ0105T1.DDD.X0060X.POBL.T1 y SIST.JJ0105T2.X0060X.DDD.POBL.T2. Se puede apreciar que estos dos datasets son los ficheros de salida del Paso anterior del JCL.

//* CONCAT: FICHERO UNIFICADO PARA REALIZAR EL ICETOOL        
//CONCAT   DD  DSN=SIST.JJ0105T1.DDD.X0060X.POBL.T1,
//             DISP=SHR                             

//         DD  DSN=SIST.JJ0105T2.X0060X.DDD.POBL.T2,
//             DISP=SHR  
                    

8º) Ficheros de salida: Aquí se deben indicar los nombres de los ficheros de salida de la herramienta ICETOOL, que contendrán los registros modificados por el operador. En nuestro ejemplo son el SIST.JJ0107S1.X0060X.DDD.POBL.MD (que contiene los registros del MASTER modificados según la relación del KEYS) y el SIST.JJ0107S1.X0060X.DDD.POBL.RS (que contiene los registros que no han sido modificados en la operación).

//* OUT  : FICHERO SALIDA CON LOS REGISTROS MODIFICADOS         
//OUT      DD  DSN=SIST.JJ0107S1.X0060X.DDD.POBL.MD,
//             DISP=(NEW,CATLG,DELETE),             

//             SPACE=(CYL,(00100,00100),RLSE),              //             UNIT=DISCO
//* OUT1 : FICHERO SALIDA CON LOS REGISTROS NO MODIFICADOS
//OUT1     DD  DSN=SIST.JJ0107S2.X0060X.DDD.POBL.RS,  
//             DISP=(,CATLG,DELETE),                       
//             SPACE=(CYL,(00100,00100),RLSE),              
//             UNIT=DISCO

                                     

9º) Ficha TOOLIN: Aquí es donde tenemos que indicarle a la utilidad cuáles son las acciones que debe realizar. En nuestro caso, como estamos haciendo cruce de ficheros, tendremos que usar el operador SPLICE.

En la primera sentencia indicamos que, partiendo del fichero CONCAT, se debe transferir la información al fichero OUT realizando el cruce por el campo de longitud 6 que empieza en la posición 1 (Población). La forma de realizar dicho cruce se especificará en la ficha de control CTL3.

La cláusula WITHALL se emplea para que, en el caso de que una clave esté más de una vez en el fichero Maestro de Poblaciones (POBL.T2), se recojan todos los registros que la contengan. Si no se especifica WITHALL, entonces el filtrado se quedará únicamente con el primer registro encontrado. Por otra parte, al no incluir los comandos KEEPNODUPS y KEEPBASE, sólo saldrán en la salida los registros cuyas claves existan en ambos ficheros (POBL.T1 y POBL.T2).

Del mismo modo, en la segunda sentencia indicamos que, partiendo del fichero CONCAT, se debe transferir la información al fichero OUT1 realizando el cruce por el campo de longitud 6 que empieza en la posición 1 (Población). La forma de realizar dicho cruce se especificará en la ficha de control CTL4.


Las cláusulas KEEPNODUPS y KEEPBASE se emplean para mostrar en el fichero de salida los registros del fichero POBL.T1 cuya clave no existe en el fichero POBL.T2. Es decir, con estas cláusulas le indicamos al filtrado que nos saque toda la información de los ficheros, incluidos los registros de POBL.T1 cuyas claves no se localicen en el Maestro de Poblaciones (POBL.T2).

*--------------------------------------------------------*
* <<< FICHA JJRS1A06 >>>
* OUT: REGISTROS QUE COINCIDEN                           *
* INCLUIMOS TODO DEL MASTER MAS POBLACION NUEVA DE KEY   *
* MAS UN INDICADOR NUEVO DE MODIFICADO
* OUT1: RESTO DE REGISTROS QUE NO COINCIDEN              *
*--------------------------------------------------------*
  SPLICE FROM(CONCAT) TO(OUT) ON(1,6,CH) WITHALL  -
  WITH(7,40) USING(CTL3)                         
  SPLICE FROM(CONCAT) TO(OUT1) ON(1,6,CH) WITHALL  -
  WITH(7,40) USING(CTL4) KEEPNODUPS KEEPBASE   
    


10º) Fichas de Control: En esta tarjeta es donde tenemos que indicar cómo queremos que se realice la función SPLICE de la herramienta ICETOOL. En nuestro ejemplo, aquí estarían incluidas las fichas CTL3 y CTL4.

*--------------------------------------------------------*
* <<< FICHA JJRS1A07 >>>
* SUSTITUIMOS POBL ENCONTRADOS POR POBL NUEVAS
* SE INCLUYE INDICADOR DE MODIFICADO
*--------------------------------------------------------*
  OUTFIL FNAMES=OUT,
  INCLUDE=(47,1,CH,EQ,C'D'),
  OUTREC=(7,2,48,6,15,32)   



    
Esta primera ficha de control (CTL3) hace referencia al primer comando SPLICE de las sentencias del TOOLIN anterior. Como podemos ver en su codificación, en ella se hacen tres cosas con los datos de entrada.

-          OUTFIL: Se indica que la información se debe enviar desde el fichero de entrada CONCAT al fichero de salida OUT.

-          INCLUDE: Se establece la Condición de Filtrado. En esta sentencia se indica que debemos quedarnos únicamente con aquellos registros que en la posición 47 tengan un campo alfanumérico de longitud 1 igual a ‘D’ (INCLUDE=(47,1,CH,EQ,C’D’)).

-          OUTREC: A continuación, transfiere del CONCAT al OUT la siguiente información de los registros: el campo de longitud 2 que empieza en la posición 7 (7,2), el campo de longitud 6 que empieza en la posición 48 (la Población nueva) y el campo de longitud 32 que empieza en la posición 15 (15,32).

*--------------------------------------------------------*
* <<< FICHA JJRS1A08 >>>
* SACAMOS REGISTROS NO ENCONTRADOS PARA CUADRAR ENTRADA
*--------------------------------------------------------*
  OUTFIL FNAMES=OUT1,
  INCLUDE=(47,1,CH,EQ,C' '),
  OUTREC=(7,40)
 
                            


La segunda ficha de control (CTL4) hace referencia al segundo comando SPLICE de las sentencias de la TOOLIN especificada anteriormente. Al igual que en la ficha CTL3 anterior, aquí también se realizan tres acciones sobre los registros de entrada.

-          OUTFIL: Se indica que la información se debe enviar desde el fichero de entrada CONCAT al fichero de salida OUT1.

-          INCLUDE: Se establece la Condición de Filtrado. Debemos quedarnos únicamente con aquellos registros que en la posición 47 tengan un campo alfanumérico de longitud 1 igual a un espacio en blanco (INCLUDE=(47,1,CH,EQ,C’ ’)).

-          OUTREC: A continuación, transfiere del CONCAT al OUT1 el campo de longitud 40 que empieza en la posición 7 (7,40).

Una vez especificados todos los puntos anteriores, ya se podrían ejecutar sin problemas los dos pasos JCL comentados. Para personalizar estas fichas, recordad que tendríamos que cambiar tanto los nombres de los ficheros de entrada y de salida como la ubicación de los campos (de los registros) indicados en las sentencias asociadas.



Ejecución de un JCL con SPLICE


Para ver el impacto que tendría la ejecución de un operador SPLICE, podemos ver cómo irían quedando los ficheros que se generen en los dos pasos JCL anteriores. Recordemos que inicialmente partimos del fichero con la relación clave antigua – clave nueva SIST.UNLOAD.X0005X.POBL.KEYS y del fichero de poblaciones SIST.UNLOAD.X0005X.POBL.MASTER.

Tras la ejecución del primer paso, se generan dos ficheros de salida, con denominación SIST.JJ0105T1.X0060X.DDD.POBL.T1 y SIST.JJ0105T2.X0060X.DDD.POBL.T2. El fichero POBL.T1 es el fichero de relaciones entre claves (KEYS) al que, al final, se ha añadido un literal ‘D’ y el código de la clave nueva de la población.

----+----1----+----2----+----3----+----4----+----5---
***************************** Top of Data ***********
000002-100002                                 D100002
000004-100004                                 D100004
000005-100005                                 D100005
000006-100006                                 D100006
000007-100007                                 D100007
000008-100008                                 D100008
000010-100010                                 D100010
**************************** Bottom of Data *********


El fichero POBL.T2 se corresponde con el fichero maestro de poblaciones (MASTER), al que al final se le han añadido siete posiciones en blanco. Con esto se consigue que POBL.T1 y POBL.T2 tengan la misma longitud de registro.

----+----1----+----2----+----3----+----4----+----5---
***************************** Top of Data ***********
000002B-000002-ALMERIA................ALM01300    
000004B-000004-SANTANDER..............SAN01500    
000005B-000005-TARRAGONA..............TAR01600    
000006B-000006-BADAJOZ................BAD01700    
000007B-000007-SALAMANCA..............SAL01800    
000008B-000008-BURGOS.................BUR01900    
000010B-000010-SEGOVIA................SEG01100    
000011B-000011-TOLEDO.................TOL01000    
000012B-000012-GRANADA................GRA00900    
000014B-000014-CASTELLON..............CAS00700    
000015A-000015-ZARAGOZA...............ZAR00600    
000017A-000017-VALENCIA...............VAL00400    
000018A-000018-BARCELONA..............BAR00300    
000019B-000019-CIUDAD REAL............CRE00200    
000020A-000020-MADRID.................MAD00100    
**************************** Bottom of Data *********



A continuación, el segundo paso del JCL usa como entrada los dos ficheros que se acaban de generar (POBL.T1 y POBL.T2). La salida la constituyen el fichero con los registros modificados SIST.JJ0107S1.X0060X.DDD.POBL.MD y el fichero con el resto de registros que no ha sufrido ningún cambio por parte de la utilidad ICETOOL SIST.JJ0107S2.X0060X.DDD.POBL.RS.

En el dataset POBL.MD encontraremos los registros cuya clave de población antigua ha sido sustituida por la nueva (según la relación establecida en el fichero KEYS): 100002, 100004, 100005, etc…

----+----1----+----2----+----3----+----4    
***************************** Top of Data *****
B-100002-ALMERIA................ALM01300    
B-100004-SANTANDER..............SAN01500    
B-100005-TARRAGONA..............TAR01600    
B-100006-BADAJOZ................BAD01700    
B-100007-SALAMANCA..............SAL01800    
B-100008-BURGOS.................BUR01900    
B-100010-SEGOVIA................SEG01100    
**************************** Bottom of Data ***


Del mismo modo, en el fichero POBL.RS se almacenan los registros que no han sufrido ninguna modificación. Y, por tanto, continúan estando identificados por la clave de población antigua: 000011, 000012, 000014, etc…

----+----1----+----2----+----3----+----4    
***************************** Top of Data *****
B-000011-TOLEDO.................TOL01000    
B-000012-GRANADA................GRA00900    
B-000014-CASTELLON..............CAS00700    
A-000015-ZARAGOZA...............ZAR00600    
A-000017-VALENCIA...............VAL00400    
A-000018-BARCELONA..............BAR00300    
B-000019-CIUDAD REAL............CRE00200    
A-000020-MADRID.................MAD00100    
**************************** Bottom of Data ***




Finalmente, si unimos en un fichero resultante los registros de los datasets POBL.MD y POBL.RS, dispondremos del nuevo maestro de poblaciones en el que ya se han realizado los cambios de código de población requeridos.

----+----1----+----2----+----3----+----4    
***************************** Top of Data *****
B-000011-TOLEDO.................TOL01000    
B-000012-GRANADA................GRA00900    
B-000014-CASTELLON..............CAS00700    
A-000015-ZARAGOZA...............ZAR00600    
A-000017-VALENCIA...............VAL00400    
A-000018-BARCELONA..............BAR00300    
B-000019-CIUDAD REAL............CRE00200    
A-000020-MADRID.................MAD00100    
B-100002-ALMERIA................ALM01300    
B-100004-SANTANDER..............SAN01500    
B-100005-TARRAGONA..............TAR01600    
B-100006-BADAJOZ................BAD01700    
B-100007-SALAMANCA..............SAL01800    
B-100008-BURGOS.................BUR01900    
B-100010-SEGOVIA................SEG01100    
**************************** Bottom of Data ***


Como vemos, en el fichero anterior se pueden apreciar tanto códigos antiguos de población (000011-TOLEDO, 000012-GRANADA, 000014-CASTELLON, etc…) como códigos nuevos de población (100002-ALMERIA, 100004-SANTANDER, 100005-TARRAGONA, etc…). En un cliente real, a partir de ahora este sería el nuevo fichero maestro de Poblaciones. 

Conclusiones del cruce de ficheros mediante SPLICE


La verdad es que, si no la hemos realizado nunca con anterioridad, la ejecución de un cruce de ficheros mediante SPLICE nos puede parecer algo compleja. Tengo que reconocer que, al menos a mí, así me lo pareció la primera ocasión en la que tuve que enfrentarme con esta operación.


Sin embargo, os aseguro que, una vez que hayáis practicado con algunos ejemplos, os daréis cuenta de que el empleo del SPLICE del ICETOOL nos puede ahorrar muchos pasos (y mucho trabajo) a la hora de elaborar un JCL. Pensad: ¿cuánto nos habría costado implementar el ejemplo (sencillo) del artículo sin usar el SPLICE?

Siempre es complicado empezar a pelearse con una nueva utilidad Host. Pero eso no significa que lo más óptimo sea seguir trabajando con nuestros antiguos operadores de toda la vida. Aunque nos pueda parecer tedioso a corto plazo, el empleo de las utilidades más potentes nos ahorrará mucho tiempo de codificación a largo plazo. No lo olvidéis la próxima vez que os tengáis que enfrentar con un SPLICE.



En este artículo hemos tratado de ver cómo se puede realizar un cruce de ficheros mediante el operador SPLICE. Ciertamente, existen otros muchos comandos relacionados con la utilidad ICETOOL, pero este es uno de los más utilizados. De todas formas, en el Blog iremos viendo poco a poco cómo se deben implementar las operativas JCL con aquellos operadores que considero como los más importantes.

Pues nada, espero que lo comentado aquí os sirva para tener un poco más claro cuáles son los pasos a seguir a la hora de elaborar un SPLICE. Si lo he conseguido, con eso ya me puedo dar por satisfecho…

Saludos.

lunes, 14 de septiembre de 2015

ICETOOL: Como cruzar Ficheros con SPLICE (1)

Una de las funcionalidades más apreciadas de ICETOOL es la que nos permite cruzar varios ficheros y realizar comparaciones entre los registros contenidos en ellos. Estas acciones se realizan mediante el comando SPLICE, que puede ser considerado como uno de los operadores más útiles de la herramienta.

Cruzar ficheros mediante SPLICE de ICETOOL


En muchas ocasiones nos podemos encontrar con la necesidad de comparar los registros de dos ficheros distintos con el objetivo de determinar si son iguales en función de una clave determinada o no. Esto, que en principio parece una operativa compleja, puede ser realizado de forma bastante ágil mediante el operador SPLICE de la utilidad ICETOOL.

Tal y como se especifica en la documentación de la herramienta, la función SPLICE sirve para realizar un cruce de ficheros, comparar los registros contenidos en ellos, extraer la información seleccionada y enviarla a un fichero de salida.



Una vez comentado de forma teórica, lo mejor es que veamos cómo funciona el proceso de cruce de ficheros mediante un ejemplo. Para ello, vamos a partir de dos ficheros de diferente longitud de registro. En ambos datasets se va a encontrar la misma clave Población, así que vamos a ejecutar la comparación mediante este campo.

Comparar registros de ficheros mediante SPLICE


En nuestro ejemplo, vamos a usar como entrada un fichero de claves (KEYS) y un fichero maestro (MASTER). En el KEYS tendremos la relación entre los códigos de Población actuales (antiguos) y los códigos de Población nuevos. En el MASTER tendremos los registros de Población con toda su información asociada (uno de esos campos será la clave Población, que figurará inicialmente con el código actual).

En este caso, nos piden que sustituyamos en el MASTER los códigos actuales de las Poblaciones por los códigos nuevos (recordemos que la relación entre ambos figura en el fichero KEYS). Aquí es donde va a entrar en escena el empleo del SPLICE de la utilidad ICETOOL.
El contenido del fichero de relaciones entre claves, denominado SIST.UNLOAD.X0005X.POBL.KEYS, es el siguiente (en primer lugar figura la clave actual y, a continuación, la clave nueva sustituta):

----+----1---
*************
000010-100010
000002-100002
000004-100004
000005-100005
000006-100006
000007-100007
000008-100008
*************




Por otra parte, los registros que tenemos cargados en el fichero de Poblaciones, denominado SIST.UNLOAD.X0005X.POBL.MASTER, son los siguientes:

----+----1----+----2----+----3----+----4
***************************** Top of Data *****
A-000020-MADRID.................MAD00100    
B-000019-CIUDAD REAL............CRE00200    
A-000018-BARCELONA..............BAR00300    
A-000017-VALENCIA...............VAL00400    
A-000015-ZARAGOZA...............ZAR00600    
B-000014-CASTELLON..............CAS00700    
B-000012-GRANADA................GRA00900    
B-000011-TOLEDO.................TOL01000    
B-000010-SEGOVIA................SEG01100    
B-000002-ALMERIA................ALM01300    
B-000004-SANTANDER..............SAN01500    
B-000005-TARRAGONA..............TAR01600    
B-000006-BADAJOZ................BAD01700    
B-000007-SALAMANCA..............SAL01800    
B-000008-BURGOS.................BUR01900    
**************************** Bottom of Data ***


La idea es que, tras lanzar nuestro proceso de conversión, la población Madrid del MASTER, por ejemplo, ya no esté asociada al código antiguo 000020 sino al código nuevo 100020. El resto de campos de los registros del fichero maestro tienen que quedar exactamente igual que ahora.

A continuación, mostramos el código necesario para realizar el cambio comentado.

//*            *******************************************
//*            * PASO ICETOOL PARA REFORMATEAR ENTRADAS      //*            *                                          //*            *******************************************
//JJ0105   EXEC PGM=ICETOOL                                          //TOOLMSG  DD  SYSOUT=*                                             
//DFSMSG   DD  SYSOUT=*                                             
//* KEYS  : FICHERO DE RELACIONES ENTRE CLAVES                   
//KEYS     DD  DSN= SIST.UNLOAD.X0005X.POBL.KEYS,    
//             DISP=SHR                                             
//* MASTER: FICHEROS DE POBLACIONES
//MASTER   DD  DSN= SIST.UNLOAD.X0005X.POBL.MASTER,
//             DISP=SHR                                              //* TEMP1 : FICHERO KEYS FORMATEADO Y ORDENADO PARA CRUCE
//TEMP1    DD  DSN=SIST.JJ0105T1.X0060X.DDD.POBL.T1,
//             DISP=(NEW,CATLG,DELETE),                              //             SPACE=(CYL,(00100,00100),RLSE),                     
//             UNIT=DISCO                                          
//* TEMP2 : F. POBLACIONES FORMATEADO Y ORDENADO PARA CRUCE
//TEMP2    DD  DSN=SIST.JJ0105T2. X0060X.DDD.POBL.T2,
//             DISP=(NEW,CATLG,DELETE),                              //             SPACE=(CYL,(00100,00100),RLSE),                     
//             UNIT=DISCO                                          
//TOOLIN   DD  DSN=SIST.SYSIN(JJRS1A03),                      
//             DISP=SHR                                            
//CTL1CNTL DD  DSN=SIST.SYSIN(JJRS1A04),                     
//             DISP=SHR                                            
//CTL2CNTL DD  DSN=SIST.SYSIN(JJRS1A05),                     
//             DISP=SHR                                            
//JJ0105A  EXEC PGM=ABEN3333,                                      
//             COND=(4,GE,JJ0105)                                  
//CTRNORST DD  DUMMY                                               
//*                                                                
//*            *******************************************
//*            * PASO ICETOOL PARA GENERAR SALIDAS
//*            *                                            
//*            *******************************************
//JJ0107   EXEC PGM=ICETOOL                                         
//TOOLMSG  DD  SYSOUT=*                                             
//DFSMSG   DD  SYSOUT=*                                             
//* CONCAT: FICHERO UNIFICADO PARA REALIZAR EL ICETOOL       
//CONCAT   DD  DSN=SIST.JJ0105T1.X0060X.DDD.POBL.T1,
//             DISP=SHR                                              //         DD  DSN=SIST.JJ0105T2.X0060X.DDD.POBL.T2,
//             DISP=SHR                                             
//* OUT   : FICHERO DE SALIDA CON LOS REGISTROS MODIFICADOS         
//OUT      DD  DSN=SIST.JJ0107S1.X0060X.DDD.POBL.MD,
//             DISP=(NEW,CATLG,DELETE),                              //             SPACE=(CYL,(00100,00100),RLSE),                      
//             UNIT=DISCO                                           
//* OUT1  : F. DE SALIDA CON LOS REGISTROS NO MODIFICADOS      
//OUT1     DD  DSN=SIST.JJ0107S2.X0060X.DDD.POBL.RS,
//             DISP=(NEW,CATLG,DELETE),                              //             SPACE=(CYL,(00100,00100),RLSE),                    
//             UNIT=DISCO                                         
//TOOLIN   DD  DSN=SIST.SYSIN(JJRS1A06),                    
//             DISP=SHR                                           
//CTL3CNTL DD  DSN=SIST.SYSIN(JJRS1A07),
//             DISP=SHR                                           
//CTL4CNTL DD  DSN=SIST.SYSIN(JJRS1A08),                    
//             DISP=SHR                                           
//JJ0107A  EXEC PGM=ABEN3333,                                      
//             COND=(4,GE,JJ0107)                                 
//CTRNORST DD  DUMMY                                              
//*

   
Y las fichas asociadas al código anterior son las siguientes:

*--------------------------------------------------------*
* <<< FICHA JJRS1A03 >>>
* COPIA KEYS A FICHERO TEMP1 PARA PERMITIR SPLICE
* COPIA MASTER A TEMP Y AÑADE LOS REGISTROS A COMBINAR
*--------------------------------------------------------*
  COPY FROM(KEYS) TO(TEMP1) USING(CTL1)          

  COPY FROM(MASTER) TO(TEMP2) USING(CTL2)

*--------------------------------------------------------*
* <<< FICHA JJRS1A04 >>>  
* REFORMATEAMOS EL KEYS - LA CLAVE ES LA POBLACION
*--------------------------------------------------------*
  SORT FIELDS=(1,6,CH,A)                                  
  OUTREC FIELDS=(1,13,33X,47:C'D',8,6)

*--------------------------------------------------------*
* <<< FICHA JJRS1A05 >>>
* REFORMATEAMOS EL MASTER - LA CLAVE ES LA POBLACION
*--------------------------------------------------------*
  INREC FIELDS=(3,6,1,40)     
  SORT FIELDS=(1,6,CH,A)      
  OUTREC FIELDS=(1,46,47:C' ',6X)

*--------------------------------------------------------*
* <<< FICHA JJRS1A06 >>>
* OUT: REGISTROS QUE COINCIDEN                           *
* INCLUIMOS TODO DEL MASTER MAS POBLACION NUEVA DE KEY   *
* MAS UN INDICADOR NUEVO DE MODIFICADO
* OUT1: RESTO DE REGISTROS QUE NO COINCIDEN              *
*--------------------------------------------------------*
  SPLICE FROM(CONCAT) TO(OUT) ON(1,6,CH) WITHALL  -
  WITH(7,40) USING(CTL3)                         
  SPLICE FROM(CONCAT) TO(OUT1) ON(1,6,CH) WITHALL  -
  WITH(7,40) USING(CTL4) KEEPNODUPS KEEPBASE     

*--------------------------------------------------------*
* <<< FICHA JJRS1A07 >>>
* SUSTITUIMOS POBL ENCONTRADOS POR POBL NUEVAS
* SE INCLUYE INDICADOR DE MODIFICADO
*--------------------------------------------------------*
  OUTFIL FNAMES=OUT,
  INCLUDE=(47,1,CH,EQ,C'D'),
  OUTREC=(7,2,48,6,15,32)                  

*--------------------------------------------------------*
* <<< FICHA JJRS1A08 >>>
* SACAMOS REGISTROS NO ENCONTRADOS PARA CUADRAR ENTRADA
*--------------------------------------------------------*
  OUTFIL FNAMES=OUT1,
  INCLUDE=(47,1,CH,EQ,C' '),
  OUTREC=(7,40)       
                       


Como vemos, el código anterior se compone de dos pasos JCL, que son los que vamos a necesitar para, en primer lugar, reformatear e igualar la longitud de los dos ficheros de entrada y, posteriormente, generar un fichero de salida con los registros modificados y otro con los registros no modificados.



Detalle de las fichas requeridas para realizar un SPLICE


A continuación, una vez mostrado el código anterior, vamos a ir revisando con un poco de detalle en qué consisten cada una de las fichas que hemos tenido que crear en los dos pasos del JCL. Como veremos, muchas de ellas son bastante genéricas.

PASO 1: PASO ICETOOL PARA REFORMATEAR ENTRADAS


1º) Invocación a ICETOOL: El primer paso es, como siempre, la invocación a la herramienta ICETOOL mediante el comando EXEC PGM.

//JJ0105   EXEC PGM=ICETOOL                                           

2º) Especificación de los ficheros de entrada: A continuación, indicamos los nombres de los dos ficheros de entrada que vamos a emplear en el ICETOOL. Se trata del fichero de relaciones entre claves (SIST.UNLOAD.X0005X.POBL.KEYS) a emplear y del fichero maestro con los datos de poblaciones (SIST.UNLOAD.X0005X.POBL.MASTER).

//* KEYS  : FICHERO DE RELACIONES ENTRE CLAVES                    
//KEYS     DD  DSN= SIST.UNLOAD.X0005X.POBL.KEYS,    
//             DISP=SHR
//* MASTER: FICHEROS DE POBLACIONES
//MASTER   DD  DSN= SIST.UNLOAD.X0005X.POBL.MASTER,
//             DISP=SHR


3º) Especificación de los ficheros de salida: Del mismo modo, también indicamos los nombres de los ficheros que van a contener la información reformateada de KEYS y MASTER. Estos datsets intermedios se denominarán SIST.JJ0105T1.X0060X.DDD.POBL.T1 y SIST.JJ0105T2.X0060X.DDD.POBL.T2.

//* TEMP1 : FICHERO KEYS FORMATEADO Y ORDENADO PARA CRUCE
//TEMP1    DD  DSN=SIST.JJ0105T1.X0060X.DDD.POBL.T1,
//             DISP=(NEW,CATLG,DELETE),               

//             SPACE=(CYL,(00100,00100),RLSE), 
//             UNIT=DISCO  
//* TEMP2 : F. POBLACIONES FORMATEADO Y ORDENADO PARA CRUCE
//TEMP2    DD  DSN=SIST.JJ0105T2. X0060X.DDD.POBL.T2,
//             DISP=(NEW,CATLG,DELETE),
//             SPACE=(CYL,(00100,00100),RLSE),
//             UNIT=DISCO



4º) Ficha TOOLIN: Aquí es donde vamos a indicarle a ICETOOL que tiene que proceder a ejecutar el operador COPY en dos ocasiones. La primera de ellas para pasar de KEYS a TEMP1 y la segunda para pasar de MASTER a TEMP2.

*--------------------------------------------------------*
* <<< FICHA JJRS1A03 >>>                                
* COPIA KEYS A FICHERO TEMP1 PARA PERMITIR SPLICE
* COPIA MASTER A TEMP2 Y AÑADE LOS REGISTROS A COMBINAR
*--------------------------------------------------------*
  COPY FROM(KEYS) TO(TEMP1) USING(CTL1)

  COPY FROM(MASTER) TO(TEMP2) USING(CTL2)
                                     
5º) Fichas de Control: Aquí es donde tenemos que indicar al SPLICE la forma en la que queremos que se ejecuten los operadores COPYS del TOOLIN anterior. En nuestro ejemplo, aquí están incluidas las fichas CTL1 y CTL2.

*--------------------------------------------------------*
* <<< FICHA JJRS1A04 >>>                            

* REFORMATEAMOS EL KEYS - LA CLAVE ES LA POBLACION
*--------------------------------------------------------*
  SORT FIELDS=(1,6,CH,A)                                  
  OUTREC FIELDS=(1,13,33X,47:C'D',8,6)





La primera ficha de control (CTL1CNTL) hace referencia al COPY de KEYS a TEMP1. Como podemos ver en la codificación de la ficha anterior, en ella se realizan dos cosas con los datos de entrada.

-          Lo primero que hace es ordenar el fichero KEYS en función del campo de longitud 6 situado en la posición 1 (se trata del campo código de Población).

-          A continuación, transfiere del KEYS al TEMP1 la siguiente información de los registros: el campo de longitud 13 que empieza en la posición 1 (1,13), 33 espacios en blanco (33X), un carácter ‘D’ en la posición 47 (47:C’D’) y el campo de longitud 6 que empieza en la posición 8 (8,6).

*--------------------------------------------------------*
* <<< FICHA JJRS1A05 >>>
* REFORMATEAMOS EL MASTER - LA CLAVE ES LA POBLACION
*--------------------------------------------------------*
  INREC FIELDS=(3,6,1,40)     
  SORT FIELDS=(1,6,CH,A)      
  OUTREC FIELDS=(1,46,47:C' ',6X)




La segunda ficha de control (CTL2CNTL) hace referencia al COPY de MASTER a TEMP2. Del mismo modo que en la anterior, en esta segunda ficha vemos que también se realizan varias acciones con los datos de entrada.

-          INREC: En primer lugar, a los registros de entrada le insertamos por delante el campo de longitud 6 que está en la posición 3 (3,6), que es la Población. A continuación, se incluyen las 40 posiciones de los registros sin realizar ningún cambio sobre ellas (1,40).

-          SORT: Se ordena el fichero en función del campo de longitud 6 situado en la posición 1 (se trata del campo código de Población que acabamos de insertar en la sentencia anterior).

-          OUTREC: Se transfiere del MASTER al TEMP2 la siguiente información de los registros: el campo de longitud 46 situado en la posición 1 (1,46), un carácter en blanco (47:C’ ‘) y 6 espacios en blanco (6X).


El próximo día, en un nuevo post, terminaremos de ver las fichas necesarias para implementar un SPLICE en un JCL. La idea será examinar en detalle para qué sirve cada una de las sentencias incluidas en el paso 2. De ese modo, completaremos la visión global del proceso y, a partir de ese momento, ya no deberíais tener problemas a la hora de codificar vuestro propio job.

Pues nada, eso es todo por hoy. Como ya sabéis, quedáis invitados a la segunda parte del artículo. De todas formas, si no podéis esperar hasta entonces, aquí abajo podéis ir dejándome las dudas que os surjan en relación con este tema.

Saludos.

lunes, 7 de septiembre de 2015

ICETOOL: Como copiar ficheros en JCL (y 2)

Hace algunas semanas comenzamos a ver la forma en la que se podía implementar, en un JCL, una copia de ficheros mediante la herramienta ICETOOL. Ya sabemos que existen métodos alternativos para realizar dicha función, pero hemos de tener en cuenta que el uso de esta utilidad de ICEMAN nos permitiará realizar copias de una de las maneras más eficientes del mundo Host.

En la primera parte del artículo empezamos a ver cuáles son las fichas que tenemos que incluir en el JCL de copia de ficheros (ver post ICETOOL - Como copiar ficheros en JCL - 1). A continuación, terminaremos de revisar cuáles son los pasos que debemos incluir en la implementación de nuestro proceso.

5º) Fichas de CONTROL CTL1 y CTL2: En estas fichas tendremos que indicar cuáles son las condiciones con las que debe realizarse la copia y el formateo de los ficheros de salida (en particular, aquí se apunta a los objetos FICTL1 y FICTL2).

A) Ficha CTL1: En la ficha CTL1 se indica cómo debe copiarse el fichero IN1 al fichero OUT1.

* <<< FICHA CONTROL FICTL1 >>>                      
  SORT FIELDS=(1,4,PD,A,5,2,PD,A)                                   
  OUTREC FIELDS=(1,21,193X,215:C'D',9,6)  

Esta ficha de control está indicando lo siguiente:

- SORT: Que se ordenen los registros de IN1 en función de la información contenida en los campos (1,4,PD,A) y (5,2,PD,A).

- OUTREC: Una vez realizada la ordenación, se transfieren al fichero de salida las primeras 21 posiciones del fichero de entrada (1,21), luego se añaden 193 espacios en blanco (193X), luego se añade el literal 'D' (215:C'D') y finalmente se incorpora el campo de 6 posiciones que empieza en la posición 9 de IN1 (9,6).



B) Ficha CTL2: En la ficha CTL2 se indica cómo debe copiarse el fichero IN2 al fichero OUT2.

* <<< FICHA CONTROL FICTL2 >>>
  INREC FIELDS=(1,6,1,208)                                          
  SORT FIELDS=(1,4,PD,A,5,2,PD,A)                                   
  OUTREC FIELDS=(1,214,215:C' ',6X)

Esta ficha de control está indicando lo siguiente:

- INREC: Que se creen registros en los que se inserte, en primer lugar, el campo de longitud 6 que empiece en la posición 1 del IN2 (1,6) y, en segundo lugar, el campo de longitud 208 que empieza en la posición 1 de IN2 (1,208).

- SORT: A continuación, se deben ordenar los registros en función de la información contenida en los campos (1,4,PD,A) y (5,2,PD,A).

- OUTREC: Finalmente, se transfiere al fichero de salida OUT2 el campo de longitud 214 que empieza en la posición 1, luego se añade un espacio en blanco (215:C' ') y finalmente se completa con 6 espacios en blanco (6X).



Ejecución de la función COPY de ICETOOL


Una vez informadas todas las fichas anteriores, la ejecución del paso JCL con la utilidad ICETOOL no debería darnos ningún problema. El proceso acabará con la ejecución del operador COPY, que trasladará la información desde los ficheros de entrada hasta los ficheros de salida, según las condiciones especificadas en las fichas de control.

En el caso de nuestro ejemplo, los registros irán desde los datasets &INDICE2..JJTV10K1.X0007X.DDD.F&FECHA..COMP3 y &INDICE1..UNLOAD.X0005X.&DIA..&TABLA hasta los destinos &INDICE1..JJ4605S1.X0060X.DDD.F&FECHA..&TAB..T1 y &INDICE1..JJ4605S2.X0060X.DDD.F&FECHA..&TAB..T2. Eso sí, los registros de origen aparecerán reformateados en los registros de destino, ya que indicamos varios cambios tanto en la ficha de Control 1 como en la ficha de Control 2.


Como vemos, el operador COPY nos sirve para realizar varias cosas a la vez. En el ejemplo, hemos realizado la copia de dos ficheros origen en dos ficheros destino. Pero, además, hemos insertado datos al comienzo de los registros de entrada (INREC), hemos ejecutado una ordenación de los registros (SORT) y, finalmente, hemos indicado cuáles son los campos que deben transferirse a los registros de salida (OUTREC). ¿Con qué otra utilidad podemos realizar tantas cosas a la vez?

Conclusiones sobre el operador COPY de ICETOOL


Tal y como hemos visto al principio, como programadores Cobol tenemos diferentes opciones para realizar copias de ficheros origen a ficheros destino. Desde las herramientas clásicas como IEBGENER o DFSORT, hasta otras más eficientes y modernas como ICEGENER o la que hemos visto en el post de hoy, ICETOOL.


Desde mi punto de vista, aunque al principio nos pueda parecer algo extraña su codificación, la implementación de esta operativa mediante ICETOOL de ICEMAN es la mejor alternativa que podemos escoger. Si nos esforzamos un poco al principio en aprender a usarla, a largo plazo ahorraremos mucho tiempo a la hora de codificar y ejecutar los JCL que empleamos para copiar ficheros.

Si implementamos el COPY de ICETOOL especificando las fichas de la forma comentada a lo largo del artículo, no deberíamos tener problemas a la hora de lanzar el job. Además, en caso de error, lo más importante es que hayamos entendido para qué sirve cada uno de los apartados del proceso y que no nos conformemos con hacer un copy-paste del código publicado en el blog. De esta manera, seremos capaces de subsanar cualquier ABEND sin dificultad.



Os digo esto último porque es algo bastante tentador y que a mi me estuvo pasando durante algún tiempo. La verdad es que era mucho más rápido y sencillo entrar en un foro, copiar el código del JCL en mi librería y ejecutarlo sin más. El problema venía cuando el job fallaba y yo no tenía muy claro qué es lo que hacía cada parte del proceso: así no había forma de corregir los errores. Hoy en día, siempre intento entender cada ficha antes de ejecutar un JCL. Creedme, al final se acaba ganando tiempo.

Pues nada, esto es todo lo que quería comentaros con respecto a la utilización del comando COPY de la utilidad ICETOOL de ICEMAN. Como ya sabéis, podéis dejarme cualquier duda que os surja en el apartado de comentarios. Os responderé lo antes posible.

Saludos.

Related Posts Plugin for WordPress, Blogger...