lunes, 31 de enero de 2011

Colas de Ejecución


Dependiendo de el sistema donde nos encontremos, el acceso a las colas de ejecución será de forma distinta. Incluso puede no existir tal acceso y tener controladas las colas desde otros lugares. El cómo llegar, lo dejo en vuestras manos, pero la pantalla común en la que tenemos estar todos es ésta:



Línea de comandos (COMMAND INPUT):

Comando PRE: Por medio de esta cola podemos observar (y cotillear) lo que está haciendo cualquier usuario del sistema. El comando para filtrar usuarios es PRE.

  • PRE * Este comando nos muestra todos los usuarios del sistema siempre y cuando estén conectados.
  • PRE “nombre_usuario” Este comando nos filtra por el usuario indicado.
Lo primero que haremos será filtrar por nuestro usuario para ver nuestras ejecuciones. Para ello escribimos en la línea de comando: PRE "Mi_Usuario"

Nota: Este comando se puede usar en ésta o en todas las sucesivas pantallas que vamos a ver.

Existen diferentes Tipos de Colas:

Cola ST: Muestra el estado en el que estan la ejecuciones. Nos dice los proceso que hay en ejecución(en máquina), los procesos que están pendientes de entrar en máquina y los procesos que ya han acabado.

Algunas columnas de información interesantes son:
  1. Columna NP: Mini línea de comandos para un proceso en concreto.
    • S     Muestra toda la información (log) de la ejecución.
    • ?     Muestra toda la información de la ejecución divida en apartados
    • SE Muestra toda la información de la ejecución pero en formato para EDITAR.
    • SJ   Ver el código del jcl.
    • C    Cancela la ejecución del proceso seleccionado
    • P    Purga el proceso seleccionado de la cola.
    • // ... //P Purga varios procesos al mismo tiempo. Para seleccionar la lista de procesos que queremos purgar, ponemos “//” en el primero y “//p” en el último
  1. Columna JOBNAME: Nombre del job o del usuario
  2. Columna JobID: Clave única que asigna el host al proceso
  3. Columna Owner: Propietario del proceso
  4. Columna Queue: EXECUTION (indica que el proceso está en ejecución(color blanco) o pendiente de entrar(color azul)), PRINT (indica que el proceso ha terminado)
  5. Columna Max-RC: Resultado de la ejecución. Todo ceros bien, un 4 bueno, otro....mal asunto.. ;-)
  6. Columna Pos: Posición global que ocupa en la cola.
Entremos un poco más en detalle sobre las opciones de la columna NP:
- Si introducimos S delante de la ejecucíón veremos el log de la ejecución completo.


- Si introducimos SJ delante de una ejecución podemos ver el código del JCL que lanzó la ejecución.


Y nos muestra el job seleccionado:


Ojo!: Los cambios que aquí se realicen no se guardan en la librería original. Si submitimos la nueva versión desde las colas solo se guarda en las colas, si no submitimos perdemos todos los cambios que hayamos hecho.


Cola DA: Muestra las tareas que se están ejecutando en tiempo real del usuario o los usuarios que tengamos filtrados con el comando PRE. Si, por ejemplo, hemos filtrado por nuestro usuario nos muestra los procesos que tengamos en ejecución (todos los jcls que tengamos ejecutándose) y además nos mostrará a nuestro propio usuario que está conectado al sistema (recordemos que nuestro usuario también forma parte del host y es un recurso más que éste debe tratar). Si no tenemos ningún jcl en ejecución, únicamente veremos nuestro usuario.

Esta opción es muy útil para realizar el seguimiento de nuestros jcls en caso de que estén en máquina. Nos da mucha información del mismo:

NP JOBNAME STEPNAME PROCSTEP TYPE  CPU%  OWNER   CPU
   USUARIO PASO010  UNLOAD   JOB   60.42 USUARIO 68.96
   USUARIO IKJDESA  NVASPB23 TSU   1.12  USUARIO 3.42

  • NP Indicador de comandos. Por ejemplo: Poniendo una C nos cancela la ejecución del jcl.
  • JOBNAME Nombre del jcl o del usuario filtrado.
  • STEPNAME Nombre del paso en el que se encuentra la ejecución.
  • PROCSTEP Tipo de paso
  • TYPE Tipo de proceso que esta en ejecución. JOB (se trata de un jcl), TSU (se trata de un usuario conectado).
  • CPU% % de CPU usada del host. Tener en cuenta que a su vez es un valor del X% que se le asigna a cada entorno. Es decir, del 100% del host, supongamos que un 10% es asignado a la parte de desarrollo(a los usuarios y jcls que lanzamos que prueban los programas en un entorno de desarrollo). Pues bien, el CPU% es el % que nosotros utilizamos de ese 10% total. Si nos marca un CPU%=60.42, estaríamos utilizando aproximadamente el 0.06% del total.
  • OWNER El propietario o usuario que a lanzado el job
  • CPU TIME Tiempo de CPU que está utilizando.


    Hay más columnas de información pero no sabría explicarlas en detalle. Agradecería si alguno de vosotros las conoce que nos dejara un comentario con la explicación.

Cola H: Muestra las ejecuciones concluidas.
Cola INIT: Muestra los iniciadores( Un iniciador es donde se ejecuta una tarea, hay un número n de iniciadores, mientras no haya ninguno libre no se pueden ejecutar más tareas).
Cola I: Muestra las ejecuciones en espera.
Cola O: Muestra la cola de salida (impresión).


Cosas Curiosas:
  1. Tirar del sistema a algún usuario. No se si sirve en todas las plataformas y al menos se necesita que tu usuario tenga la misma autoridad o más que la del usuario que pretendes tirar, pero el comando es el siguiente:
    /c u=”nombre_usuario”

  2. Si escribimos en la línea de comandos la función SET DISPLAY ON nos activa la siguiente línea en la pantalla:
    PREFIX=USUARIO* DEST=(ALL) OWNER=* SYSNAME=*
Con eso podemos ampliar el filtro de procesos de las colas. 

  • En este caso PREFIX nos indica el usuario que tenemos seleccionado. Si tuvieramos PREFIX=* indicaría que tenemos seleccionados todos los usuarios y procesos. Esto se controla con el comando PRE (explicado al principio del documento).
  • DEST=(ALL). Cada proceso se ejecuta en un destino, normalmente el destino es el mismo que el que ejecuta el proceso. Si tenemos ALL significa que tenemos filtrados todos los destinos. Para habilitar este comando la sentencia es: 
