Mostrando entradas con la etiqueta pepegan. Mostrar todas las entradas
Mostrando entradas con la etiqueta pepegan. Mostrar todas las entradas

lunes, 21 de mayo de 2012

Utilidades REXX II: editar fichero desde JCL.

ACTUALIZADO: corregido el error que daba cuando el fichero no existía.

En esta ocasión os traemos un programa REXX, pequeño, fácil y para toda la familia. Con él podremos entrar a "ver" o "editar" un fichero desde el JCL, sólo con posicionar el cursor encima del nombre del fichero.

Unos lo llamarán comodidad, otros vagancia^^

Veamos el código REXX:

/* REXX */
/*EDIT DE FICHERO SEGUN POSICION DEL CURSOR DSN= */
MENSA=MSG('OFF')
"PROFILE NOPREFIX"
TRACE OFF
ARG A_OPC .

ADDRESS ISPEXEC "ISREDIT MACRO"
ADDRESS ISPEXEC "ISREDIT (LIN,COL) = CURSOR "
ADDRESS ISPEXEC "ISREDIT (LINEA) = LINE " LIN

X=OPCION() /* IDENTIFICAR OPCIÓN */
X=BFILE() /* BUSCA NOMBRE */
X=VFILE() /* VALIDA FILE */
RETURN 0

OPCION: /* IDENTIFICA LA OPCIóN ELEGIDA, EDIT O VIEW */
IF A_OPC = '' THEN
  DO
     SAY 'SELECCIONE OPCIóN: E (EDITAR) ó V (VIEW)'
     PARSE EXTERNAL A_OPC .
     IF A_OPC = '' THEN
        EXIT
     UPPER A_OPC
  END
RETURN 0

BFILE: /* BUSCA NOMBRE FICHERO EN LA LINEA */
F = INDEX(LINEA,'DSN=')
IF F=0 THEN
  DO
     FILE=STRIP(SUBWORD(SUBSTR(LINEA,COL),1,1),T,',')
     FILE=STRIP(FILE,,"'")
     F=INDEX(FILE,"')")
     IF F=0 THEN
        DO
           FILE=SUBSTR(FILE,1,F-1)
        END
  END
ELSE
  DO
     FILE=SUBWORD(SUBSTR(LINEA,F+4),1,1)
     F=INDEX(FILE,',')
     IF F<>0 THEN
        DO
           FILE = SUBSTR(FILE,1,F-1)
        END

     F = INDEX(FILE,'(')
     IF F <> 0 THEN
        DO
     /*-- EXTRAER VERSION DEL GDG --*/
          GDGVERS = SUBSTR(FILE, F+1, INDEX(FILE,')')-F-1)
          IF SUBSTR(GDGVERS, 1, 1) = '+' THEN
             IN = 0
          ELSE
             DO
                IF SUBSTR(GDGVERS, 1, 1) = '-' THEN
                   IN = SUBSTR(GDGVERS, 2, LENGTH(GDGVERS)-1)
                ELSE
                   IN = 0
             END
          FILE = SUBSTR(FILE,1,F-1)
    /*-- LISTA TODAS LAS VERSIONES DEL GDG. --*/
          Q = OUTTRAP(DATA.)
          "LISTC ENT('"||FILE||"')"
          Q = OUTTRAP(OFF)
    /*-- DETERMINA EL NOMBRE DE LA VERSION ACTUAL --*/
          REC = DATA.0 -(2*IN+1)
          PARSE VAR DATA.REC . . FILE
      END
  END
FILE=STRIP(FILE,,' ')
RETURN 0

VFILE: /* VALIDA FILE */
IF SYSDSN(FILE) <> 'OK' THEN
  ADDRESS ISPEXEC "ISREDIT LINE_AFTER "LIN" = MSGLINE <1>"
ELSE
  DO
     IF A_OPC = 'E' THEN
       ISPEXEC "EDIT DATASET('"FILE"')"
     IF A_OPC = 'V' THEN
       ISPEXEC "VIEW DATASET('"FILE"')"
  END
RETURN 0

Donde:
ARG A_OPC:
Variable donde guardaremos la opción "E" o "V"
"ISREDIT MACRO":
  Es una macro
"ISREDIT (LIN,COL) = CURSOR ":
  Posición del cursor
"ISREDIT (LINEA) = LINE " LIN:
  Línea
PARSE EXTERNAL A_OPC:
  Guarda el valor introducido en la variable A_OPC
INDEX(LINEA,'DSN='):
  El nombre del fichero estará después del 'DSN='.
FILE=STRIP(SUBWORD(SUBSTR(LINEA,COL),1,1),T,','):
  Recupera la cadena de caracteres que contiene el nombre del fichero.
GDGVERS = SUBSTR(FILE, F+1, INDEX(FILE,')')-F-1):
  Recupera la versión en la que estamos para el caso de que el fichero sea un GDG. Por ejemplo (-1).
SUBSTR(GDGVERS, 1, 1) = '+' THEN IN = 0:
  Para las versiones positivas mostraremos la última versión del GDG.
SUBSTR(GDGVERS, 1, 1) = '-':
  Si se trata de versiones negativas del GDG calculamos a cual corresponde.
"EDIT DATASET('"FILE"')":
  Abre el fichero en modo EDIT.
"VIEW DATASET('"FILE"')":
  Abre el fichero en modo VIEW.

Para ejecutar el programa tenemos dos opciones:
1. Escribir en la linea de comando el nombre del programa (por ejemplo REXEDI), colocar el cursor encima del nombre del fichero que queremos abrir y pulsar intro. Elegimos E o V y damos intro.
2. Guardar el nombre del programa REXX en una de nuestras KEYS. Colocamos el cursor encima del nombre del fichero y pulsar intro. Elegimos E o V y damos intro.

En el caso de que no os funcione, habrá que hacer esta comprobación:

En la línea de comandos escribimos "TSO ISRDDN". Nos saldrá algo de este estilo:

Volume   Disposition Act DDname   Data Set Name   Actions: B E V
TSST50   NEW,DEL    >    ISPCTL0  LIBRERIA1
CXDR21   SHR,KEEP   >    ISPILIB  LIBRERIA2
MVSC04   SHR,KEEP   >    ISPLLIB  LIBRERIA3
CXDR21   SHR,KEEP   >             LIBRERIA4
MVSC04   SHR,KEEP   >    ISPMLIB  LIBRERIA5
(...)


Buscaremos SYSEXEC escribiendo "O SYSEXEC". Nos mostrará algo de este estilo:

Volume   Disposition Act DDname   Data Set Name   Actions: B E V
CXDS01   SHR,KEEP   >    SYSEXEC  LIBRERIA6
MVSC09   SHR,KEEP   >             LIBRERIA7
PSTS01   SHR,KEEP   >             LIBRERIA8
MVSC09   SHR,KEEP   >             LIBRERIA9

