FETCH FIRST n ROWS ONLY y OPTIMIZE FOR n ROWS

Las cláusulas OPTIMIZE FOR n ROWS y FETCH FIRST n ROWS ONLY permiten optimizar una SELECT indicándole al DB2 el número de filas (n) que estimamos se van a obtener. La diferencia entre ambas es que con OPTIMIZE FOR se recuperan todas las filas que cumplan la condición de la SELECT y con FETCH FIRST sólo las n primeras.

IBM recomienda utilizar OPTIMIZE for 1 ROW siempre que se vaya recuperar un número reducido de filas (aunque sea superior a uno). De esta forma le indicamos a DB2 que intente evitar caminos de acceso que impliquen ordenaciones.

Ejemplo:

SELECT CAMPO1,
       CAMPO2,
       CAMPO3
FROM TABLA
WHERE CAMPO4  = :VARIABLE
FETCH FIRST 10 ROWS ONLY

Esta SELECT recuperará un máximo de 10 filas. Un intento de recuperar la número 11 devolverá un SQLCODE 100.

El FETCH FIRST implica también optimizar el camino de acceso para el mismo número de filas. Es decir, en el ejemplo anterior se asume por omisión un OPTIMIZE FOR 10 ROWS.

Cuando queramos hacer una optimización para un número inferior de filas, debemos indicarlo explícitamente. Por ejemplo, para optimizar la SELECT anterior siguiendo la recomendación de IBM:

SELECT CAMPO1,
       CAMPO2,
       CAMPO3
FROM TABLA
WHERE CAMPO4  = :VARIABLE
FETCH FIRST 10 ROWS ONLY
OPTIMIZE FOR 1 ROW

Cuando solo queramos optimizar la SELECT sin limitar el número de filas, basta con incluir el OPTIMIZE:

SELECT CAMPO1,
FROM TABLA
WHERE CAMPO2  = :VARIABLE
OPTIMIZE FOR 1 ROW
Publicado en DB2

Crear informes con un Sort

En este artículo vamos a introducir las sentencias de control del parámetro OUTFIL del Sort que permiten generar un fichero de salida con formato de informe. Las principales son:

HEADER1
Cabecera general del informe.

HEADER2
Cabecera de página.

HEADER3
Cabecera de sección.

TRAILER1
Pie final del informe

TRAILER2
Pie de página.

TRAILER3
Pie de sección.

SECTIONS
Campo/s de ruptura que marcan el final de una sección y el inicio de la siguiente.

Su sintaxis completa se puede encontrar en el manual de IBM. Aquí vamos a aprender a utilizarlas mediante un sencillo ejemplo.

Partimos de un fichero con las ventas que han realizado los comerciales de una empresa. Tiene la siguiente estructura:

Campo Formato COBOL
COMERCIAL PIC X(25)
SUCURSAL PIC X(10)
TOTAL VENDIDO PIC 9(9)V9(2) COMP-3
COMISIONES PIC 9(9)V9(2) COMP-3

Y queremos obtener un informe que muestre las ventas y comisiones de cada vendedor, así como los totales de cada sucursal. Tendrá cabeceras de página y de sección (sucursal) y pies de sección.


                                              23/04/2014  PAG.     1
         INFORME DE VENTAS Y COMISIONES POR SUCURSAL
         -------------------------------------------
SUCURSAL: BARCELONA

NOMBRE                     SUCURSAL    VENTA         COMISION
------------------------   ----------  ------------  ------------
NOMBRE APELL1 APELL2       BARCELONA        4108,92        350,65
NOMBRE APELL1 APELL2       BARCELONA       12789,23       1190,67
NOMBRE APELL1 APELL2       BARCELONA        2109,34        208,65
-----------------------------------------------------------------------
TOTAL BARCELONA                            19007,49       1749,97

SUCURSAL: LEVANTE

NOMBRE                     SUCURSAL    VENTA         COMISION
------------------------   ----------  ------------  ------------
NOMBRE APELL1 APELL2      LEVANTE          8177,01         711,23
…                        …                  …            …
 