DEST ALL
ó
DEST nombre_proceso_ó_usuario
  • OWNER=*. Indica el filtrado por propietario, es decir el que ejecuta el proceso.Para habilitar este comando la sentencia es: 
                 OWNER * 
                 OWNER ALL
                 OWNER
                 OWNER nombre_proceso_ó_usuario
  • SYSNAME=*. Filtrado por nombre de systema que donde se ejecuta el proceso.Para habilitar este comando la sentencia es: 
                SYSNAME
                SYSNAME *
                SYSNAME ALL
                SYSNAME nombre_proceso_ó_usuario
    Combinar estos 4 métodos nos permite un amplia variedad de filtros.

    viernes, 28 de enero de 2011

    Nuevo CPD en Santander

    Según publica Europapress, en un reciente artículo, el Banco Santander tiene previsto que su nuevo CPD de Cantabria esté disponible a finales de este año y entre en funcionamiento a principios del próximo.

    La inversión está presupuestada en 150 millones de euros. Se calcula que generará unos 300 puestos de trabajo directos y en torno a 700 indirectos a través de empresas auxiliares.

    Este CPD se unirá con los que cuenta la entiedad en Madrid, Londres, Sao Paulo y Querétaro.

    Será una gran oportunidad para los profesionales del Host, que tendrán en Santander nuevas oportunidades. Sin duda se convertirá en un lugar de referencia como actualmente son Madrid, Barcelona, Alicante o Valencia.

    Noticia Europapress

    miércoles, 26 de enero de 2011

    WORKING-STORAGE: definiendo variables.

    Las variables que se usan en un programa cobol deben haber sido previamente definidas en la WORKING-STORAGE SECTION.

    Existen diferentes tipos de variables en cobol. Cada variable que definamos debe terminar con un punto ".".
    Si la variable va a tener un valor inicial lo indicaremos con la cláusula VALUE.

    Alfanuméricas: se indican con un PIC X(número de caracteres). Podrán tener como valores números y letras.

    01 WX-VARIABLE-ALFNUM PIC X(10) VALUE 'LOQUESEA10'.


    Numéricas normales: se indican con un PIC 9(número de dígitos). Sólo tendrán valor numérico. La V indica la coma decimal.

    01 WX-VARIABLE-NUM PIC 9(5)V9(2) VALUE 12345,67.
    (value sin las comillas)

    Numéricos comprimidos: se indican con un PIC 9(número de dígitos) COMP-3. Sólo valores numéricos. Estas variables ocuparán, en espacio, menos que un numérico normal.
    El cálculo de longitudes lo veremos más adelante. La S indica que lleva signo decimal que puede ser positivo o negativo.

    01 WX-VARIABLE-COMP3 PIC S9(5)V9(2) COMP-3 VALUE 12345,67.


    Numéricos hexadecimales: se indican con un PIC 9(número de dígitos) COMP. Sólo valor numérico. OJO! Aceptan letras porque el número va en formato hexadecimal, pero eso no significa que podamos informarlas con caracteres alfabéticos.

    01 WX-VARIABLE-COMP PIC 9(5)V9(2) COMP VALUE 12345,67.


    Editados: se indican con la cláusula PIC seguida por tantas "zetas" Z como necesitemos. Se utilizan para dar formato a campos numéricos.

    01 WX-VARIABLE-EDI PIC -Z.ZZZ.ZZ9,99.


    Esto equivaldría a un campo numérico PIC S9(7)V9(2). El último dígito antes de la coma decimal se indica con un 9 para que aparezca el valor numérico del 0 al 9. Las Zs sustituirán los "ceros" 0 a la izquierda por espacios.

    01 WX-VARIABLE-NUM PIC S9(7)V9(2) VALUE -0001234,56.
    01 WX-VARIABLE-EDI PIC -Z.ZZZ.ZZ9,99

    DISPLAY 'WX-VARIABLE-NUM:'WX-VARIABLE-NUM

    MOVE WX-VARIABLE-NUM TO WX-VARIABLE-EDI

    DISPLAY 'WX-VARIABLE-EDI:'WX-VARIABLE-EDI


    El resultado sería:
    WX-VARIABLE-NUM:00012345O
    WX-VARIABLE-EDI:-    1.234,56



    Si queremos que el signo aparezca pegado al número, podemos definir el campo editado de la siguiente manera:
    01 WX-VARIABLE-EDI PIC -.---.--9,99.

    Para que se muestre como "-1.234,56".

    Ahora que sabemos definir variables, vamos a ver que las variables pueden estar definidas en diferentes "niveles". Estos niveles vienen indicados por un número a la izquierda:

    01 WX-INFORMACION-PERSONA.
       05 WX-NOMBRE    PIC X(10).
       05 WX-APELLIDO1 PIC X(10).
       05 WX-APELLIDO2 PIC X(10).
       05 WX-DIRECCION.
          10 WX-CALLE  PIC X(10).
          10 WX-NUMERO PIC 9(3).
          10 WX-PISO   PIC 9(2).

    Y así sucesivamente.

    Si tenemos algún subnivel que sea una constante, en lugar de ponerle nombre al campo podemos utilizar la cláusala FILLER:

    01 WX-FECHA.
       05 WX-DIA PIC 9(2).
       05 FILLER PIC X VALUE '-'.
       05 WX-MES PIC 9(2).
       05 FILLER PIC X VALUE '-'.
       05 WX-ANO PIC 9(4).


    La diferencia del FILLER con un campo "con nombre", es que el FILLER no se puede utilizar para "mover" la información que contenga. Es un campo fijo (la traducción sería "RELLENO") que no se puede informar (a no ser que informásemos el nivel superior y nos cargásemos todo, claro... xd).
    El resultado del campo informado sería por ejemplo:
    Fecha = 01-01-2011

    Existe una forma de definir variables con dos o más tipos de formato (PIC) distintos. Se trata de la cláusula REDEFINES.
    Se suele utilizar para evitar mover campos alfanuméricos a numéricos, o bien para agrupar un campo que tenga subniveles:

    01 WX-CAMPO1 PIC X(3).
    01 WX-CAMPO2 REDEFINES WX-CAMPO1 PIC 9(3).

    01 WX-CAMPO1.
       05 WX-SUBCAMPO1 PIC X(3).
       05 WX-SUBCAMPO2 PIC 9(5).
    01 WX-CAMPO2 REDEFINES WX-CAMPO1 PIC X(8).


    IMPORTANTE: sólo se pueden "redefinir" campos que estén al mismo nivel, es decir, para redefinir un campo de un nivel 01, tendremos que hacerlo en otro campo de nivel 01.

    También podemos definir tablas o arrays:
    01 WX-TABLA-INTERNA OCCURS 5 TIMES.
       05 WX-CAMPO1 PIC X VALUE 'A'.
       05 WX-CAMPO2 PIC X VALUE 'B'.


    De tal manera que el conjunto de los campos 1 y 2 se repiten 5 veces:
    ABABABABAB

    Existen otro tipo de niveles que son los niveles "88":
    01 WX-RESPUESTA   PIC X.
       88 RESPUESTA-SI      VALUE 'S'.
       88 RESPUESTA-NO      VALUE 'N'.


    De tal forma que la variable WX-RESPUESTA podrá tomar los valores S ó N según queramos.
    Para hacer que la variable tome un valor:

    SET RESPUESTA-SI TO TRUE
       tomará el valor S.
    SET RESPUSETA-NO TO TRUE    tomará el valor N.

    Imaginemos que tenemos una serie de campos que definen la estructura de un fichero. Si tenemos varios programas que usan ese fichero, tendremos que escribir esos campos tantas veces como programas tengamos. Para ahorrarnos trabajo tenemos la cláusula COPY.
    Una "COPY" es un trozo de código cobol que puede contener la definición de variables (COPY de WORKING) o una parte del código del programa (COPY de PROCEDURE):

    COPY WORKING
    Se incluirán dentro de la WORKING-STORAGE.
    COPY WSCOPY.

    Donde WSCOPY contendrá algo de este estilo:
    01 WX-WSCOPY.
       05 WX-CAMPO1 PIC X(5).
       05 WX-CAMPO2 PIC X(5).
       05 WX-CAMPO3 PIC X(5).
       05 WX-CAMPO4 PIC X(5).


    COPY PROCEDURE
    Se incluirán dentro de la PROCEDURE DIVISION.
    COPY PDCOPY.

    Donde PDCOPY contendrá algo de este estilo:
    MOVE WX-CAMPO1 TO WX-REGISTRO1
    MOVE WX-CAMPO2 TO WX-REGISTRO2
    MOVE WX-CAMPO3 TO WX-REGISTRO3
    MOVE WX-CAMPO4 TO WX-REGISTRO4
    MOVE WX-CAMPO5 TO WX-REGISTRO5



    CÁLCULO DE LONGITUDES

    Numéricos y alfanuméricos: lo que venga indicado en el PIC X(n) ó PIC 9(n) respectivamente.

    01 WX-VARIABLE-ALFNUM PIC X(5).
    01 WX-VARIABLE-NUM    PIC 9(5).

    longitud = 5

    Numéricos COMP-3: si tenemos un PIC 9(n) COMP-3, la longitud será (n+1)/2. Si el resultado tiene decimales redondeamos hacia arriba. Hay que tener en cuenta que la coma decimal no ocupa espacio, y que el signo ocupa sólo media posición.

    01 WX-VARIABLE-COMP3 PIC S9(5) COMP-3.

    longitud = (5 + 1)/2 = 3

    Numéricos COMP:
    Un campo con PICTURE numérica y COMP se almacena en formato binario puro (base 2); el signo lo representa el bit de la derecha. El número de bytes que se reservan en memoria para cada campo es:
    2 posiciones para números de 1 a 4 dígitos.
    4 posiciones para números de 5 a 9 dígitos.
    8 posiciones para números de 10 a 18 dígitos.

    01 WX-VARIABLE-COMP PIC 9(5) COMP.

    longitud = 4


    VISUALIZANDO CAMPOS

    Como ya hemos comentado, existen campos en cobol q no se ven "tal cual", como son los comprimidos o los hexadecimales. Vamos a ver como se verían en un fichero estos campos:

    ----+----1----+----2----+----3----+----4
    999999999          q Íì%      00000  S 
    FFFFFFFFF444444444493716000000FFFFF00E2
    99999999900000000008265C000000000000122
    ----------------------------------------


    * El campo que va de la posición 1 a la 9 sería un campo numérico normal con valor 999999999.
    * El campo que va de la posición 10 a la 19 sería un campo alfanumérico con valor '         ' (9 espacios).
    * El campo que va de la posición 20 a la 24 sería un campo numérico COMP-3 con valor 983276156 positivo (letra C, letra D para negativos*).
    * El campo que va de la posición 25 a la 30 sería un campo informado con low-values.
    * El campo que va de la posición 31 a la posición 35 sería un campo informado con 5 ceros 00000.
    * El campo que va de la posición 36 a la 39 sería un campo numérico COMP con valor 1E222 (en hexadecimal) / 123426(en decimal).

    *Para campos numéricos con signo del tipo S9(3)V99, podemos distinguir el signo fijándonos en la última posición:
    A = +1
    B = +2
    C = +3
    D = +4
    E = +5
    F = +6
    G = +7
    H = +8
    I = +9
    J = -1
    K = -2
    L = -3
    M = -4
    N = -5
    O = -6
    P = -7
    Q = -8
    R = -9
    { = +0
    } = -0


    En el próximo artículo veremos la PROCEDURE DIVISION, donde se codifica la "miga" del programa. Veremos como informar campos, como inicializarlos y como codificar bucles y condiciones.

    Principiantes COBOL

    Hola hola! Cansado de leer manuales de IBM en inglés? Harto de descargarte traducciones automáticas de google? No busques más!

    En esta ocasión os traemos el "manual de COBOL para principiantes"!
    El único, el inconfundible, el del Consultorio Cobol!

    Habrás visto más, pero no mejores. Bienvenido.

    Manual de COBOL para principiantes

    1. Esquema de un programa cobol.
    • Secciones en que se divide todo programa cobol.

    2. WORKING-STORAGE: definiendo variables.
    • Cálculo de longitudes.
    • Visualización de campos.

    3. PROCEDURE DIVISION: proceso del programa.
    • Informando variables.
    • Inicializando campos.
    • Bucles y condiciones.

    4. Ejemplo 1: Leer de SYSIN y escribir en SYSOUT.
    • JCL que ejecuta un programa sin DB2.
    • Programa que realiza un ACCEPT de la SYSIN y un DISPLAY de la información recibida para mostrarla por SYSOUT.

    5. Ejemplo 2: leer de SYSIN y escribir en fichero.
    • JCL con 2 pasos: borrado de ficheros; ejecución de un programa sin DB2 con fichero de salida.
    • Programa que realiza un ACCEPT de la SYSIN y escribe la información recogida en un fichero de salida.

    6. Ejemplo 3: leer de fichero y escribir en fichero.
    • JCL con 2 pasos: borrado de ficheros; ejecución de un programa sin DB2 con fichero de entrada y fichero de salida.
    • Programa que lee del fichero de entrada, formatea los datos, y escribe en el fichero de salida. Uso de las sentencias SEARCH y EVALUATE.

    7. Ejemplo 4: generando un listado.
    • JCL con 3 pasos: borrado de ficheros; ordenación de un fichero, ejecución de un programa sin DB2 con fichero de entrada y fichero de salida tipo listado.
    • Programa que escribe un listado. Incluye control de número de líneas, paginación, etc.

    8. Ejemplo 5: programa con DB2.
    • JCL que ejecuta un programa con DB2.
    • Descripción de la tabla DB2 a la que accederemos a través de una SELECT.
    • Programa con DB2 que accede a una tabla de nuestra base de datos a través de una SELECT. Control del SQLCODE.

    9. Ejemplo 6: programa que llama a otro programa.
    • JCL que ejecuta un programa sin DB2.
    • Programa que llama a una rutina.
    • Rutina que calcula la letra del NIF a partir del número.

    10. Ejemplo 7: ficheros VB (longitud variable)
    • JCL que ejecuta un programa sin DB2 y crea un fichero de longitud variable.
    • Programa que lee de un fichero de longitud fija (FB) y escribe un fichero de longitud variable (VB).

    11. Ejemplo 8: ficheros VSAM
    • JCL que ejecuta un programa sin DB2 actualiza un fichero VSAM.
    • Programa que lee de 2 ficheros GDGs y graba en un fichero VSAM existente.

    Continuará...

    Nueva sección PRINCIPIANTES

    Como habréis visto hemos creado una nueva pestaña llamada "Principiantes" donde iremos agrupando los artículos relacionados con la iniciación al mundo del cobol.
    Lo cierto es que no teníamos pensado meternos a las cosas básicas, pues normalmente cuando entras a trabajar en este mundillo suelen darte un curso con lo más esencial.

    Pero haciendo un repaso de las búsquedas de la gente hemos pensado que no estaría de más tener una sección con un pequeño "curso" de cobol para principiantes, siempre orientado al mundo mainframe.

    Dentro de la sección iremos poniendo los links a los artículos del "curso", acompañados de una pequeña descripción donde indicaremos qué es lo que se trata en cada caso.

    Esperamos que os sirva de ayuda a todos aquellos que acabáis de aterrizar, y de repaso a los que ya están de vuelta : )

    martes, 25 de enero de 2011

    Esquema de un programa cobol

    Los programas cobol se dividen en partes llamadas "DIVISION" que a su vez se separan en secciones llamadas "SECTION". Vamos a ver el esquema básico que todo programa cobol debe tener:

    IDENTIFICATION DIVISION.
    PROGRAM-ID. PRUEBA1.

       Nombre del programa. Debe coincidir con el nombre externo del programa.

    ENVIRONMENT DIVISION.
    CONFIGURATION SECTION.
    SPECIAL-NAMES.
        DECIMAL-POINT IS COMMA.


       Nosotros usamos los puntos como separadores de miles y la coma como el separador de decimales.

    INPUT-OUTPUT SECTION.
    FILE-CONTROL.

       En esta parte se definen los ficheros en caso de que los haya.

        SELECT FICHERO1

       Nombre de fichero.

        ACCES MODE IS SEQUENTIAL

       Tipo de acceso: SEQUENTIAL para los planos, INDEXED para los indexados.

        FILE STATUS IS FS-FICHERO1

       Variable donde guardará el file-status (código de control de errores de acceso a ficheros).

    DATA DIVISION.
    FILE SECTION.
    FD FICHERO1 RECORDING MODE IS F

       Fichero con longitud fija. Si fuese de longitud variable pondríamos V.

        BLOCK CONTAINS 0 RECORDS
        RECORD CONTAINS 129 CHARACTERS.

       Longitud del fichero.

    01 REG-FICHERO1 PIC X(129).

       Donde guardaremos la información.

    WORKING-STORAGE SECTION.

       En ella definimos las variables que usaremos en el programa.

    01 WX-VARIABLE PIC X.

       Definición de variables lo veremos más adelante.

    LINKAGE SECTION.

       Área de comunicación con otros programas

    01 AREA-LINKAGE PIC X.
    PROCEDURE DIVISION. / PROCEDURE DIVISION USING AREA-LINKAGE.

       Si hay un área definida en la linkage debemos incluir el USING en la procedure.

    Aquí es donde va el programa en sí. La estructura general será:

    0000-PRINCIPAL.
        PERFORM 1000-INICIO

        PERFORM 2000-PROCESO
          UNTIL CUMPLE-CONDICION

        PERFORM 3000-FINAL
        .


    La numeración de párrafos suele ser esa, pero cada empresa puede tener su propia nomenclatura estándar.

    El proceso en un programa se suele repetir n veces, hasta que se cumple la condición indicada en el UNTIL.

    En el párrafo de final se incluye la instrucción de finalización de ejecución:
    STOP RUN para programas principales.
    GOBACK para rutinas.

    Se pueden definir tantos párrafos como queramos, pero la estructura de todos ellos será la siguiente:

    1000-INICIO.
        código con instrucciones a realizar
        .


    Es decir, todos los párrafos terminan con un punto "." que indica el final de párrafo.

    A tener en cuenta:
    En cobol no podemos empezar a escribir donde queramos, pues cada cosa tiene su sitio^^
    Un programa cobol mide de ancho 80 posiciones, aunque sólo escribiremos hasta la 72.

    ----+----1----+----2----+----3----+----4----+----5----+----6----+----7--
           01 WX-CAMPOS.
              05 WX-CAMPO1 PIC X.
              05 WX-LITERAL PIC X(40) VALUE 'LITERAL TAN GRANDE QUE NO CABE '
          -   'EN UNA LINEA Y LO TENEMOS QUE PARTIR'.
          *
           PROCEDURE DIVISION.
          ******************************************************************
           00000-PRINCIPAL.
          *
               PERFORM 10000-INICIO.


    * De la posición 1 a la 6: no se codifica nada.
    * Posición 7: se escribirá un * si queremos comentar la línea. En caso de que un texto no nos quepa en una línea, se escribirá en esta posición un guión "-" para continuarlo.
    * De la posición 8 a la 11: se denomina área A. Aquí escribiremos las "divisiones" y "secciones", los niveles 01 y los descriptores de ficheros "FD".
    * De la 12 a la 72: se denomina área B. Aquí se escribirán el resto de instrucciones del programa, y los niveles de variables subordinadas (02 en adelante).
    * De la 73 a la 80: no se codifica nada.

    Esto que acabamos de ver es algo muuuuy general, pero iremos viéndolo mejor con ejemplos.


    Y hasta aquí la primera lección. En el siguiente artículo veremos la WORKING-STORAGE, donde se definen las varibles que vamos a usar a lo largo del programa. Veremos los diferentes tipos de variables, el cálculo de longitudes y como se visualizan en un fichero.

    lunes, 24 de enero de 2011

    JCL básico V: IDCAMS, IEFBR14, IEBGENER.

    En este artículo vamos a describir algunos utilidades MVS básicas que pueden sernos útiles/necesarias al codificar un JCL.

    IDCAMS:
    El programa IDCAMS puede utilizarse para borrar ficheros, copiar ficheros y crear ficheros VSAM.
    Veamos algunos pasos de ejemplo:

    //******************************************************
    //*
    //**************************** BORRADO *****************
    //BORRADO   EXEC PGM=IDCAMS
    //SYSPRINT  DD SYSOUT=*
    //SYSIN DD *
           DEL NOMBRE.DE.FICHERO
     SET MAXCC = 0


    Borra los ficheros que le indiquemos en la SYSIN.
    Añadimos el "SET MAXCC = 0" para que en caso de que no exista el fichero no nos de error y continúe la ejecución del JCL.

    Opciones de borrado:
    * CLUSTER: indica que se tiene que borrar un fichero VSAM. Cuando se elimina el CLUSTER, se borran también los DATA e INDEX asociados al fichero.
    * PURGE: permite borrar un fichero aunque no haya llegado su fecha de caducidad.
    * ERASE: se machacan los datos del fichero con ceros binarios.


    //******************************************************
    //*
    //********************** COPIA DE FICHEROS *************
    //COPIA    EXEC PGM=IDCAMS
    //SYSPRINT DD SYSOUT=*
    //ENTRADA  DD DSN=FICHERO.DE.ENTRADA,DISP=SHR
    //SALIDA   DD DSN=FICHERO.DE.SALIDA,DISP=SHR
    //SYSIN DD *
      REPRO -
      INFILE(ENTRADA) -
      OUTFILE(SALIDA)
    /*


    Copia el fichero de entrada en el fichero de salida.
    Opciones de copiado:
    # SKIP: número de registros que deben saltarse desde el inicio del fichero antes de comenzar el proceso de copiado. También se pueden codificar:
    # COUNT: número de registros que queremos copiar.

    Comprobar si un fichero está vacío con PRINT + COUNT:
    //**=======================================================*
    //** PASO01 - COMPRUEBA SI EL FICHERO TIENE DATOS O ESTÁ VACIO
    //**=======================================================*
    //PASO01   EXEC PGM=IDCAMS
    //IN       DD DSN=nombre.fichero.prueba1,DISP=SHR
    //SYSPRINT DD SYSOUT=*
    //SYSIN    DD *
      PRINT INFILE(IN) COUNT(1)
      IF MAXCC=12 THEN SET MAXCC=4

    - Si existe y tiene datos --> Todo es correcto (MAXXCC=0) 
    - Si existe pero está vacío --> da un MAXXCC=4 
    - Si no existe --> El jcl abenda dando un FLUSH


    Creación de VSAM:

    //****************************************************
    //*
    //******************** DEFINICION DE VSAM ************
    //PASOXXXX EXEC PGM=IDCAMS
    //SYSPRINT DD SYSOUT=*
    //SYSIN DD *
      DELETE NOMBRE.FICHERO -
      CLUSTER
      SET MAXCC = 0
      DEFINE CLUSTER( -
          NAME(NOMBRE.FICHERO) -
          INDEXED -
          VOL(BCT668) -
          TRACKS(300 50) -
          RECSZ(4800 4800) -
          CISZ(12288) -
          KEY(16 0) -
          SHR(2 3) -
          FSPC(15 15)

    Crea el fichero VSAM NOMBRE.FICHERO.
    Opciones:
    * DELETE: Es la instrucción de borrado.
    * CLUSTER: Indica que borre todos los ficheros que componen el VSAM
    (índice y datos).
    * DEFINE CLUSTER: Definición del fichero VSAM.
    * NAME: Nombre físico del fichero.
    * INDEXED: Indica que el fichero es indexado.
    * VOL: El nombre del volumen/disco donde se quiere que resida. En
    nuestra instalación no es necesario ponerlo, ya que la asignación es
    automática.
    * TRACKS, CYLS, RECORDS: Determina el espacio en pistas, cilindros
    o registros. La primera magnitud indica el espacio de la extensión
    primaria y la segunda el espacio para las extensiones secundarias.
    * RECSZ: Longitud del registro. La primera magnitud indica la longitud.
    * CISZ:: Control Interval Size, longitud Intervalo de control. El intervalo de
    control es el equivalente al bloque en los ficheros VSAM.
    * KEY: Indica la información necesaria para ubicar la clave. La primera
    magnitud indica la longitud de la clave y la segunda el desplazamiento
    de la misma respecto del principio del registro.
    * SHR: Indica el grado de accesibilidad concurrente al fichero. (2 3) son
    los valores más usuales e indican que sólo puede haber un acceso
    concurrente de actualización y es de libre acceso de lectura.
    * FSPC: Free Space. Indica la distribución del espacio libre en el fichero,
    la primera magnitud indica el espacio libre en cada intervalo de control
    y la segunda indica el número de intervalos de control libres en el Area
    de Control.


    IEFBR14:
    Sirve para catalogar ficheros en vacío,borrarlos, etc. utilizando la sentencia DISP.

    //******************************************************
    //*
    //******************** CATALOGACION VACIO **************
    //PASO040  EXEC PGM=IEFBR14
    //*
    //FILEOUT  DD  DSN=NOMBRE.DE.FICHERO,
    //             DISP=(NEW,CATLG,DELETE),
    //             UNIT=SYSDA,
    //             SPACE=(350,(169,50),RLSE),
    //             RECFM=FB,LRECL=350
    /*

    Cataloga un fichero en vacío.
    Incluiremos en el DISP la información que consideremos.

    //*******************************************************
    //**** BORRADO DE FICHEROS TEMPORALES
    //*******************************************************
    //BORRADO  EXEC PGM=IEFBR14
    //DD1      DD DSN=FICHERO.A.BORRAR,DISP=(OLD,DELETE,KEEP)
    /*

    Borra el fichero indicado.


    IEBGENER:

    Sirve para copiar ficheros secuenciales de uno a otro.

    //*******************************************************
    //**** COPIA DE FICHERO SECUENCIAL
    //*******************************************************
    //PASOXXXX EXEC PGM=IEBGENER
    //SYSPRINT DD SYSOUT=*
    //SYSUT1   DD DSN=FICHERO.DE.ENTRADA
    //SYSUT2   DD DSN=FICHERO.DE.SALIDA
    //SYSIN  DD DSN=DUMMY
    /*

    Cuando es una copia simple en la SYSUT1 se codifica el fichero de entrada.
    Si en el SYSUT1 indicamos NULLFILE crearemos el fichero indicado en SYSUT2 en vacío.

    viernes, 21 de enero de 2011

    MERGE: unión de ficheros.

    Agrupa varios ficheros por el/los campo/s indicado/s.

    IMPORTANTE: para utilizar la sentencia MERGE, los ficheros de entrada deben haber sido previamente ordenados.

    ¡OJO!: Ahora el SORTIN cambia por SORTIN01, SORTIN02, etc.

    //SORT001  EXEC PGM=SORT,PARM=('DYNALLOC=(SYSALLDA,32)')
    //SORTIN01 DD DSN=nombre.fichero.entrada1,DISP=SHR
    //SORTIN02 DD DSN=nombre.fichero.entrada2,DISP=SHR
    //SORTINnn DD DSN=nombre.fichero.entradann,DISP=SHR
    //SORTOUT  DD DSN=nombre.fichero.salida1,
    //            DISP=(,CATLG,DELETE),SPACE=(CYL,(500,100))
    //SYSOUT   DD SYSOUT=*
    //SYSPRINT DD SYSOUT=*
    //SYSIN    DD *
     MERGE FIELDS=(I,L,T,O)

    I – Inicio. Posición donde empieza el campo.
    L – Longitud máxima del campo.
    T – Tipo de dato del campo que se quiere sumar:
       CH - Alfanumérico o numérico normal(sin COMP)
       BI - Hexadecimal (campos COMP)
       PD - Empaquetado con o sin signo(campos COMP-3)
    O – Orden. A-Ascendente, D- Descendente


    Ejemplo:

    Unir los siguientes ficheros por código de empleado:

    Fichero 1:
    ----+----1----+----2----+----3----+----4----+----5
    000000001JOSE      LOPEZ     PITA     AUTONOMO
    000000002JAVIER    MARTINEZ  CARRETEROASALARIADO
    000000003CARLOS    PEREZ     FANO     AUTONOMO
    000000004CARLOS    POLO      DEL BARROAUTONOMO
    000000005YOLANDA   LOPEZ     ALONSO   AUTONOMO
    000000006ANTONIO   VILLA     SUSO     AUTONOMO
    000000007FULANITO  VILLA     SUSO     AUTONOMO

    Fichero 2:
    ----+----1----+----2----+----3----+----4----+----5
    000000001JOSE      LOPEZ     PITA     AUTONOMO
    000000002JAVIER    MARTINEZ  CARRETEROASALARIADO
    000000003CARLOS    PEREZ     FANO     AUTONOMO
    000000008CARLOS    POLO      DEL BARROAUTONOMO
    000000009YOLANDA   LOPEZ     ALONSO   AUTONOMO
    000000010ANTONIO   VILLA     SUSO     AUTONOMO

    Fórmula:
    MERGE FIELDS=(1,9,CH,A)

    Resultado:
    ----+----1----+----2----+----3----+----4----+----5
    000000001JOSE      LOPEZ     PITA     AUTONOMO
    000000001JOSE      LOPEZ     PITA     AUTONOMO
    000000002JAVIER    MARTINEZ  CARRETEROASALARIADO
    000000002JAVIER    MARTINEZ  CARRETEROASALARIADO
    000000003CARLOS    PEREZ     FANO     AUTONOMO
    000000003CARLOS    PEREZ     FANO     AUTONOMO
    000000004CARLOS    POLO      DEL BARROAUTONOMO
    000000005YOLANDA   LOPEZ     ALONSO   AUTONOMO
    000000006ANTONIO   VILLA     SUSO     AUTONOMO
    000000007FULANITO  VILLA     SUSO     AUTONOMO
    000000008CARLOS    POLO      DEL BARROAUTONOMO
    000000009YOLANDA   LOPEZ     ALONSO   AUTONOMO
    000000010ANTONIO   VILLA     SUSO     AUTONOMO

    Al igual que con el SORT, el MERGE puede usarse con la opción COPY:
    MERGE FIELDS=COPY

    En este caso la estructura del JCL será la misma que la del SORT:

    //SORT001  EXEC PGM=SORT,PARM=('DYNALLOC=(SYSALLDA,32)')
    //SORTIN   DD DSN=nombre.fichero.entrada1,DISP=SHR
    //         DD DSN=nombre.fichero.entrada2,DISP=SHR
    //         DD DSN=nombre.fichero.entradann,DISP=SHR
    //SORTOUT  DD DSN=nombre.fichero.salida1,
    //            DISP=(,CATLG,DELETE),SPACE=(CYL,(500,100))
    //SYSOUT   DD SYSOUT=*
    //SYSPRINT DD SYSOUT=*
    //SYSIN    DD *
    MERGE FIELDS=COPY
    /*

    Ejemplo:

    Fichero 1:
    ----+----1----+----2----+----3----+----4----+----5
    000000001JOSE      LOPEZ     PITA     AUTONOMO
    000000002JAVIER    MARTINEZ  CARRETEROASALARIADO
    000000003CARLOS    PEREZ     FANO     AUTONOMO
    000000004CARLOS    POLO      DEL BARROAUTONOMO
    000000005YOLANDA   LOPEZ     ALONSO   AUTONOMO
    000000006ANTONIO   VILLA     SUSO     AUTONOMO
    000000007FULANITO  VILLA     SUSO     AUTONOMO

    Fichero 2:
    ----+----1----+----2----+----3----+----4----+----5
    000000001JOSE      LOPEZ     PITA     AUTONOMO
    000000002JAVIER    MARTINEZ  CARRETEROASALARIADO
    000000003CARLOS    PEREZ     FANO     AUTONOMO
    000000008CARLOS    POLO      DEL BARROAUTONOMO
    000000009YOLANDA   LOPEZ     ALONSO   AUTONOMO
    000000010ANTONIO   VILLA     SUSO     AUTONOMO

    Fórmula:
    MERGE FIELDS=COPY

    Resultado:
    ----+----1----+----2----+----3----+----4----+----5
    000000001JOSE      LOPEZ     PITA     AUTONOMO
    000000002JAVIER    MARTINEZ  CARRETEROASALARIADO
    000000003CARLOS    PEREZ     FANO     AUTONOMO
    000000004CARLOS    POLO      DEL BARROAUTONOMO
    000000005YOLANDA   LOPEZ     ALONSO   AUTONOMO
    000000006ANTONIO   VILLA     SUSO     AUTONOMO
    000000007FULANITO  VILLA     SUSO     AUTONOMO
    000000001JOSE      LOPEZ     PITA     AUTONOMO
    000000002JAVIER    MARTINEZ  CARRETEROASALARIADO
    000000003CARLOS    PEREZ     FANO     AUTONOMO
    000000008CARLOS    POLO      DEL BARROAUTONOMO
    000000009YOLANDA   LOPEZ     ALONSO   AUTONOMO
    000000010ANTONIO   VILLA     SUSO     AUTONOMO

    miércoles, 19 de enero de 2011

    ICETOOL(II): REPORT; informes.

    Continuando con la serie de artículos sobre ICETOOL, en esta ocasión vamos a presentar un artículo de como a partir de un fichero de entrada podemos presentar sus datos mediante un REPORT, con cabecera, roturas, detalle (en columnas), y totales.

    Report de un Fichero usando el comando DISPLAY.

    Fichero RECS:
    ----+----1----+----2--
    1111000021CCCCCCCCC010
    1111000021CCCCCCCCC090
    1111000021QQQQQQQQQ020
    1111000022VVVVVVVVV050

    JCL que realiza el cruce:

    //PASO010 EXEC PGM=ICETOOL
    //TOOLMSG DD SYSOUT=*
    //DFSMSG DD SYSOUT=*
    //TOOLIN DD *
      SORT FROM(RECS) TO(TMP) USING(CNTL1)
      DISPLAY FROM(TMP) LIST(REPORT)           -
      DATE TITLE('Facturacion XXXXXXX de la empresa') PAGE -
          BTITLE('Factura:')  BREAK(1,10,CH)               -
          HEADER('Documento') ON(12,10,CH)                 -
          HEADER('Importe')   ON(22,3,ZD)                  -
          BTOTAL('Total x Factura:')                        -
        BMINIMUM('Minimo x Factura:')                      -
        BMAXIMUM('Maximo x Factura:')                      -
        BAVERAGE('Media x Factura:')                       -
      BLANK
    //RECS    DD DISP=SHR,
    //           DSN=USER.ICETOOL.IN
    //TMP     DD DSN=&&TEMP,DISP=(MOD,PASS),
    //           SPACE=(TRK,(10,10)),UNIT=SYSDA
    //REPORT  DD SYSOUT=*
    //CNTL1CNTL DD *
      SORT FIELDS=(1,10,CH,A,12,9,CH,A)
    /*

    Para hacer un informe a partir de un fichero usaremos la sentencia DISPLAY con varios complementos que iremos viendo.
    En este informe utilizamos una ruptura por el campo "Factura", por lo que será importante ordenar el fichero por ese campo, para poder ver la información agrupada por Factura.

    Para este paso previo usamos la sentencia SORT con la sentencia USING (en este caso sólo hemos hecho un SORT FIELDS pero podría acompañarse de otras sentencias como INCLUDE COND para seleccionar un determinado número de registros):
     
    SORT FROM(RECS) TO(TMP) USING(CNTL1)
    ...
    //CNTL1CNTL DD *
      SORT FIELDS=(1,10,CH,A,12,9,CH,A)
    /*

    Para escribir el informe hacemos varios pasos:

    Mostramos el informe a partir del fichero temporal TMP (que está ordenado para nuestro propósito).
      DISPLAY FROM(TMP) LIST(REPORT)                     -

    Podemos tanto enviarlo a la cola de ejecución, como a un fichero. En [//REPORT  DD SYSOUT=*]  especificamos el tipo de salida que queramos.

    Mostramos la cabecera del informe.
      DATE TITLE('Facturación XXXXXXX de la empresa') PAGE -

    En este caso se va componer por:
    • La fecha (a la izquierda).
    • El título (en el centro).
    • El número de página (a la derecha).

    Mostramos el detalle del informe.
       BTITLE('Factura:')  BREAK(1,10,CH)               -
         HEADER('Documento') ON(12,10,CH)               -
         HEADER('Importe')   ON(22,3,ZD)                -

    En este caso se va a componer por:
    • Una ruptura (código de factura BREAK(posición incial, longitud, tipo).
    • Por cada factura dos columnas de detalle:
      • Documento (ON(posicion inicial, longitud, tipo).
      • Importe.

    Mostramos los totales por factura (total, mínimo, máximo y media de los importes de detalle por cada factura).
       BTOTAL('Total x Factura:')                        -
        BMINIMUM('Minimo x Factura:')                     -
        BMAXIMUM('Maximo x Factura:')                     -
        BAVERAGE('Media x Factura:')                      -

    Finalmente tenemos la sentencia BLANK cuya función es eliminar del informe espacios en blanco (es opcional).
      BLANK

    Esta es una manera sencilla de presentación de los datos de un fichero.
    Se podría ampliar con más cosas como totales generales, formatos de campos, etc.

    lunes, 17 de enero de 2011

    SORT vol.5: SUM.

    La estructura general de un SORT es la siguiente:

    //SORT001  EXEC PGM=SORT,PARM=('DYNALLOC=(SYSALLDA,32)')
    //SORTIN   DD DSN=nombre.fichero.entrada1,DISP=SHR
    //         DD DSN=nombre.fichero.entrada2(opcional),DISP=SHR
    //SORTOUT  DD DSN=nombre.fichero.salida1,
    //            DISP=(,CATLG,DELETE),SPACE=CYL,500,100))
    //SYSOUT   DD SYSOUT=*
    //SYSPRINT DD SYSOUT=*
    //SYSIN    DD *



    PGM=SORT --> Indica el programa a utilizar, en este caso el SORT.
    PARM=('DYNALLOC=(SYSALLDA,32)') --> Cantidad de memoria que se da a la ejecución del paso. Si se queda corto, aumentarla en valores de 8,12,32,64,128, 256 (como las memorias RAM)
    SORTIN --> Ficheros de entrada
    SORTOUT --> Ficheros de salida
    SYSIN --> Indica el tipo de sort a realizar, las opciones disponibles son muchas y muy variadas, pudiendo utilizarse varias juntas en un mismo paso. Algunas de ellas son SORT, SUM, OMIT, INCLUDE, INREC, OUTREC, OUTFIL, OPTION … .

    En este documento se explica en detalle algunas de estas funciones:

    SUM

    SUM FIELDS.
    Suma los valores del campo especificado. Sólo pueden sumarse campos ZD(numéricos), PD(comprimidos) o BI(hexadecimales). El resultado de la suma se guarda en un registro:

    //SORT001  EXEC PGM=SORT,PARM=('DYNALLOC=(SYSALLDA,32)')
    //SORTIN   DD DSN=nombre.fichero.entrada1,DISP=SHR
    //         DD DSN=nombre.fichero.entrada2,DISP=SHR
    //SORTOUT  DD DSN=nombre.fichero.salida1,
    //            DISP=(,CATLG,DELETE),SPACE=(CYL,(500,100))
    //SYSOUT   DD SYSOUT=*
    //SYSPRINT DD SYSOUT=*
    //SYSIN    DD *
      SORT FIELDS=(I,L,T,O)
       SUM FIELDS=(I,L,T)
    /*

    I – Inicio. Posición donde empieza el campo.
    L – Longitud máxima del campo.
    T – Tipo de dato del campo que se quiere sumar:
        CH - Alfanumérico o numérico normal(sin COMP)
        ZD - Numérico normal(sin COMP)
        BI - Hexadecimal (campos COMP)
        PD - Empaquetado con o sin signo(campos COMP-3)
    O – Orden. A-Ascendente, D- Descendente


    Ejemplo:

    Ordenar el siguiente fichero por código de empresa y acumular el saldo para cada una de ellas:

    ----+----1----+----2----+----3----+----4----+
    000000002EMPRESA CAFETERIA NOVELTY 00110000
    000000001EMPRESA LANAS MARUJA      00220000
    000000002EMPRESA CAFETERIA NOVELTY 00090000
    000000004EMPRESA ASESORIA ASOCIA   00100000
    000000002EMPRESA CAFETERIA NOVELTY 00110000
    000000004EMPRESA ASESORIA ASOCIA   00160000

    Fórmula:
    SORT FIELDS=(1,9,CH,A)
     SUM FIELDS=(36,8,ZD)


    Resultado:
    ----+----1----+----2----+----3----+----4----+
    000000001EMPRESA LANAS MARUJA      00220000
    000000002EMPRESA CAFETERIA NOVELTY 00310000
    000000004EMPRESA ASESORIA ASOCIA   00260000

    Lo que hace el SUM FIELDS es sacar un registro por cada campo distinto que haya en las posiciones indicadas en el SORT. En esos registros acumulará la cantidad que vaya en las posiciones indicadas por el SUM FIELDS.


    Otra utilidad del SUM es la de eliminar duplicados.

    Para eliminar registros que tengan un campo en concreto duplicado, indicaremos en la parte del SORT el campo que traerá los duplicados:
    SORT FIELDS=(I,L,T,O)
     SUM FIELDS=NONE


    Ejemplo:

    Eliminar los registros con código de empresa duplicado del siguiente fichero:
    ----+----1----+----2----+----3----+----4----+
    000000002EMPRESA CAFETERIA NOVELTY 00111111
    000000001EMPRESA LANAS MARUJA      00222222
    000000002EMPRESA CAFETERIA NOVELTY 00333333
    000000004EMPRESA ASESORIA ASOCIA   00444444
    000000002EMPRESA CAFETERIA NOVELTY 00555555
    000000004EMPRESA ASESORIA ASOCIA   00666666

    Fórmula:
    SORT FIELDS=(1,9,CH,A)
    SUM FIELDS=NONE


    Resultado:
    ----+----1----+----2----+----3----+----4----+
    000000001EMPRESA LANAS MARUJA      00222222
    000000002EMPRESA CAFETERIA NOVELTY 00111111
    000000004EMPRESA ASESORIA ASOCIA   00444444

    En la salida mostrará el primer registro (por el orden indicado) de cada código de empresa.

    En el SORT FIELDS podemos utilizar cualquiera de los campos del fichero. Por ejemplo, podríamos indicar que ordenase por nombre de la empresa:

    ----+----1----+----2----+----3----+----4----+
    000000001EMPRESA CAFETERIA NOVELTY 00111111
    000000003EMPRESA LANAS MARUJA      00222222
    000000003EMPRESA CAFETERIA NOVELTY 00333333
    000000004EMPRESA ASESORIA ASOCIA   00444444
    000000004EMPRESA CAFETERIA NOVELTY 00555555
    000000006EMPRESA ASESORIA ASOCIA   00666666

    Fórmula:
    SORT FIELDS=(10,26,CH,A)
    SUM FIELDS=NONE


    Resultado:
    ----+----1----+----2----+----3----+----4----+
    000000004EMPRESA ASESORIA ASOCIA   00444444
    000000001EMPRESA CAFETERIA NOVELTY 00111111
    000000003EMPRESA LANAS MARUJA      00222222


    El resultado es que se han eliminado los registros con nombre de empresa duplicado.

    viernes, 14 de enero de 2011

    ICETOOL(I): SPLICE; cruce de ficheros.

    Vamos a publicar una serie de articulos sobre el uso del ICETOOL.
    En esta ocasión explicaremos como cruzar dos fichero mediante un JCL, con ICETOOL.

    Cruce de dos ficheros usando el comando SPLICE.

    Vamos a hacer el caso de cruce 1:n  (un caso particular seria el caso 1:1)

    Fichero IN1:
    ----+----1----+----2----+
    4444 1111000021 1 YYYYYYYYY
    4444 1111000021 2 QQQQQQQQQ
    4444 1111000021 3 VVVVVVVVV
    4444 1111000021 4 CCCCCCCCC
    4444 1111000021 5 ZZZZZZZZZ
    4444 1111000021 6 DDDDDDDDD

    Fichero IN2:
    ----+----1----+----2----+----3----+
    000000000 CCCCCCCCC 016 1111000021 010
    000000000 QQQQQQQQQ 016 1111000021 020
    000000000 ZZZZZZZZZ 016 1111000021 030
    000000000 YYYYYYYYY 016 1111000021 050
    000000000 VVVVVVVVV 016 1111000021 050
    000000000 CCCCCCCCC 016 1111000021 090
    000000000 XXXXXXXXX 016 1111000021 010


    JCL que realiza el cruce:

    //PASO010 EXEC PGM=ICETOOL
    //TOOLMSG  DD SYSOUT=*
    //DFSMSG   DD SYSOUT=*
    //SHOWDEF  DD SYSOUT=*
    //IN1      DD DISP=SHR,
    //            DSN=USER.ICETOOL.IN1
    //IN2      DD DISP=SHR,
    //            DSN=USER.ICETOOL.IN2
    //TMP      DD DSN=&&TEMP,DISP=(MOD,PASS),
    //            SPACE=(TRK,(10,10)),UNIT=SYSDA
    //OUT      DD DISP=(,CATLG),SPACE=(TRK,(10,10),RLSE),
    //            DSN=USER.ICETOOL.OUT
    //TOOLIN   DD *
      COPY FROM(IN1) TO(TMP) USING(CTL1)
      COPY FROM(IN2) TO(TMP) USING(CTL2)
      SPLICE FROM(TMP) TO(OUT) ON(1,10,CH) ON(11,9,CH) -
      WITH(21,3) WITH(24,1) WITHALL KEEPNODUPS USING(CTL3)
    /*
    //CTL1CNTL DD *
      INREC BUILD=(1:5,10,11:16,9,20:15,1,24:C'BB')
    //CTL2CNTL DD *
      INREC BUILD=(1:22,10,11:10,9,21:32,3,24:C'VV')
    //CTL3CNTL DD *
      OUTFIL FNAMES=OUT,INCLUDE=(24,2,CH,EQ,C'VB'),OUTREC=(1,23)
    /*

    La idea básica para la realización del cruce consiste en usar un fichero de apoyo (un fichero temporal) que una ambos ficheros, donde la clave de cruce estará en la misma posición para todos los registros de los ficheros IN1 e IN2.
    Para lograr esto utilizaremos la sentencia COPY con la ayuda del USING, que nos permitirá usar "reglas de copiado" (en este caso hemos usando INREC, pero podriamos usar otras formas como OUTREC, condicionales IFTHEN, OVERLAY, ....).

    Para el IN1 tenemos :
    COPY FROM(IN1) TO(TMP) USING(CTL1)
    ...
    //CTL1CNTL DD *
      INREC BUILD=(1:5,10,11:16,9,20:15,1,24:C'BB')

    ----+----1----+----2----+
    4444 1111000021 1 YYYYYYYYY
    4444 1111000021 2 QQQQQQQQQ
    4444 1111000021 3 VVVVVVVVV
    4444 1111000021 4 CCCCCCCCC
    4444 1111000021 5 ZZZZZZZZZ
    4444 1111000021 6 DDDDDDDDD

    Para el IN2 tenemos:
    COPY FROM(IN1) TO(TMP) USING(CTL1)
    ....
    //CTL2CNTL DD *
      INREC BUILD=(1:22,10,11:10,9,21:32,3,24:C'VV')

    ----+----1----+----2----+----3----+
    000000000 CCCCCCCCC 016 1111000021 010
    000000000 QQQQQQQQQ 016 1111000021 020
    000000000 ZZZZZZZZZ 016 1111000021 030
    000000000 YYYYYYYYY 016 1111000021 050
    000000000 VVVVVVVVV 016 1111000021 050
    000000000 CCCCCCCCC 016 1111000021 090
    000000000 XXXXXXXXX 016 1111000021 010

    El fichero temporal tendría la siguiente estructura:
    ----+----1----+----2----+
    1111000021 YYYYYYYYY 1 BB   Este registro viene del fichero IN1
    1111000021 QQQQQQQQQ 2 BB   Este registro viene del fichero IN1
    1111000021 QQQQQQQQQ 030 VV   Este registro viene del fichero IN2
    1111000021 VVVVVVVVV 3 BB   Este registro viene del fichero IN1
    1111000021 CCCCCCCCC 4 BB   Este registro viene del fichero IN1
    1111000021 CCCCCCCCC 020 VV   Este registro viene del fichero IN2
    ...


    SPLICE FROM(TMP) TO(OUT) ON(1,10,CH) ON(11,9,CH) -
    WITH(21,3) WITH(24,1) WITHALL KEEPNODUPS USING(CTL3)

    IMPORTANTE: aunque acabemos de ver una estructura de fichero ordenada, la sentencia COPY no ordena. Será la sentencia SPLICE la que antes de realizar el cruce ordene por la clave especificada.

    SPLICE.
    Aquí nos detendremos para explicar cada una de las sentencias que acompañan a la sentencia SPLICE.

    SPLICE FROM(TMP) TO(OUT) ON(1,10,CH) ON(11,9,CH) -
      WITH(21,3) WITH(24,1) WITHALL KEEPNODUPS USING(CTL3)
    ...
    //CTL3CNTL DD *
      OUTFIL FNAMES=OUT,INCLUDE=(24,2,CH,EQ,C'VB')

    • Sentencia ON(posicion inicial, longitud, tipo).
      Esta sentencia nos permite especificar las claves de cruce. En este caso tenemos dos [ON(1,10,CH) ON(11,9,CH)]
    • Sentencia WITH(posicion inicial, longitud).
      Especificaremos aquellos campos del fichero IN2 que queramos que se vean en el fichero de salida. El resto de posiciones del fichero de salida se rellenarán con los campos del fichero IN1. En este caso hemos eligido 2 [WITH(21,3) WITH(24,1)]:

      ----+----1----+----2----+
      1111000021 CCCCCCCCC 4 B B
      1111000021 CCCCCCCCC 020 V V

    Antes de seguir vamos a hacer un breve parentesis para explicar los últimos campos añadidos para el fichero IN1 'BB' y para el fichero IN2 'VV'.
    Tiene dos funciones:
    1. Identificar en el fichero de salida los registros que provienen de cada fichero de entrada.
    2. Identificar los registros que se han cruzado con éxito.
      Al especificar con el WITH la primera posición del fichero IN2 [WITH(24,1)], cuando dos registros tengan la misma clave el registro de salida vendrá marcado en las dos últimas posiciones con VB.
    Asi:
    1. Registros con las dos últimas posiciones BB son aquellos registros del fichero de entrada IN1 que no encontraron correspondencia en el fichero de entrada IN2
    2. Registros con las dos últimas posiciones VV son aquellos registros del fichero de entrada IN2 que no encontraron correspondencia en el fichero de entrada IN1
    3. Registros con las dos últimas posiciones VB son aquellos registros que tuvieron correspondencia.

      ----+----1----+----2----+
      1111000021 CCCCCCCCC 1 020 V B

    • Sentencia WITHALL.
      En el caso de que una clave esté más de una vez en el fichero IN2, podemos especificar WITHALL para recoger todos los registros que la contengan. Si no se especifica se quedaría con el primero.
    Ejemplo:
    El registro del fichero IN1:
    ----+----1----+----2----+
    1111 1111000021 4 CCCCCCCCC

    Tiene la siguiente correspondencia en el fichero IN2:
    ----+----1----+----2----+----3----+
    000000000 CCCCCCCCC 016 1111000021 010
    000000000 CCCCCCCCC 016 1111000021 090

    Se almacena en el fichero temporal:
    ----+----1----+----2----+
    1111000021 CCCCCCCCC 4 BB   Registro del fichero IN1
    1111000021 CCCCCCCCC 020 VV   Registro del fichero IN2
    1111000021 CCCCCCCCC 090 VV   Registro del fichero IN2

    Si nosotros no especificásemos WITHALL veríamos la salida:
    ----+----1----+----2----+
    1111000021 CCCCCCCCC 4 020 V B

    Si nosotros SI especificásemos WITHALL veríamos la salida:
    ----+----1----+----2----+
    1111000021 CCCCCCCCC 4 020 V B
    1111000021 CCCCCCCCC 4 090 V B

    • Sentencia KEEPNODUPS.
      Se utiliza para mostrar en el fichero de salida, los registro del fichero IN1 cuya clave no existe en el fichero IN2.

    Ejemplo:
    El registro del fichero IN1:
    ----+----1----+----2----+
    1111 1111000021 6 DDDDDDDDD

    Se almacena en el fichero temporal:
    ----+----1----+----2----+
    1111000021 DDDDDDDDD 6     BB   Registro del fichero IN1

    Para nuestra clave sólo hay un registro (no encontró coincidencia en el fichero IN2).
    Si nosotros no especificásemos KEEPNODUPS, no veríamos el registro en la salida.

    Si lo especificamos veríamos el siguiente registro en la salida:
    ----+----1----+----2----+
    1111000021 DDDDDDDDD 6     B B

    • Sentencia USING.
      También fue usada cuando copiábamos los ficheros al temporal. Podríamos decir que son unas "reglas de copiado" para definir el fichero de salida. En este caso:
      • OUTFIL, para generar el fichero de salida OUT.
      • INCLUDE, para seleccionar aquellos registros que se cruzaron.
      • OUTREC, para eliminar las dos últimas posiciones de trabajo.

    Esto es una manera sencilla y rapida de hacer un cruce de dos ficheros.
    Es importante asegurarse de que el fichero de entrada no tenga duplicados por clave, es decir, es un cruce 1 a n.

    Para terminar, os dejo para descargar el JCL, los ficheros de entrada, y el fichero de salida:
    JCL con ICETOOL.
    Fichero IN1.
    Fichero IN2.
    Fichero OUT.