Nuestro programilla REXX deberá estar en una de las librerías del SYSEXEC. La razón es que lo que estamos ejecutando es en realidad una macro.

Si se da el caso de que no tenemos acceso a ninguna de las librerías del SYSEXEC podemos añadir una nuestra. Para ello tenemos el siguiente código:

CONTROL NOFLUSH LIST

ALLOC F(SYSEXEC)  SHR REUSE DA('LIBRERIA6' +
                               'LIBRERIA7' +
                               'LIBRERIA8' +
                               'LIBRERIA9' +
                               'MILIBRERIA')

que escribiremos en un fichero (es un CLIST).
Lo ejecutaremos con un TSO EX FICHERO.CON.CODIGO.ANTERIOR.
Ahora al consultar el ISRDDN veremos que ya aparece en el SYSEXEC nuestra librería.

Perfecto, ya podemos ejecutar macros!

Si preferís que no os pida elegir entre editar "E" o visualizar "V", podemos tener dos macros: una para edit y otra para view.
Guardando el nombre de la macro en una de las KEYS, o escribiéndolo en la línea de comandos, sólo tendremos que posicionarnos en el nombre del fichero que queremos ver y dar intro. Según estemos ejecutando la macro de edit o la de view nos abrirá el fichero en edición o visualización respectivamente.

ACTUALIZADO: corregido el error que daba cuando el fichero no existía.
Os dejo la última versión del código REXX para la opción de editar:
Descargar EDI-FILE

Si tenéis cualquier problema con esto nos contáis e intentamos solucionarlo : )

lunes, 17 de octubre de 2011

Comandos TSO vol.3: línea de comandos.

En esta tercera parte de comandos TSO, explicaremos los comandos que se usan desde la linea de comandos:

BOOKMARKS en el editor de TSO.
Antes de empezar a comentar estos comandos, haremos una explicación sobre los bookmarks en un editor de texto.
Para poder crear un bookmark en una determinada linea de texto, sobre la columna que indica el numero de linea escribimos . seguido de un literal.
000010 Texto A
.A0011 Texto B
000012 Texto C
000013 Texto D


Nos mostraría la siguiente ventana.
000010 Texto A
.A     Texto B
000012 Texto C
000013 Texto D


Con esto hemos creado un marcador en nuestro texto.
A continuación vamos a ver la utilidad de estos marcadores.


Comandos básicos.

LINE. Comando linea
El comando LINE o de una forma abreviada L, es el primer comando de esta nueva entrega.
Se usa a través de la línea de comando, y su objetivo es ir a una linea concreta de nuestro texto.
Command ===> L 11              
000010 Texto A
000011 Texto B
000012 Texto C
000013 Texto D


Nos mostraría la siguiente ventana.
Command ===>                   
000011 Texto B
000012 Texto C
000013 Texto D


Si tuvieramos un marcador por ejemplo en la línea 12, podriamos usar el comando LINE con el marcador
Command ===> L .A              
000010 Texto A
000011 Texto B
.A     Texto C
000013 Texto D


Nos mostraría la siguiente ventana.
Command ===>                   
.A     Texto C
000013 Texto D



FIND. Busca un texto.
El comando FIND o de una forma abreviada F tiene como objetivo buscar una cadena de texto.
Command ===> F 'Te'              
000010 Texto A
000011 Texto B
000012 Texto C
000013 Texto D


Nos mostraría la siguiente ventana.
Command ===>                   
000010 Texto A
000011 Texto B
000012 Texto C
000013 Texto D


Vamos a combinar el comando FIND con los marcadores anteriormente comentados
Supongamos dos marcadores por ejemplo en las líneas 10 y 12, podemos el comando FIND para buscar texto entre un rango de líneas
Command ===> F 'Te' .A .B        
.A     Texto A
000011 Texto B
.B     Texto C
000013 Texto D


Nos mostraría la siguiente ventana.
Command ===>                   
.A     Texto A
000011 Texto B
.B     Texto C
000013 Texto D


Vamos un poco más allá. Vamos a combinar este comando con el comando BNDS mencionado en el articulo anterior.
Supongamos que queremos buscar una cadena de texto en una porción de texto usaremos el comando FIND con los marcadores y BNDS.
Queremos buscar la t entre las columnas 3 y 6 y entre las líneas 10 y 12.
1.-Tecleamos el comando COLS, para saber las posición de las columnas
2.-Tecleamos el comando BNDS, para establecer el intervalo (de columnas) en el que buscar
3.-Establecemos dos marcadores. Uno en la línea 10 y otro en la línea 12.
4.-Tecleamos el comando F t .A .B
Veamoslo en nuestro editor:
Command ===> F 't'  .A .B        
=COLS> ----+----1---
=BNDS>   <  >                
.A     Texto A
000011 Texto B
.B     Texto C
000013 Texto D


Nos mostraría la siguiente ventana.
Command ===>                   
=COLS> ----+----1---
=BNDS>   <  >                
.A     Texto A
000011 Texto B
.B     Texto C
000013 Texto D





















miércoles, 25 de mayo de 2011

Comandos TSO vol.2

En este artículo continuamos con los comandos TSO, muy útiles tanto para codificar como para editar texto.
Para los que no lo hayan leído, podéis ver los comandos básicos en "Comandos TSO vol.1: Comandos del editor".

OCULTAR / MOSTRAR LINEAS

Ocultar líneas.
El comando para ocultar líneas es la letra 'X'.

Si tecleamos una X sobre la columna de la línea 11:
000010 Texto A
X00011 Texto B
000012 Texto C
000013 Texto D


Ocultaría la línea 11.
000010 Texto A
- - - - - - - - - - - - - - - - - 1 Line(s) not Displayed
000012 Texto C
000013 Texto D


Si tecleamos X2 sobre la columna de la línea 11:
000010 Texto A
X20011 Texto B
000012 Texto C
000013 Texto D


Ocultaría 2 líneas a partir de la linea 11 incluída.
000010 Texto A
- - - - - - - - - - - - - - - - - 2 Line(s) not Displayed
000013 Texto D


Ocultar bloques de líneas.
El comando para ocultar bloques de líneas es 'XX ... XX'.

Si tecleamos una XX ... XX sobre la columna de las líneas 11 y 12:
000010 Texto A
XX0011 Texto B
XX0012 Texto C
000013 Texto D


Ocultaría las líneas que haya enter el primer XX y el último (incluídas las líneas sobre las que hayamos escrito el XX).
000010 Texto A
- - - - - - - - - - - - - - - - - 2 Line(s) not Displayed
000013 Texto D


Mostar líneas.
Los comandos para mostrar líneas son las letras 'S / F / L'.
Veamos cada uno de ellos.

Comando F: Muesta la(s) primera(s) línea(s)