TOTAL LEVANTE                               39022,42       3749,92
…
 
                                                 23/04/2014  PAG.     2
            INFORME DE VENTAS Y COMISIONES POR SUCURSAL
            -------------------------------------------
…

Para generarlo utilizaríamos el siguiente paso de Sort:

//PA0010   EXEC PGM=SORT
//SYSOUT   DD SYSOUT=*
//SORTIN   DD DSN=FICHERO.ENTRADA,
//         DISP=SHR
//SALIDA   DD DSN=FICHERO.SALIDA,
//         DISP=(NEW,CATLG,DELETE),RECFM=FB,DSORG=PS,LRECL=72
//SYSIN    DD *
SORT FIELDS=(26,10,CH,A)
OUTFIL FNAMES=SALIDA,
BUILD=(1X,1,25,2X,26,10,2X,36,6,PD,EDIT=(IIIIIIITT,TT),
2X,42,6,PD,EDIT=(IIIIIIITT,TT),6X),
REMOVECC,
HEADER2=(50X,DATE=(DM4/),2X,'PAG.',PAGE,/,
10X,'INFORME DE VENTAS Y COMISIONES POR SUCURSAL',/,
10X,'-------------------------------------------'),
SECTIONS=(26,10,
HEADER3=(/,2:'SUCURSAL: ',26,10,/,
/,2:'NOMBRE',29:'SUCURSAL',41:'VENTA',
55:'COMISION',/,
2:'------------------------',
29:'----------',
41:'------------',
55:'------------'),
TRAILER3=(2:71'-',/,1X,'TOTAL ',26,10,23X,
TOTAL=(36,6,PD,EDIT=(IIIIIIITT,TT)),
2X,
TOTAL=(42,6,PD,EDIT=(IIIIIIITT,TT))))

Vemos que se ordena por el campo de ruptura (sucursal, posición 26). A continuación se incluye la ficha OUTFIL correspondiente al fichero de salida. Dentro de ella incluimos el parámetro BUILD para generar los registros de detalle y a continuación, los parámetros que definen el informe. Algunos de ellos ya los hemos descrito en la introducción; los nuevos que aparecen en el ejemplo son:

REMOVECC
Elimina los caracteres de control que se incluyen por defecto

TOTAL
Parámetro que se puede incluir dentro de cualquiera de los pies del informe (TRAILER1, TRAILER2 y TRAILER3). Muestra la suma de todos los valores del campo indicado. En la suma se incluirán todos los registros del informe, pagina o sección, dependiendo del pie en el que se sitúe.

Publicado en JCL, SORT

Comprobar si un fichero está vacío con un Sort

En un Sort es posible forzar un código de retorno determinado (0, 4 u 8) si el SORTOUT (fichero de salida) no tiene registros. Se hace mediante la opción NULLOUT=RCn, donde n puede tomar el valor 0, 4 u 8. 0. El valor por defecto es 0.
Esta facilidad nos permite construir un paso de JCL que devuelva retorno 4 si el fichero de entrada está vacío y 0 en caso contrario.

Ejemplo:


//PA0010 EXEC PGM=SORT
//SYSOUT DD SYSOUT=*
//SORTIN DD DSN=FICHERO.PRUEBA.NULLOUT,DISP=SHR
//SORTOUT DD DSN=&&TEMP,DISP=(,PASS)
//SYSIN DD *
OPTION COPY,NULLOUT=RC4,STOPAFT=1
Publicado en JCL, SORT

Controlar la existencia de un fichero en un JCL

La existencia de un fichero se puede controlar mediante el comando LISTCAT de la utilidad IDCAMS. Esta devolverá código de retorno 4 si el fichero que se le pasa como parámetro no existe en el catálogo.

Ejemplo:

Tenemos un JOB que recibe un fichero de entrada. En caso de que dicho fichero no exista, queremos que el JOB no haga nada pero termine sin JCL Error.

//PA0010   EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=*
//SYSIN    DD *
  LISTCAT ENTRIES (FIC.ENTRAD.EJEMPLO) ALL   