Si tecleamos una F sobre la línea que nos muestra las líneas ocultas:
000010 Texto A
F - - - - - - - - - - - - - - - - 3 Line(s) not Displayed
000014 Texto E


Mostraría la línea 11.
000010 Texto A
000011 Texto B
- - - - - - - - - - - - - - - - - 2 Line(s) not Displayed
000014 Texto E


Si tecleamos F2 sobre la línea que nos muestra las líneas ocultas:
000010 Texto A
F2- - - - - - - - - - - - - - - - 3 Line(s) not Displayed
000014 Texto E


Mostraría 2 líneas a partir de la linea 10 (líneas 11 y 12).
000010 Texto A
000011 Texto B
000012 Texto C
- - - - - - - - - - - - - - - - - 1 Line(s) not Displayed
000014 Texto E


Comando L: Muestra la(s) última(s) línea(s).
Tiene un análogo comportamiento a F, pero en vez de mostrar las primeras líneas muestra las últimas, veamos el ejemplo:

Si tecleamos L2 sobre la línea que nos muestra las líneas ocultas:
000010 Texto A
L2- - - - - - - - - - - - - - - - 3 Line(s) not Displayed
000014 Texto E


Mostraría 2 líneas a partir de la linea 10 (líneas 12 y 13).
000010 Texto A
- - - - - - - - - - - - - - - - - 1 Line(s) not Displayed
000012 Texto C
000013 Texto D
000014 Texto E


Por último tenemos el comando S: Muestra la(s) línea(s) más significativas de un bloque de líneas ocultas.
No vamos a exponer el funcionamiento del mismo. Si alguien tiene interés lo puede preguntar y se lo explicaremos.


OTROS COMANDOS

BNDS. Establece límites de trabajo.

El comando BNDS nos permite fijar los márgenes izquierdo y derecho.
Para poder trabajar con este comando escríbelo sobre la columna que indica el numero de línea BNDS
000010 Texto A
BNDS11 Texto B
000012 Texto C
000013 Texto D


Nos mostraría la siguiente ventana.
000010 Texto A
=BNDS> <                      >
000011 Texto B
000012 Texto C
000013 Texto D


El símbolo < indica la posición del margen izquierdo. Por defecto es la primera posición.
El símbolo > indica la posición del margen derecho. Por defecto es la última posición.

El objetivo del BOUNDS es limitar la acción de los comandos entre el margen izquierdo y el margen derecho.
Lo vemos en un ejemplo:

Si tuviésemos:
=BNDS> <                      >
M00010 Texto A1
OO0011 Texto B
000012 Texto C
OO0013 Texto D


Nos daría el siguiente resultado:
000010 Texto B1
000011 Texto C1
000012 Texto D1


Sin embargo
Si tuviésemos:
=BNDS> <  >
M00010 Texto A1
OO0011 Texto B
000012 Texto C
OO0013 Texto D


Nos daría el siguiente resultado:
000010 Texto B
000011 Texto C
000012 Texto D


El 1 no se llevaría al resto de líneas ya que queda fuera de los márgenes establecidos por el BNDS.


COLS. Es un comando informativo que nos indica el número de columna.

Para poder trabajar con este comando escríbelo sobre la columna que indica el numero de línea COLS
000010 Texto A
COLS11 Texto B
000012 Texto C
000013 Texto D


Nos mostraría la siguiente ventana.
000010 Texto A
=COLS> ---+----1---+----2---+----3---+----4---+----5
000011 Texto B
000012 Texto C
000013 Texto D



MASK. Es un comando que nos permite definir la estrucutura de una linea.

Para poder trabajar con este comando escríbelo sobre la columna que indica el número de línea MASK
000010 Texto A
MASK11 Texto B
000012 Texto C
000013 Texto D


Nos mostraría la siguiente ventana.
000010 Texto A
=MASK>
000011 Texto B
000012 Texto C
000013 Texto D


La máscara por defecto es una línea en blanco. Cada vez que hagamos un insert de una nueva línea nos mostrará una línea en blanco.

Si tecleamos una I sobre la columna de la línea 11:
000010 Texto A
I00011 Texto B
000012 Texto C
000013 Texto D


Insertaría 1 línea para poder escribir nuestro texto a continuación de nuestra línea 11.
000010 Texto A
000011 Texto B
''''''
000012 Texto C
000013 Texto D


Cambiamos la máscara:
000010 Texto A
=MASK> *                 *
000011 Texto B
000012 Texto C
000013 Texto D


Si tecleamos una I sobre la columna de la línea 11:
000010 Texto A
I00011 Texto B
000012 Texto C
000013 Texto D


Insertaría 1 línea para poder escribir nuestro texto a continuación de nuestra línea 11.
000010 Texto A
000011 Texto B
'''''' *                 *
000012 Texto C
000013 Texto D


Nos puede ser útil en casos como añadir un párrafo de comentarios.

También dentro de este bloque podemos añadir el comando TABS que nos permite ver y cambiar la tabulación por defecto.
En este articulo no vamos a explica su fucionamiento, si alguien tiene interés lo puede preguntar y se lo explicaremos.

Con esto finalizamos este segundo volumen de comandos TSO.
Continuaremos explicando otros comandos, pero ahora ya desde la linea de comandos, en siguientes entregas.

miércoles, 20 de abril de 2011

Comandos TSO vol.1: Comandos del editor

El editor de TSO cuenta con una serie de comandos que nos ayudarán a escribir nuestro código.
En este volumen veremos los comandos básicos con algunos ejemplos.

Supongamos el siguiente trozo de texto en el editor:
000010 Texto A
000011 Texto B
000012 Texto C
000013 Texto D


El bloque de la izquierda (verde) nos indica el número de línea del texto.
El bloque de la derecha (amarillo) contiene el texto de cada línea.

Los comandos se teclearán sobre la columna de los números de línea.


INSERTAR / BORRAR / DUPLICAR LINEAS

Insertar líneas.
El comando para insertar líneas es la letra 'I'.

Si tecleamos una I sobre la columna de la línea 11:
000010 Texto A
I00011 Texto B
000012 Texto C
000013 Texto D


Insertaría 1 línea para poder escribir nuestro texto a continuación de nuestra línea 11.
000010 Texto A
000011 Texto B
'''''' En esta sección amarilla escribiremos nuestro texto
000012 Texto C
000013 Texto D


Al pulsar INTRO quedaría:
000010 Texto A
000011 Texto B
000012 En esta sección amarilla escribiremos nuestro texto
000013 Texto C
000014 Texto D


Si en el texto inicial tecleamos I3 sobre la columna de la línea 11:
000010 Texto A
I30011 Texto B
000012 Texto C
000013 Texto D


Insertaría 3 líneas para poder escribir nuestro texto a continuación de la línea 11.
000010 Texto A
000011 Texto B
''''''
''''''
''''''
000012 Texto C
000013 Texto D



Borrar líneas.
El comando para borrar líneas es la letra 'D'.

Si tecleamos una D sobre la columna de la línea 11:
000010 Texto A
D00011 Texto B
000012 Texto C
000013 Texto D


Borraría la línea 11.
000010 Texto A
000012 Texto C
000013 Texto D


Si tecleamos D2 sobre la columna de la línea 11:
000010 Texto A
D20011 Texto B
000012 Texto C
000013 Texto D


Borraría 2 líneas a partir de la linea 11 incluída.
000010 Texto A
000013 Texto D


Borrar bloques de líneas.
El comando para borrar bloques de líneas es 'DD ... DD'.

Si tecleamos una DD ... DD sobre la columna de las líneas 11 y 12:
000010 Texto A
DD0011 Texto B
DD0012 Texto C
000013 Texto D


Borraría las líneas que haya enter el primer DD y el último (incluídas las líneas sobre las que hayamos escrito el DD).
000010 Texto A
000013 Texto D



Replicar líneas.
El comando para replicar líneas es la 'R'.

Si tecleamos una R sobre la columna de la línea 11:
000010 Texto A
R00011 Texto B
000012 Texto C
000013 Texto D


Replicaría la línea 11.
000010 Texto A
000011 Texto B
000012 Texto B
000013 Texto C
000014 Texto D


Si tecleamos R2 sobre la columna de la línea 11:
000010 Texto A
R20011 Texto B
000012 Texto C
000013 Texto D


Replicaría la línea 11 dos veces.
000010 Texto A
000011 Texto B
000012 Texto B
000013 Texto B
000014 Texto C
000015 Texto D


Replicar bloques de líneas.
El comando para replicar bloques de líneas es 'RR ... RR'.

Si tecleamos una RR ... RR sobre la columna de las líneas 11 y 12:
000010 Texto A
RR0011 Texto B
RR0012 Texto C
000013 Texto D


Replicaría las líneas que hay entre el primer RR y el último (ambas incluídas).
000010 Texto A
000011 Texto B
000012 Texto C
000013 Texto B
000014 Texto C
000015 Texto D



MOVER / COPIAR LINEAS

Para mover y/o copiar líneas entran dos comandos en juego: el que selecciona la línea a copiar / mover y el que nos indica el lugar donde se va a copiar o mover.
Para la primera parte tenemos comandos C->copiar línea y M->mover línea.
Para la segunda parte tenemos comandos A->después de, B->antes de y O->sobre.

Dado el siguiente código en nuestro editor:
000010 Texto A
000011 Texto B
000012 Texto C
000013 Texto D


Copiar líneas
Si queremos copiar la línea de texto "Texto B" entre las líneas de texto "Texto C" y "Texto D" podríamos hacer:
000010 Texto A
C00011 Texto B
000012 Texto C
B00013 Texto D


O bien
000010 Texto A
C00011 Texto B
A00012 Texto C
000013 Texto D


En el primer caso nos indica que vamos a copiar la línea antes de la línea de texto "Texto D". En el segundo caso vamos a copiar la línea después de la línea de texto "Texto C". En ambos casos el resultado sería:
000010 Texto A
000011 Texto B
000012 Texto C
000013 Texto B
000014 Texto D


Mover líneas
Si en vez de copiar la línea, la quisiésemos mover podríamos hacer:
000010 Texto A
M00011 Texto B
000012 Texto C
B00013 Texto D


O bien
000010 Texto A
M00011 Texto B
A00012 Texto C
000013 Texto D


En ambos casos el resultado sería:
000010 Texto A
000011 Texto C
000012 Texto B
000013 Texto D


Si quisiésemos copiar una linea varias veces podríamos hacer:
000010 Texto A
C00011 Texto B
000012 Texto C
B20013 Texto D


O bien
000010 Texto A
C00011 Texto B
A20012 Texto C
000013 Texto D


El resultado sería:
000010 Texto A
000011 Texto B
000012 Texto C
000013 Texto B
000014 Texto B
000015 Texto D



Mover / Copiar bloques de líneas
Para copiar bloques usaremos el comando CC ... CC. Y para mover bloques MM ... MM.
En ambos casos siguen siendo válidos (y necesarios) los comandos A (después de) y B (antes de).

Copiar un bloque.
CC0010 Texto A
CC0011 Texto B
000012 Texto C
B00013 Texto D


Nos daría el siguiente resultado:
000010 Texto A
000011 Texto B
000014 Texto C
000012 Texto A
000013 Texto B
000015 Texto D


Mover un bloque.
MM0010 Texto A
MM0011 Texto B
000012 Texto C
A00013 Texto D


Nos daría el siguiente resultado:
000010 Texto C
000011 Texto D
000012 Texto A
000013 Texto B


Mover / Copiar líneas sobre ...
Hemos dejado para el final el comando O, por tener un comportamiento distinto a los comandos A y B.

Veamos funcionar este comando en un ejemplo.
000010 Texto A
C00011 Texto B1
O00012 Texto C
000013 Texto D


Nos daría el siguiente resultado:
000010 Texto A
000011 Texto B1
000012 Texto C1
000013 Texto D


Del mismo modo si tuviésemos:
000010 Texto A
M00011 Texto B1
O00012 Texto C
000013 Texto D


Nos daría el siguiente resultado:
000010 Texto A
000011 Texto C1
000012 Texto D


NOTA: Como se puede observar, este último comando actúa sobre aquellas posiciones que están informadas a blancos. Sobre las que están informadas no hace nada.
Así la letra C (de la línea de destino) permanece, y le añade un 1 cuyo origen está en la linea inicial.

Al igual que A y B, el comando O admite On, donde n es un número.
Si tuviésemos:
C00010 Texto A1
O30011 Texto B
000012 Texto C
000013 Texto D


Nos daría el siguiente resultado:
00010 Texto A1
000011 Texto B1
000012 Texto C1
000013 Texto D1


También adimite bloques. Por ejemplo si tuviésemos:
M000010 Texto A1
OO0011 Texto B
000012 Texto C
OO0013 Texto D


Nos daría el siguiente resultado:
000010 Texto B1
000011 Texto C1
000012 Texto D1


Esto es aplicable también al comando de copiar ('C') y también a copiar y mover con bloques ('CC...CC' y 'MM...MM').


DESPLAZAR TEXTO SOBRE UNA LINEA

Los comandos para manipular la posición del texto en una línea son '(' y '<' para desplazamiento a la izquierda, ')' y '> para desplazamiento a la derecha.
Pero veamos cada unos de los comandos en detalle con ejemplos:

Dado el siguiente código en nuestro editor:
000010 Texto A
000011 Texto B
000012 Texto C
000013 Texto D