/*
//IFPAS1  IF  PA0010.RC EQ 0 THEN
//PA0020   …
//PA0030   …
//…
//ENPAS1 ENDIF

Recordemos que IDCAMS nos permite cambiar el código de retorno de la ejecución. Por ejemplo, si nos interesa que el job termine con retorno 1 en vez de 4, podemos forzarlo así:

LISTCAT ENTRIES (FIC.ENTRAD.EJEMPLO) ALL
  IF LASTCC NE 0 THEN DO
    SET MAXCC=1
  END
Publicado en IDCAMS, JCL

Cálculos con fechas en COBOL

El COBOL de IBM para z/OS incluye dos funciones que simplifican mucho los cálculos con fechas, gracias a que convierten una fecha en formato AAAAMMDD en su equivalente numérico y a la inversa. Ello nos permite hacer sumas o restas con fechas fácilmente, sin necesidad de tener en cuenta el número de días de cada mes o si alguno de los años que intervienen en el cálculo es bisiesto.

Las funciones son:

INTEGER-OF-DATE (argumento)

El argumento debe ser un número entero que represente una fecha en formato AAAAMMDD comprendida entre 16010101 y 99991231.

El resultado es un número entero en el rango de 1 a 3.067.671 que representa el número de días transcurridos entre el 31 de diciembre de 1600 y la fecha del argumento.

DATE-OF-INTEGER (argumento)

El argumento debe ser un número entero comprendido entre 1 y 3.067.671 que represente el número de días transcurridos desde el 31 de diciembre de 1600.

El resultado es la fecha en formato AAAAMMDD equivalente al número entero pasado como argumento.

Ejemplos:

Definimos las  variables

01  W-FECHA1            PIC 9(08).
01  W-FECHA2            PIC 9(08).
01  W-DIAS              PIC 9(04).

1)  Calcular el número días de diferencia entre el 5 de marzo de 2012 y el 10 de febrero de 2011.

MOVE  20120305  TO W-FECHA2
MOVE  20110210  TO W-FECHA1

COMPUTE W-DIAS =
FUNCTION INTEGER-OF-DATE(W-FECHA2) -
FUNCTION INTEGER-OF-DATE(W-FECHA1)

2) Restarle diez días al 7 de marzo de 2013.

MOVE 20130307 TO W-FECHA1

COMPUTE W-FECHA2 =
FUNCTION DATE-OF-INTEGER (
FUNCTION INTEGER-OF-DATE(W-FECHA1) - 10 )

Publicado en COBOL

Conversión de fichero de longitud variable a fija y viceversa

La conversión de un fichero de longitud variable a fija y viceversa en un Sort se hace mediante las opciones VTOF y FTOV del parámetro OUTFIL del Sort.

Recordemos que el parámetro OUTFIL permite generar de 1 a n ficheros de salida. Cada uno de estos puede ser construido de forma independiente mediante instrucciones BUILD o OUTREC y condiciones INCLUDE u OMIT.

Conversión de variable a fija

La opción VTOF (o su sinónimo CONVERT) incluida en un grupo OUTFIL indica que los registros de entrada de longitud variable deben ser convertidos a longitud fija.

Se debe incluir también un parámetro BUILD o OUTREC, que generará un registro reformateado de longitud fija sin el descriptor de registro de 4 bytes (los datos comenzarán en la posición 1).

Los campos que se incluyan en el parámetro BUILD o OUTREC deberán estar referidos a los registros de entrada de longitud variable (los datos empiezan en la posición 5).

VTOF utiliza por defecto la opción VLFILL=X’40’ (relleno con espacios) para que sea posible procesar registros de entrada de longitud variable que sean demasiado cortos para contener todos los campos especificados en BUILD o OUTREC.

El formato de registro del fichero de salida será FB por defecto. Y si se especifica, deberá ser FB u otro de los de longitud fija

Si se utiliza VTOF con FTOV, IFTRAIL, IFTHEN, FINDREP u OVERLAY, el Sort terminará con error.

Ejemplo:

//SORT006  EXEC PGM=SORT
//SYSOUT   DD SYSOUT=*
//SORTIN   DD DSN=ENTRADA...,
//         DISP=SHR
//SALIDA1  DD DSN=SALIDA...,
//         DCB=(RECFM=FB,LRECL=300,BLKSIZE=0,DSORG=PS),
//         DISP=(,CATLG,DELETE),
//         SPACE=(TRK,(2,1),RLSE)
//SYSIN    DD *
SORT     FIELDS=COPY
OUTFIL   FNAMES=SALIDA1,VTOF,BUILD=(5,300)
/*

* En vez de VTOF podemos usar su sinónimo CONVERT:

OUTFIL   FNAMES=SALIDA1,CONVERT,BUILD=(5,300)

Conversión de fija a variable

FTOV incluida en un grupo OUTFIL indica que los registros de entrada de longitud fija deben ser convertidos a longitud variable.

Si no se incluye un parametro OUTREC, BUILD, OVERLAY, FINDREP o IFTHEN, los registros de entrada de longitud fija completos se transforma en registros de salida de longitud variable

Si se incluye un parametro OUTREC, BUILD, OVERLAY, FINDREP o IFTHEN, los campos especificados se transforman en registros de salida de longitud variable.

En los dos casos, antes de escribir los registros de salida se les añade un descriptor de registro de 4 bytes (los datos empiezan en la posición 5).

Los campos especificados en OUTREC, BUILD, OVERLAY, FINDREP o IFTHEN deberán estar referidos a los registros de entrada de longitud fija (los datos empiezan en la posición 1).

El formato de registro del fichero de salida del OUTFIL será VB por defecto. Y si se especifica, deberá ser FB o cualquier otro de longitud variable.

Si no se especifica el parámetro LRECL para el fichero de salida, se le dará una longitud que permita contener el registro de mayor longitud.

Ejemplo:

EXEC  PGM=SORT
//SYSOUT   DD SYSOUT=*
//SORTIN   DD DISP=SHR,DSN=ENTRADA…
//SALIDA   DD DSN=SALIDA…
//          DISP=(NEW,CATLG,DELETE),
//          LRECL=448,RECFM=VB,DSORG=PS
//SYSIN    DD *
OPTION COPY
OUTFIL FNAMES=SALIDA,FTOV,OUTREC=(1:1,444)
/*

Fuente: DFSORT Application Programming Guide v1R13

Publicado en JCL, SORT

¿Qué es el BIND?

El BIND es un proceso por el que debe pasar todo programa COBOL que contenga instrucciones SQL.

Como el compilador de z/OS solo es capaz de interpretar sentencias COBOL, el SQL requiere un tratamiento especial que vamos a describir a continuación.

En primer lugar, antes de la compilación, un programa COBOL con SQL pasa por un proceso denominado precompilación, que realiza las siguientes acciones:

  • Una verificación sintáctica sencilla de las sentencias SQL utilizando las DCLGEN incluidas en el programa. Se comprueba que los nombres de campos y tablas de las sentencias coinciden con los de la DCLGEN.
  • Remplaza las sentencias SQL del programa fuente por llamadas al módulo de interfase con DB2,ST que sí pueden ser traducidas por el compilador.
  • Crea el módulo de llamadas a la base de datos (DBRM) en la librería DBRMLIB del sistema.  Este módulo contiene las sentencias SQL extraídas del programa.

Después de la precompilación y la compilación, para poder conectar el programa a DB2 debemos ejecutar el proceso de BIND, que consiste en:

  • Comprobar que nuestro usuario tiene permiso para realizar el bind y las autorizaciones SQL necesarias para ejecutar las sentencias.
  • Una verificación sintáctica más completa de las sentencias SQL en la que se utiliza la información del catálogo de DB2.
  • La generación  del código ejecutable correspondiente al SQL del DBRM. Se analizan todas las sentencias  y para cada una de ellas se elige el camino de acceso menos costoso en términos de uso de CPU y número de operaciones de E/S.

¿Cúal el resultado del BIND?

El resultado final dependerá de la opción que hayamos elegido:  es posible hacer un BIND PLAN o un BIND PACKAGE (PAQUETE).  Plan y paquete son objetos DB2 que contienen código ejecutable y se almacenan en el directorio de DB2.

¿Qué diferencia hay entre un plan y un paquete?

La relación entre paquete y DBRM es siempre uno a uno.  Los paquetes se agrupan en colecciones. Se utilizan en rutinas y módulos que son llamados por programas principales.

La relación entre plan y DBRM puede ser uno a muchos.  Los planes se utilizan en programas principales y pueden contener también referencias a paquetes (cuyo BIND se realiza de forma separada).  De esta forma,  si cambian los accesos DB2 de un módulo  llamado por el programa, sólo habrá que hacer el BIND PACKAGE del modulo;  no será necesario el BIND PLAN del programa principal.

Publicado en DB2

Introducción al parámetro DISP de la DD de los JCL

DISP es un parámetro de la instrucción DD (Data Definition) de los JCL. Sirve para indicarle al sistema el estado actual de un fichero y las acciones a realizar sobre el mismo si el paso termina correctamente y en caso de terminación anormal (abend).  Su formato es:

DISP=(Estado, Acción normal, Acción anormal).

Valores para el subparámetro de estado

NEW

Crear nuevo dataset

OLD

El dataset ya existe y se require uso exclusivo (no compartido) del mismo.

SHR

El dataset ya existe y no se require su uso exclusivo. Otros jobs pueden utilizarlo al mismo tiempo.

MOD:

Si el dataset ya existe, indica que se quieren añadir registros al final del mismo (debe ser secuencial). Si el dataset no existe, indica que debe ser creado.

Valores para el subparámetro de acción normal

DELETE

Indica que el dataset dejará de ser necesario. Dependiendo de la configuración de seguridad y de si está almacenado en cinta o no, puede suponer que se eliminará físicamente o bien que su espacio quedará disponible para otros datasets.

KEEP

Mantener el dataset en el volumen en el que esté almacenado.

PASS

Pasar el dataset para su utilización en un paso posterior del job. Ahorra tiempo con respecto a KEEP, ya que el sistema retiene información sobre la ubicación y el volumen. De todas formas, es preferible evitar su su uso porque puede provocar pérdida de datos o incluso el borrado del fichero si no se codifica correctamente en ciertos casos.

CATLG

Incluir una entrada que apunte al dataset en el catálogo, lo que nos permitirá utilizar el dataset posteriormente.

UNCATLG

Si está activado el sistema de gestión automática de almacenamiento (SMS), que suele ser lo habitual, es ignorado y equivale a KEEP. Indica que se eliminará del catálogo la entrada correspondiente al dataset.

Valores para el subparámetro de acción anormal

DELETE

Liberar el espacio que ocupa el dataset, que quedará disponible para su uso por otros datasets.

KEEP

Mantener el dataset.

CATLG

Incluir una entrada que apunte al dataset en el catálogo, lo que nos permitirá utilizar el dataset posteriormente.

UNCATLG

Si está activado el sistema de gestión automática de almacenamiento (SMS), que suele ser lo habitual, es ignorado y equivale a KEEP. Indica que se eliminará del catálogo la entrada correspondiente al dataset.

Valores por defecto

Si se omite el suparámetro de estado, el valor por defecto es NEW.

Si se omite el subparámetro de ejecución normal, el valor por defecto es DELETE para un dataset nuevo y KEEP para un dataset existente.

Si se omite el suparámetro de ejecución anormal, el valor por defecto es el especificado para la ejecución normal, salvo que se trate de PASS. En ese caso el valor por defecto es DELETE para un dataset nuevo o KEEP para uno existente.

Para indicar la ausencia del primer y el segundo subparámetro se debe incluir la coma de separación.

Ejemplos:

DISP=(,CATLG,DELETE) – Equivale a DISP=(NEW,CATLG,DELETE)

DISP=(,KEEP) – Equivale a (NEW,KEEP,KEEP)

DISP=(OLD,,DELETE) – Equivale a (OLD,KEEP,DELETE)

Si se omite el parámetro DISP por completo, el valor por defecto es  (NEW,DELETE,DELETE) Es decir, un fichero que se crea y se borra en el mismo paso.

Nota:  Utilizamos el término inglés dataset para referirnos a los ficheros que se utilizan en el z/OS. Se trata de conjuntos de datos organizados en forma de registros y bloques que pueden ser almacenados en dispositivos de acceso directo (DASD) o en cinta.

Publicado en JCL

Descarga de una tabla con DSNTIAUL

La utilidad DB2 DSNTIAUL permite realizar una descarga de tabla a fichero fácilmente. Tiene menos restricciones de seguridad que la utilidad UNLOAD, por lo que es ideal para hacer descargas en entornos DB2 de prueba.

En su forma más sencilla se ejecutaría con un JCL como el siguiente:

//DESCARGA EXEC PGM=IKJEFT01,DYNAMNBR=20
//STEPLIB DD DSN=PRE.SDSNLOAD,DISP=SHR
//           DSN=PRE.RUNLIB.LOAD,DISP=SHR
//SYSTSPRT DD SYSOUT=*
//SYSTSIN DD *
DSN SYSTEM(SUB)
RUN PROGRAM(DSNTIAUL) PLAN(DSNTIAUL) -
//SYSPRINT DD SYSOUT=*
//SYSUDUMP DD SYSOUT=*
//SYSREC00 DD DSN=FIC.SYSREC00,DISP=(,CATLG,DELETE)
//SYSPUNCH DD DSN=FIC.SYSPUNCH,DISP=(,CATLG,DELETE)
//SYSIN DD *
CREADOR.TABLA

Observaciones:

  1. En la STEPLIB hemos incluido dos librerías de cargables (sustituir “PRE” por el prefijo que corresponda al subsistema DB2 al que queramos acceder)
    – DB2.SDSNLOAD:  contiene el cargable del procesador de comandos DSN
    – DB2.RUNLIB.LOAD:  contiene el cargable de DSNTIAUL
  2. En la SYSTSIN indicamos el nombre del subsistema DB2 (sustituir “SUB” por el nombre del subsistema),  la utilidad (DSNTIAUL) y el nombre del plan, que no siempre coincide con el de la utilidad. Tendremos que consultar el nombre que tenga  en nuestra instalación.
  3. En la SYSIN se especifica el nombre de la tabla. También es posible incluir un WHERE y un ORDER BY (en realidad es una sentencia SQL sin el “SELECT * FROM”)
  4. Ficheros de salida:
    – SYSREC00: Registros descargados de la tabla
    – SYSPUNCH: Contiene las sentencias de control para hacer una carga de la tabla mediante la utilidad LOAD.

El siguiente ejemplo nos va a servir para introducir nuevas opciones de esta utilidad.

//DESCARGA EXEC PGM=IKJEFT01,DYNAMNBR=20
//STEPLIB DD DSN=PRE.SDSNLOAD,DISP=SHR
//           DSN=PRE.RUNLIB.LOAD,DISP=SHR
//SYSTSPRT DD SYSOUT=*
//SYSTSIN DD *
DSN SYSTEM(SUB)
RUN PROGRAM(DSNTIAUL) PLAN(DSNTIAUL) PARMS('SQL,1000')
//SYSPRINT DD SYSOUT=*
//SYSUDUMP DD SYSOUT=*
//SYSREC00 DD DSN=FIC.SYSREC00,DISP=(,CATLG,DELETE)
//SYSREC01 DD DSN=FIC.SYSREC01,DISP=(,CATLG,DELETE)
//SYSPUNCH DD DSN=FIC.SYSPUNCH,DISP=(,CATLG,DELETE)
//SYSIN DD *
SELECT CREADOR.TABLA1_CAMPO1, CAST(COUNT(*) AS CHAR(10)) FROM CREADOR.TABLA1
GROUP BY CAMPO1;
SELECT * FROM CREADOR.TABLA2 ORDER BY CAMPO1, CAMPO2;

Hemos incluido los parámetros ‘SQL’, que nos permite incluir sentencias SQL completas en la SYSIN; y 1000 para limitar el número de filas de salida a ese valor.

Al haber dos sentencias SQL, se generan dos ficheros de salida (SYSREC00 y SYSREC01). Es posible incluir hasta 99 sentencias SQL separadas por puntos y coma que darían lugar a 99 ficheros de salida.

Publicado en DB2, JCL

SYMNAMES: Nombres simbólicos en un Sort

En este artículo vamos a introducir el uso de nombres simbólicos (SYMNAMES) en un SORT para hacer referencia a campos y constantes. Se trata de una facilidad que hace más legibles  los Sort y facilita su mantenimiento.

Por ejemplo, supongamos que tenemos un fichero con la siguiente estructura:

Campo Pos. Inicio Longitud Formato COBOL
COD_FACTURA 1 8 X(8)
CENTRO 9 4 X(4)
FECHA 13 10 X(10)
COD_CLIENTE 23 8 X(8)
IMP_FACTURA 31 8 9(6)V9(2)

Queremos obtener las facturas del centro ‘2040’ (Madrid),  que se hayan generado durante el periodo de rebajas (del 07/01/2013 al 10/02/2013)  y cuyo importe sea igual o superior a 500.

El SORT  para conseguirlo sería el siguiente:

//SELECCI  EXEC PGM=SORT,COND=(0,NE)
//SYSOUT   DD SYSOUT=*
//SORTIN   DD DSN=A144759.SYMNAMES.SMSSTD,DISP=SHR
//SORTOUT  DD DSN=A144759.SYMNAMES.SMSSTD.SORT,
//         DISP=(NEW,CATLG,DELETE),LRECL=38,RECFM=FB,
//         DSORG=PS
//SYSIN    DD *
SORT FIELDS=COPY
INCLUDE COND=(9,4,CH,EQ,C'2040',AND,
13,10,CH,GE,C'2013-01-07',AND,
13,10,CH,LE,C'2013-02-10',AND,
31,8,ZD,GT,+50000)

Si cambia la estructura del fichero, tendremos que recalcular las posiciones de inicio de los campos afectados y sustituirlas en todas las condiciones en que aparezcan (en este Sort y en cualquier otro que utilice el mismo fichero).

Lo mismo ocurrirá si cambia alguna de las constantes; por ejemplo, si queremos cambiar la fecha de fin del periodo de rebajas, tendremos que sustituirla en todas las condiciones de todos los Sort en que aparezca.

Estos inconvenientes podemos evitarlos mediante el uso de SYMNAMES. Veámoslo mediante una nueva versión del SORT anterior en la que hemos sustituido los nombres de campos y las constantes por nombres simbólicos.

//SELECCI  EXEC PGM=SORT,COND=(0,NE)
//SYSOUT   DD SYSOUT=*
//SORTIN   DD DSN=A144759.SYMNAMES.SMSSTD,DISP=SHR
//SORTOUT  DD DSN=A144759.SYMNAMES.SMSSTD.SORT,
//         DISP=(NEW,CATLG,DELETE),LRECL=38,RECFM=FB,
//         DSORG=PS
//SYMNAMES DD *
*CAMPOS
COD_FACTURA,1,8,CH
CENTRO,*,4,CH
FECHA,*,10,CH
COD_CLIENTE,*,8,CH
IMPORTE,*,8,ZD
*CONSTANTES
MADRID,'2040'
INI_REBA,'2013-01-07'
FIN_REBA,'2013-02-10'
TOPE_FACT,50000
//SYSIN    DD *
SORT FIELDS=COPY
INCLUDE COND=(CENTRO,EQ,MADRID,AND,
FECHA,GE,INI_REBA,AND,
FECHA,LE,FIN_REBA,AND,
IMPORTE,GT,TOPE_FACT)

En este ejemplo hemos incluido los nombres simbólicos directamente en la ficha SYMNAMES para mayor claridad, pero lo lógico es incluirlos en un fichero. De esa forma podremos reutilizarlos en cualquier otro Sort que utilice el fichero referenciado.  Además será posible hacer modificaciones en la estructura del fichero sin tener que cambiar los Sort.

Vemos que es posible incluir la posición de inicio y la longitud del campo, o bien indicar que comienza en la posición siguiente al campo anterior mediante un asterisco.

Publicado en JCL, SORT, SYMNAMES