Si tuviésemos la siguiente situación:
000010 Texto A
(00011   Texto B
000012 Texto C
000013 Texto D


Desplazaría el texto de la línea 2 posiciones a la izquierda.
000010 Texto A
000011 Texto B
000012 Texto C
000013 Texto D


Análogo resultado tendremos con el siguiente ejemplo:
000010 Texto A
<00011   Texto B
000012 Texto C
000013 Texto D


Sin embargo estos comandos tienen diferencias. Veamos el siguiente ejemplo:
<00010   Texto A
(00011   Texto B
<00012 Texto C
(00013 Texto D
<00014   Texto  E
(00015   Texto  F


El resultado sería:
000010 Texto A
000011 Texto B
==ERR> Texto C
000013 xto D
000014 Texto  E
000015 Texto F


¿Qué ha sucedido?
El comando ( elimina dos posiciones de texto desde la posición 1.
El comando < elimina dos espacios desde la posición 1.
He ahí que la línea 000012 al intentar desplazar el texto con el comando < nos haya dado un error (las dos primeras posiciones son distintas de espacio).

Otra diferencia entre ambos comandos la vemos en las líneas 000014 y 000015
El comando ( elimina dos posiciones de texto desplazando toda la línea.
El comando < elimina dos espacios desplazando sólo el primer trozo de texto que esté separado por más de dos espacios.

Un compartamiento análogo ocurriría entre > y ). Veámoslo en un ejemplo:

Supongamos que estamos editando un fichero de 13 posiciones.
=COLS> ----+----1---
>00010       Texto A
)00011       Texto B
>00012 Texto C
)00013 Texto D
>00014   Texto  E
>00015   Texto   G
)00016   Texto  F


El resultado sería:
=COLS> ----+----1---
==ERR>       Texto A
000011         Texto
000012   Texto C
000013   Texto D
000014     Texto E
000015     Texto G
000015     Texto  F


Vemos que el comportamiento es el esperado, pero en sentido contrario. En vez de a la izquierda a la derecha.
El único compartamiento que nos puede sorprender es el de la línea 000014. En este caso lo que ha ocurrido con el comando >, al tratar de desplazar el primer trozo de texto que esté separado por más de dos espacios, es que el primer trozo de texto es "Texto" pues el resto de la línea ("E") estaba separado de "Texto" dos espacios. Al desplazar un espacio "Texto", ese primer trozo ha dejado de ser "Texto" para ser "Texto E". Y por tanto al desplazar el segundo espacio ha desplazado "Texto E".

Además cada uno de estos comandos pueden ser seguidos de un número < n ó (n ó >n ó )n donde n es un número.
También es válido para bloques: << ... << n ó (( ... ((n ó >> ... >>n ó )) ... ))n donde n es un número (si no viene especificado por defecto es 2).

Veámoslo en un ejemplo:
=COLS> ----+----1----+----2
))0010      Texto  A
000011      Texto  B
))5012      Texto  C
<<0013      Texto  D
<<3014      Texto  E
000015      Texto  G
000016      Texto  F


Veamos el resultado:
=COLS> ----+----1----+----2
000010           Texto  A
000011           Texto  B
000012           Texto  C
000013   Texto     D
000014   Texto     E
000015      Texto  G
000016      Texto  F

lunes, 11 de abril de 2011

CONSULTAS SQL. PARTE I

Vamos a presentar una serie de artículos para mostar las distintas posibilidades que puede ofrecer el SQL.
En esta primera parte nos centraremos en las JOINs de tablas.

LEFT OUTER JOIN

Supongamos la siguiente situación, una lista de alumnos que no estén matriculados en alguna asignatura obligatoria.

Para ello tenemos las entidades ALUMNO (alumnos matriculados en una universidad), ASIGNATOBL (lista de asignaturas obligatorias, existente en los grados) y ASIGNATOPC (lista de asignaturas opcionales, existente en los grados).

Entidad ALUMNO.
idPersona | idAsignatura | idCurso | idGrado
       P1            OB1        C1        G1
       P1            OB2        C1        G1
       P2            OP1        C1        G1
       P3            OP2        C1        G1
       P4            OP1        C1        G1
       P4            OB2        C1        G1
       P5            OB1        C1        G1
       P6            OP2        C1        G1


Donde la clave primaria es idPerona, idAsignatura, idCurso, idGrado.

Entidad ASIGNATOBL

idAsignatura| idCurso | idGrado | Nombre   | Duración
         OB1       C1        G1   ASIGOB1    S
         OB2       C1        G1   ASIGOB2    S
         OB3       C1        G1   ASIGOB3    S


Donde la clave primaria es idAsignatura, idCurso, idGrado.

Entidad ASIGNATOPC

idAsignatura| idCurso | idGrado | Nombre   | Duración
         OP1       C1        G1   ASIGOP1    S
         OP2       C1        G1   ASIGOP2    S
         OP3       C1        G1   ASIGOP3    S


Donde la clave primaria es idAsignatura, idCurso, idGrado.

La Consulta que haríamos:

SELECT A.idPersona, B.Nombre, B.Duracion
  FROM ALUMNO A
       LEFT OUTER JOIN
       ASIGNATOBL B
    ON A.idAsignatura = B.idAsinatura
   AND A.idCurso = B.idCurso
   AND A.idGrado = B.idGrado


Esto crea una nueva tabla resultante, que tendrá tantos registros como la entidad ALUMNO. El campo idPersona proviene de la tabla ALUMNOS y por tanto vendrá siempre informado a no NULOS. Los campos Nombre y Duración provienen de la tabla ASIGNATOBL en el caso de que coincidan las claves vendrán informados con valor en otro caso vendrán informados a NULOS.

El resultado de la consulta sería:
idPersona | Nombre       | Duración
       P1   ASIGOB1        S
       P1   ASIGOB2        S
       P2   NULL           NULL
       P3   NULL           NULL
       P4   NULL           NULL
       P4   ASIGOB2        S
       P5   ASIGOB1        S
       P6   ASIGOB2        S
       P7   NULL           NULL 


Vamos a comentar un par de cosas sobre esta consulta:

El bloque sobre el que estamos uniendo ambas entidades(tablas)
   ON A.idAsignatura = B.idAsinatura
  AND A.idCurso = B.idCurso
  AND A.idGrado = B.idGrado


El bloque sobre el que estamos definiendo el tipo de interacción de las tablas:
 FROM ALUMNO A
      LEFT OUTER JOIN
      ASIGNATOBL B

Aquí también es importante decir que cuando hacemos un "A LEFT OUTER JOIN B", las diferentes claves de interacción de la tablas (A y B), tienen que estar todas ellas en la tabla A, pudiendo no estar obligatoriamente todas en la tabla B. Es decir, A contiene B.

Podríamos añadir una sentencia WHERE [WHERE B.Nombre IS NULL].
En este punto debemos hacer una pausa. La sentencia WHERE es una condición de filtrado sobre la tabla temporal resultante de unir las tablas A y B. Es decir:
    ON A.idAsignatura = B.idAsinatura
   AND A.idCurso = B.idCurso
   AND A.idGrado = B.idGrado
   AND B.Nombre IS NULL

Es distinto a:
    ON A.idAsignatura = B.idAsinatura
   AND A.idCurso = B.idCurso
   AND A.idGrado = B.idGrado
 WHERE B.Nombre IS NULL


En el primer caso [AND B.Nombre IS NULL]. Usaría una condicion más para la creación de la nueva tabla temporal (en este caso no haríamos ningún filtro pues en la tabla B no hay valores nulos sobre la columna Nombre).

En el sengundo caso [WHERE B.Nombre IS NULL]. Usaría la condición para filtrar el resultado de la nueva tabla temporal.
El resultado de la consulta sería:
idPersona | Nombre       | Duración
       P2   NULL           NULL
       P3   NULL           NULL
       P4   NULL           NULL
       P7   NULL           NULL 



INNER JOIN(modo explícito)

Supongamos la siguiente situación, nos piden la información de qué alumnos están matriculados en alguna asignatura obligatoria, e información sobre dicha asignatura.

La Consulta que haríamos:

SELECT A.idPersona, B.Nombre, B.Duracion
  FROM ALUMNO A
       INNER JOIN
       ASIGNATOBL B
    ON A.idAsignatura = B.idAsinatura
   AND A.idCurso = B.idCurso
   AND A.idGrado = B.idGrado


Esto crea una nueva tabla resultante, que tendrá aquellos registros en los que las claves de ambas tablas coincidan. A diferencia del anterior caso los campos idPersona, Nombre y Duración vendrán informado a no NULOS.

El resultado de la consulta sería:
idPersona | Nombre       | Duración
       P1   ASIGOB1        S
       P1   ASIGOB2        S
       P4   ASIGOB2        S
       P5   ASIGOB1        S
       P6   ASIGOB2        S


Este sería un caso particular del LEFT OUTER JOIN. Podemos representar el INNER JOIN con la siguiente consulta creada con LEFT OUTER JOIN:

SELECT A.idPersona, B.Nombre, B.Duracion
  FROM ALUMNO A
       LEFT OUTER JOIN
       ASIGNATOBL B
    ON A.idAsignatura = B.idAsinatura
   AND A.idCurso = B.idCurso
   AND A.idGrado = B.idGrado
 WHERE B.Nombre IS NOT NULL



El resultado de la consulta sería:
idPersona | Nombre       | Duración
       P1   ASIGOB1        S
       P1   ASIGOB2        S
       P4   ASIGOB2        S
       P5   ASIGOB1        S
       P6   ASIGOB2        S



INNER JOIN(modo implícito)
Veamos la INNER JOIN anterior expresada de otra forma:

SELECT A.idPersona, B.Nombre, B.Duracion
  FROM ALUMNO A
      ,ASIGNATOBL B
 WHERE A.idAsignatura = B.idAsinatura
   AND A.idCurso = B.idCurso
   AND A.idGrado = B.idGrado


Es otra manera de expresar la inner join de dos tablas (tal vez la más conocida, la que primero que se estudia).

EL resultado es el mismo:
idPersona | Nombre       | Duración
       P1   ASIGOB1        S
       P1   ASIGOB2        S
       P4   ASIGOB2        S
       P5   ASIGOB1        S
       P6   ASIGOB2        S



RIGHT OUTER JOIN
Es un caso particular del LEFT OUTER JOIN. Mientras que en la LEFT, A contiene a B, en la RIGHT B contiene a A.

Veámoslo con un ejemplo:
Cuando explicábamos la LEFT OUTER JOIN, en el caso que proponíamos ("obtener la información de qué alumnos están matriculados en alguna asignatura obligatoria, e información sobre dicha asignatura"), llegamos a la siguiente consulta:

SELECT A.idPersona, B.Nombre, B.Duracion
  FROM ALUMNO A
       LEFT OUTER JOIN
       ASIGNATOBL B
    ON A.idAsignatura = B.idAsinatura
   AND A.idCurso = B.idCurso
   AND A.idGrado = B.idGrado


Esta consulta se puede expresar con la RIGHT OUTER JOIN obteniendo el mismo resultado. La consulta sería:

SELECT A.idPersona, B.Nombre, B.Duracion
  FROM ASIGNATOBL A
       RIGHT OUTER JOIN
       ALUMNO B
    ON A.idAsignatura = B.idAsinatura
   AND A.idCurso = B.idCurso
   AND A.idGrado = B.idGrado



FULL OUTER JOIN
Para este ultimo caso vamos a hacer un estudio entre dos alumnos. En este estudio nos piden información sobre qué asignaturas comparten, y cuales no.

Para esto primero seleccionamos las asignaturas en las que están matriculados cada uno de ellos. Haremos las siguientes consultas:

SELECT idAsinatura, B.Nombre, B.Duracion
  FROM ALUMNO
 WHERE A.idPersona = 'P5'


Análogamente hacemos la misma consulta para el segundo alumno:

SELECT idAsinatura, B.Nombre, B.Duracion
  FROM ALUMNO
 WHERE A.idPersona = 'P6'


Para extraer la información deseada hacemos una FULL OUTER JOIN entre ambas tablas temporales creadas. Nuestra consulta sería:

SELECT A.idAsignatura AS ASIGNP5, B.idAsignatura AS ASIGNP6
  FROM (
       SELECT idAsignatura
         FROM ALUMNO
        WHERE idPersona = 'P5') A
       FULL OUTER JOIN (     
       SELECT idAsignatura
         FROM ALUMNO
        WHERE idPersona = 'P6') B
    ON A.idAsignatura = B.idAsignatura   


NOTA: En esta consulta hemos introducido un nuevo concepto de SELECT anidadas, que explicaremos en un artículo posterior. Quedémonos con la idea de que estamos haciendo una FULL OUTER JOIN sobre dos tablas, tablas temporales que tienen su raiz en una SELECT.

Esta consulta creará una nueva tabla que tendrá tantos registros como claves dierentes existan entre las dos tablas usadas para crear el FULL OUTER JOIN. Así nuestra nueva tabla temporal, será la siguiente:

ASIGNP5 | ASIGNP6
   NULL      OP2
   OB1       NULL


Esta tabla sería la base para nuestro estudio pedido:

Si añadiésemos una sentencia WHERE [WHERE ASIGNP5 IS NOT NULL AND ASIGNP6 IS NOT NULL ] tendríamos la lista de las asignaturas en las que ambos están matriculados. En este caso la consulta sería vacía (no comparten las asiganturas en las que están matriculados).

NOTA: La sentencia WHERE tiene el mismo comportamiento que explicamos anteriormente con la LEFT OUTER JOIN.

Si añadiésemos una sentencia WHERE [WHERE ASIGNP5 IS NOT NULL] tendriamos la lista de las asignaturas en las que está matriculado el alumno P5 pero no el alumno P6. En este caso la consulta nos devolvería la asignatura OB1.

Si añadiésemos una sentencia WHERE [WHERE ASIGNP6 IS NOT NULL] tendriamos la lista de las asignaturas en las que está matriculado el alumno P6 pero no el alumno P5. En este caso la consulta nos devolvería la asignatura OP2.


Hasta aquí hemos hecho un breve repaso de los maneras más frecuentes que nos podemos encontrar a la hora de relacionar dos tablas en una consulta SQL. Espero que os sirva de ayuda y, si teneis alguna duda, no dudéis en preguntarla.

miércoles, 16 de marzo de 2011

EASYTRIEVE(I): Cruce ficheros 1:1

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

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

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

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

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

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

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

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

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

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

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

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

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

La sentencia PUT nos escribe en el fichero de salida

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

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

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

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

 SECUEN.   IN1   IN2    

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

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

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

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

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

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

lunes, 14 de febrero de 2011

Sentencia SORT en un programa COBOL

Hemos visto en otros artículos como hacer un SORT en un JCL. En este artículo veremos como hacerlo en un programa cobol.
¿Utilidad? Depende. Lo cierto es que pudiendo hacerlo por JCL, no tiene caso hacerlo en un programa. Pero quién sabe! Tal vez alguno de los lectores puedan darnos una idea de su uso práctico : D

SORT:

La sentencia SORT en cobol sirve para ordenar registros por un campo clave que le indiquemos. Podremos elegir varias claves y definir si el orden será ascendente o descendente.
Para el ejemplo, hemos creado un fichero temporal en nuestro programa que utilizaremos para generar en él la información ordenada. Como registros a ordenar, utilizaremos una tabla interna, de tal forma que no necesitaremos utilizar ficheros en ningún momento.
La información ordenada se cargará desde el fichero temporal al registro de salida, que podría utilizarse a lo largo de la ejecución.
Nosotros nos pararemos cuando tengamos nuestra información ordenada, y la "displayaremos" mostrándola por SYSOUT.

 IDENTIFICATION DIVISION.
 PROGRAM-ID. PRGSORT.
*
 ENVIRONMENT DIVISION.
 CONFIGURATION SECTION.
 SPECIAL-NAMES.
 DECIMAL-POINT IS COMMA.
*
 INPUT-OUTPUT SECTION.
 FILE-CONTROL.
* Fichero temporal donde guardaremos la información ordenada
 SELECT TABLA-SORT ASSIGN TO DISK "SORTWORK".
*
 DATA DIVISION.
 FILE SECTION.
* Definición del fichero temporal
 SD TABLA-SORT
 DATA RECORD IS ELEMENTO-SORT.
 01 ELEMENTO-SORT.
    05 SORT-CLAVE1 PIC X.
    05 SORT-CLAVE2 PIC X(3).
    05 SORT-CAMPO PIC X(10).
    05 SORT-INDICADOR PIC X.
*
 WORKING-STORAGE SECTION.
* Formato del registro de salida
 01 VARIABLES.
    05 WA-REGISTRO.
       10 WA-SORT-CLAVE1 PIC X.
       10 WA-SORT-CLAVE2 PIC X(3).
       10 WA-SORT-CAMPO PIC X(10).
       10 WA-SORT-INDICADOR PIC X.
* Switches que utilizaremos en los bucles
 01 SWITCHES.
    05 SW-FIN-TABLA-SORT PIC X(1).
       88 SI-FIN-TABLA-SORT VALUE 'S'.
       88 NO-FIN-TABLA-SORT VALUE 'N'.
* Tabla interna con los datos a ordenar
 01 TABLA.
    05 WT-TBL-LISTA.
       10 PIC X(15) VALUE 'F216CAMPO02802S'.
       10 PIC X(15) VALUE 'M144CAMPO17114N'.
       10 PIC X(15) VALUE 'Q651CAMPO24536S'.
       10 PIC X(15) VALUE 'F217CAMPO03312N'.
       10 PIC X(15) VALUE 'T487CAMPO44914S'.
       10 PIC X(15) VALUE 'O372CAMPO52113N'.
       10 PIC X(15) VALUE 'F457CAMPO61224N'.
       10 PIC X(15) VALUE 'L547CAMPO73985N'.
       10 PIC X(15) VALUE 'L354CAMPO89173N'.
       10 PIC X(15) VALUE 'W516CAMPO92815N'.
    05 REDEFINES WT-TBL-LISTA.
       10 WT-TBL-ELEMENTO OCCURS 15
                           INDEXED BY WI-ELEM.
          15 WT-TBL-CLAVE1 PIC X.
          15 WT-TBL-CLAVE2 PIC X(3).
          15 WT-TBL-CAMPO PIC X(10).
          15 WT-TBL-INDICADOR PIC X.
* Formato del registro a ordenar
 01 WR-ELEMENTO-SORT.
   05 WR-SORT-CLAVE1 PIC X.
   05 WR-SORT-CLAVE2 PIC X(3).
   05 WR-SORT-CAMPO PIC X(10).
   05 WR-SORT-INDICADOR PIC X.
*
 PROCEDURE DIVISION.
*
    PERFORM 1000-INICIO
    PERFORM 2000-PROCESO
    PERFORM 9000-FINAL
    .
*
 1000-INICIO.
*
    INITIALIZE VARIABLES
    .
*
 2000-PROCESO.
* Proceso de SORT
* Indicamos las claves por las que ordenaremos ON ASCENDING ó 

* ON DESCENDING

    SORT TABLA-SORT
     ON ASCENDING KEY SORT-CLAVE1
     ON DESCENDING KEY SORT-CLAVE2
     INPUT PROCEDURE 2100-PROCESO-ENTRADA
     OUTPUT PROCEDURE 2200-PROCESO-SALIDA
* En la INPUT PROCEDURE cargamos los datos a ordenar
* En la OUTPUT PROCEDURE guardamos la información ordenada 

* en variables del programa

* Controlamos el retorno del SORT con SORT-RETURN
    IF SORT-RETURN NOT = ZEROS
       DISPLAY 'ERROR EN EL SORT:' SORT-RETURN
    END-IF
    .
*
 2100-PROCESO-ENTRADA.
* Cargamos los datos a ordenar(los de la tabla interna) en el

* fichero temporal donde se realizará la ordenación
* (ELEMENTO-SORT) con la sentencia RELEASE.
    PERFORM VARYING WI-ELEM
       FROM 1 BY 1
      UNTIL WT-TBL-CLAVE1(WI-ELEM) = LOW-VALUES
            OR WI-ELEM = 16
*
        MOVE WT-TBL-CLAVE1(WI-ELEM) TO WR-SORT-CLAVE1
        MOVE WT-TBL-CLAVE2(WI-ELEM) TO WR-SORT-CLAVE2
        MOVE WT-TBL-CAMPO(WI-ELEM) TO WR-SORT-CAMPO
        MOVE WT-TBL-INDICADOR(WI-ELEM) TO WR-SORT-INDICADOR
*
        RELEASE ELEMENTO-SORT FROM WR-ELEMENTO-SORT
        DISPLAY 'ELEMENTO-SORT:'WR-ELEMENTO-SORT
    END-PERFORM
    .
*
 2200-PROCESO-SALIDA.
* Recuperamos los datos ordenados del fichero temporal con la

* sentencia RETURN y los displayamos
    SET NO-FIN-TABLA-SORT TO TRUE
*
    PERFORM UNTIL SI-FIN-TABLA-SORT

     RETURN TABLA-SORT INTO WR-ELEMENTO-SORT
     AT END
            SET SI-FIN-TABLA-SORT TO TRUE
     NOT AT END
            MOVE WR-SORT-CLAVE1 TO WA-SORT-CLAVE1
            MOVE WR-SORT-CLAVE2 TO WA-SORT-CLAVE2
            MOVE WR-SORT-CAMPO TO WA-SORT-CAMPO
            MOVE WR-SORT-INDICADOR TO WA-SORT-INDICADOR
*
            DISPLAY 'REGISTRO->' WA-REGISTRO
*
     END-RETURN
    END-PERFORM
    .
*
 9000-FINAL.
*
    STOP RUN
    .
*


RESULTADO:

REGISTRO->F457CAMPO61224N
REGISTRO->F217CAMPO03312N
REGISTRO->F216CAMPO02802S
REGISTRO->L547CAMPO73985N
REGISTRO->L354CAMPO89173N
REGISTRO->M144CAMPO17114N
REGISTRO->O372CAMPO52113N
REGISTRO->Q651CAMPO24536S
REGISTRO->T487CAMPO44914S
REGISTRO->W516CAMPO92815N

lunes, 7 de febrero de 2011

Sentencia MERGE en un programa COBOL

Hemos visto en otros artículos como hacer un MERGE en un JCL. En este artículo veremos como hacerlo en un programa cobol.
¿Utilidad? Depende. Lo cierto es que pudiendo hacerlo por JCL, no veo la razón de hacerlo en un programa. Pero quién sabe! Tal vez alguno de los lectores pueda darnos una idea de su uso práctico : D

MERGE:
La sentencia MERGE en cobol sirve para unir dos ficheros teniendo en cuenta la clave por la que están ordenados. Es decir, no podemos hacer un MERGE de ficheros desordenados.
Lo que hará será "colocar" las claves que coincidan, juntas en el fichero de salida.
Para el ejemplo, utilizaremos:
2 ficheros de entrada con los datos a unir.
1 fichero temporal donde se realizará el MERGE.

Los datos de los ficheros de entrada para nuestro ejemplo serán:

FICHERO1:
A
B
C


FICHERO2:
B
C
E


Programa:
 IDENTIFICATION DIVISION.
 PROGRAM-ID.PRGMERGE.
*
 ENVIRONMENT DIVISION.
 CONFIGURATION SECTION.
 SPECIAL-NAMES.
 DECIMAL-POINT IS COMMA.
*
 INPUT-OUTPUT SECTION. 

 FILE-CONTROL.
*Definición de ficheros
 SELECT TABLA-MERGE ASSIGN TO DISK 'SORTWORK'.
 SELECT TABLA-FICH1 ASSIGN TO FICHERO1.
 SELECT TABLA-FICH2 ASSIGN TO FICHERO2.
*
 DATA DIVISION.
 FILE SECTION.
*Ficheros físicos
 FD TABLA-FICH1
    DATA RECORD IS FICHERO1.
 01 FICHERO1.
    05 FILLER PICTURE X.
 FD TABLA-FICH2
    DATA RECORD IS FICHERO2.
 01 FICHERO2.
    05 FILLER PICTURE X.
* Ficheros temporales
 SD TABLA-MERGE
    DATA RECORD IS ELEMENTO-MERGE.
 01 ELEMENTO-MERGE.
    05 MERGE-CLAVE1 PIC X.
*
 WORKING-STORAGE SECTION.
* Variable donde guardaremos el resultado del MERGE
 01 VARIABLES.
    05 WA-REGISTRO.
       10 WA-AUX-CLAVE1 PIC X.
* Switches para el bucle
 01 SWITCHES.
    05 SW-FIN-TABLA-MERGE PIC X(1).
       88 SI-FIN-TABLA-MERGE VALUE 'S'.
       88 NO-FIN-TABLA-MERGE VALUE 'N'.
* Registro para los datos después del MERGE
 01 WR-ELEMENTO-MERGE.
    05 WR-MERGE-CLAVE1 PIC X.
*
 PROCEDURE DIVISION.
*
     PERFORM 1000-INICIO
     PERFORM 2000-PROCESO
     PERFORM 9000-FINAL
     .
*
 1000-INICIO.
*
     INITIALIZE VARIABLES
     .
*
 2000-PROCESO.
* Sentencia MERGE
* Juntamos FICHERO1 y FICHERO2 en TABLA-MERGE por clave 

* MERGE-CLAVE1

     MERGE TABLA-MERGE ASCENDING KEY MERGE-CLAVE1
     USING FICHERO1 FICHERO2
     OUTPUT PROCEDURE 2100-PROCESO-SALIDA
* En la OUTPUT PROCEDURE usaremos la información ya unida
     IF SORT-RETURN NOT = ZEROS
        DISPLAY 'ERROR EN EL MERGE:' SORT-RETURN
     END-IF
     .
*
 2100-PROCESO-SALIDA.
* Movemos del fichero temporal al registro de salida con la

* sentencia RETURN
* Displayamos la información desde una variable definida 
* en el programa
     SET NO-FIN-TABLA-UNION TO TRUE
*
     PERFORM UNTIL SI-FIN-TABLA-UNION
      RETURN TABLA-MERGE INTO WR-ELEMENTO-MERGE
          AT END
             SET SI-FIN-TABLA-MERGE TO TRUE
         NOT AT END
             MOVE WR-MERGE-CLAVE1 TO WA-AUX-CLAVE1

             DISPLAY 'REGISTRO->' WA-REGISTRO

      END-RETURN
     END-PERFORM
     .
*
 9000-FINAL.
*
     STOP RUN
     .
*


RESULTADO:

REGISTRO->A
REGISTRO->B
REGISTRO->B
REGISTRO->C
REGISTRO->C
REGISTRO->E


Nota: aunque no veais nigún párrafo de "abrir fichero", "leer fichero", etc., no significa que se nos haya ido la olla, es que no es necesario!