Desarrollando en Cobol y Natural sobre Plataforma Mainframe

jueves, 30 de abril de 2015

CA Database Management Solutions for DB2 for ZOS (1)

La herramienta “CA Database Management Solutions for DB2 for ZOS” es una aplicación que nos permite, de una forma cómoda y sencilla, acceder a la estructura de campos de una tabla DB2. Aunque existen formas alternativas de realizar estas consultas, disponer de una herramienta de este tipo se acaba traduciendo en un ahorro considerable de tiempo.

Herramienta CA Database Management Solutions for DB2 for ZOS

En general, cada instalación suele tener su propia aplicación de gestión de Bases de Datos. Por tanto, cuando empecemos a trabajar con un nuevo cliente, lo más probable es que tengamos que aprender a operar con una solución diferente. De entre todos los que nos podemos encontrar, la herramienta “CA Database Management Solutions for DB2 for ZOS” (CA DMS) forma parte del grupo de gestores de Bases de Datos DB2 más conocidos.

Obviamente, cada empresa decidirá colocar el CA DMS en un menú diferente. Por tanto, no tendría mucho sentido tratar de indicaros desde qué punto del ISPF se puede acceder a ella. En su defecto, a continuación os mostraremos directamente el menú principal que se visualiza nada más arrancar la herramienta.



Tal y como se puede observar, en la parte superior de la ventana hay que provisionar una serie de campos obligatorios. Por un lado, en “DB2 SSID” tendremos que indicar el esquema de la Base de Datos a la que queremos acceder: en nuestro caso, se trata de JJ2D. Por otro lado, en los campos ACMID y SQLID tendremos que especificar el código de nuestro usuario: en este caso, se trata del JJ00917.

DB2 SSID ==> JJ2D LOCATION ==> LOCAL                   
ACM      ==> OFF ACMID    ==> JJ00917  SQLID ==> JJ00917

A continuación, para acceder a la Base de Datos (BBDD) requerida, tendremos que seleccionar la opción “1 – RC/Query” del bloque “Database Administration”. Para ello, bastará con insertar un 1 en la línea de comandos (OPTION ===>).

<-> Database Administration
    _ 1  RC/Query          
    _ 2  RC/Migrator       
    _ 4  RC/Secure         

Tras pulsar INTRO accederemos a una nueva ventana, denominada Menú principal de RC/Query, donde se desplegarán una nueva serie de opciones.



Si queremos acceder a la configuración de una determinada Tabla, entonces tendremos que introducir una T en el campo DB2 Object.

DB2 Object ===> T

A continuación, en la parte inferior de la pantalla se nos mostrarán las opciones que tenemos disponibles para los objetos T (Tablas). La información que tendremos que provisionar es la siguiente.

-    Option: Aquí especificaremos qué es lo que queremos hacer con la tabla seleccionada. Por ejemplo, una ‘I’ nos servirá para ver cuáles son los índices que la componen.

-    Table Name: Hay que indicar el nombre del fichero DB2. Por ejemplo, podemos insertar el nombre JJPEDITD (Tabla de Pedidos).

-    Creator: Tendremos que introducir el nombre del creador de la tabla DB2 seleccionada anteriormente.

DB2 Object ===> T                      
Option  ===> I   
Table Name ===> JJPEDITD            
Creator ===> JJDB22

Consulta de Columnas de un Indice

Si pulsamos INTRO, a continuación nos trasladaremos a la pantalla de “RC/Query Table Index Inquiry” y se nos mostrarán todos los índices que actualmente están asociados la tabla DB2 especificada.



Como podemos ver, en la imagen se indica que, para la tabla JJPEDITD, actualmente hay definidos tres índices: JJPEDII0, JJPEDII1 y JJPEDII2. Si nos posicionamos en la columna CMD y seleccionamos uno de ellos (por ejemplo, el primero) con el comando ‘C’ (Column), la herramienta nos desglosará todos los campos que forman parte de dicho índice.



En la pantalla anterior, por ejemplo, podemos comprobar que el índice seleccionado está compuesto por nueve campos, ordenados de la forma mostrada.

El próximo día continuaremos examinando funcionalidad adicional. Tal y como veremos, la herramienta también nos permite consultar la query con la que se generó un índice en particular, opción que puede ser muy útil a la hora de detectar algún tipo de error en la construcción de la tabla DB2.

Y eso es todo por hoy. Os citamos para la segunda parte del post, donde esperamos completar el repaso de las funciones esenciales de esta aplicación.

Saludos.

jueves, 23 de abril de 2015

Como crear una Subquery DB2 con SQL

A la hora de acceder a DB2, cuando precisamos realizar algún filtrado relativamente complejo, lo más probable es que no tengamos suficiente con la implementación de una query simple. En estos casos, no nos va a quedar más remedio que recurrir al empleo de subqueries.

¿Qué es una subquery de SQL? Básicamente, una subquery no es más que una query anidada dentro de otra Query de orden superior. Normalmente, las subqueries se incorporan en la cláusula WHERE y se utilizan para extraer una serie de valores que luego vamos a utilizar para la ejecución de la Query superior.



Esto se ve más claramente con un ejemplo, así que a continuación incorporamos un fragmento de código que incorpora una subquery.

SELECT * 
FROM JJCABLT0 A, JJPEDIT0 B       
WHERE A.SITUACION IN                             
               (SELECT DISTINCT SITUACION

                FROM JJPEDIT0         
                WHERE CENTRO = 'ZARAGOZA')
      AND B.CENTRO = 'ZARAGOZA'                          
      AND A.SITUACION = B.SITUACION                
      AND A.PEDIDO = B.PEDIDO               


En la query anterior estamos buscando registros en el fichero CABL que cumplan con unos determinados criterios en los campos SITUACION, CENTRO y PEDIDO. Las cláusulas de CENTRO y PEDIDO no merecen mayor comentario, pues hacen referencia a declaraciones SQL simples que ya hemos visto en muchas otras ocasiones.

En cuanto al campo SITUACION, vemos que está incluido en una sentencia definida como A.SITUACION IN (...). Lo curioso es que el contenido de la cláusula IN (...) no se corresponde con códigos númericos o alfanuméricos, sino que en su interior aparece definida (anidada) otra query completa. Esta query interna es lo que se conoce como subquery.

SELECT DISTINCT SITUACION
FROM JJPEDIT0         
WHERE CENTRO = 'ZARAGOZA'


La ejecución de la subquery devolverá una serie de valores del campo SITUACION, los cuales serán utilizados para realizar el filtrado de la cláusula WHERE de la Query superior. Dicho de otro modo, la query principal buscará todos aquellos registros cuyo campo SITUACION coincida con alguno de los códigos devueltos por la query subordinada.

Si ejecutásemos la subquery por separado, el resultado que nos retornaría sería el siguiente (en este caso se trata de un registro único, pero podrían haber sido varios).

 SITUACION
 ---------
    500001


Por tanto, la ejecución de la Query principal buscará todos los registros de la tabla CABL cuyo campo SITUACION sea igual al valor 500001. A continuación, mostramos las líneas que se obtienen si lanzamos la query completa.



Como vemos, todos los registros mostrados aparecen con SITUACION igual a 500001, cosa que coincide con el resultado esperado. El resto de páginas del listado, pese a que no se ve en la imagen, también cumple con esa condición.

Aunque en el post hemos puesto como ejemplo un caso muy sencillo, esta es la estructura básica con la que deben implementarse una Query y sus subqueries anidadas. Obviamente, en las aplicaciones de nuestros clientes nos iremos encontrando con codificaciones más complejas pero, en realidad, estarán basadas en esta misma configuración. Otro día, en un nuevo post, os mostraremos un caso en el que se ilustre la dificultad que puede llegar a alcanzar una query de este tipo.

Eso es todo. Esperamos que lo comentado valga para despertar el interés por el mundo de las subqueries DB2. Al menos, os puede servir como punto de partida para ir ampliando vuestro conocimiento posteriormente.

Saludos.

jueves, 16 de abril de 2015

Como obtener la fecha del sistema en Cobol

Una rutina típica que suele tener todo programa Cobol es la de recuperación de la fecha y la hora actual del sistema. Se trata de una información muy útil generalmente precisada para su inclusión en los informes solicitados por el usuario o para indicar la fecha de actualización de los datos en las tablas DB2 (TIMESTAMP).

Aunque hay varias alternativas, una forma estándar de obtener la fecha en Cobol es mediante la utilización de los comandos CICS ASKTIME y FORMATTIME. Con unas pocas operaciones, estas instrucciones nos permiten disponer de una variable fecha (y hora) formateada según las necesidades de nuestra aplicación.



A continuación, vamos a ver un ejemplo en Cobol de cómo se podría recuperar la fecha y dejarla cargada en una variable de trabajo. Supongamos, en primer lugar, que tenemos definidas las siguientes variables en nuestra WORKING-STORAGE SECTION.

05  W-FECHA-HORA-SISTEMA  PIC   S9(15) COMP-3 VALUE   0.
05  W-ANNO-SISTEMA        PIC   S9(8)  COMP   VALUE   0.
05  W-MES-SISTEMA         PIC   S9(8)  COMP   VALUE   0.
05  W-DIA-SISTEMA         PIC   S9(8)  COMP   VALUE   0.


05  W-HORA-SISTEMA.                                  
    10  W-HH-SISTEMA      PIC    9(2)      VALUE    0.
    10  FILLER            PIC    X         VALUE  '.'.
    10  W-MM-SISTEMA      PIC    9(2)      VALUE    0.
    10  FILLER            PIC    X         VALUE  '.'.
    10  W-SS-SISTEMA      PIC    9(2)      VALUE    0.


05  W-FECHA-SISTEMA.                                 
    10  W-ANNO            PIC    9(4)      VALUE    0.
    10  FILLER            PIC    X         VALUE  '-'.
    10  W-MES             PIC    9(2)      VALUE    0.
    10  FILLER            PIC    X         VALUE  '-'.
    10  W-DIA             PIC    9(2)      VALUE    0.

05  W-TIMESTAMP.                                       
    10  W-FECHA-SYS       PIC    X(10)     VALUE SPACES.
    10  FILLER            PIC    X(1)      VALUE '-'.  
    10  W-HORA-SYS        PIC    X(8)      VALUE SPACES.
    10  FILLER            PIC    X(1)      VALUE '-'.  
    10  W-MILISEG-SYS     PIC    9(6)      VALUE 0.    


La idea es recuperar la fecha del CICS en el parámetro W-FECHA-HORA-SISTEMA y, tras las operaciones pertinentes, dejar preparada la fecha/hora del sistema en W-TIMESTAMP. Esta última variable podrá ser utilizada para provisionar los campos TIMESTAMP de los registros de las tablas DB2 que sean modificados por nuestro programa.

A continuación, mostramos un código que podría ser incluido en una rutina Cobol para realizar la transformación comentada. Obviamente, no es la única solución que podría ser implementada.

EXEC CICS                        
     ASKTIME                     
     ABSTIME(W-FECHA-HORA-SISTEMA)
END-EXEC                         
                                 
EXEC CICS                        
     FORMATTIME                  
     ABSTIME(W-FECHA-HORA-SISTEMA)
     YEAR(W-ANNO-SISTEMA)        
     MONTHOFYEAR(W-MES-SISTEMA)  
     DAYOFMONTH (W-DIA-SISTEMA)  
     TIME(W-HORA-SISTEMA)        
     TIMESEP('.')                
END-EXEC.                        

MOVE W-DIA-SISTEMA  TO  W-DIA              
MOVE W-MES-SISTEMA  TO  W-MES              
MOVE W-ANNO-SISTEMA TO  W-ANNO.            
                                           
MOVE W-FECHA-SISTEMA      TO W-FECHA-SYS   
MOVE W-HORA-SISTEMA       TO W-HORA-SYS    
MOVE W-FECHA-HORA-SISTEMA TO W-MILISEG-SYS.

Una vez visto el código completo, pasamos a detallar un poco cuál sería el objetivo de cada uno de los bloques desarrollados.

1º) En primer lugar, se emplea el comando CICS ASKTIME para recuperar la fecha y hora actual del sistema.

2º) La cláusula ABSTIME permite almacenar (en una variable) la fecha del sistema con un formato conocido como tiempo absoluto. Dicho formato especifica la cantidad de milisegundos transcurridos desde el inicio del año 1900 hasta el momento actual.

3º) El comando FORMATTIME transforma una fecha de tiempo absoluto en una fecha y hora con el formato que deseemos.



4º) En el ejemplo anterior se obtienen varios elementos de la variable de tiempo absoluto. Con YEAR, MONTHOFYEAR y DAYOFMONTH se extraen de ella el año, el mes y el día, respectivamente. Finalmente, con TIME extraemos la hora del reloj y con TIMESEP especificamos el separador deseado entre horas, minutos y segundos.

5º) A continuación, transferimos los datos extraídos de día, mes y año a una variable W-FECHA-SISTEMA donde guardaremos la información con el formato que deseemos.

6º) Finalmente, movemos la fecha del sistema, la hora del sistema y el tiempo absoluto en milisegundos a una variable W-TIMESTAMP. Esta variable será la que se use para almacenar el TIMESTAMP en los registros que nuestro programa Cobol precise modificar en las tablas DB2.

Aunque aparentemente parecen demasiados pasos para la elaboración de una rutina tan básica, la realidad es que, una vez codificada, podremos ir copiándola sin más de un programa a otro. Se trata de un párrafo que no necesitará modificaciones adicionales, salvo que queramos cambiar el formato de la fecha obtenida.



Como vemos, aquí tenemos un método estándar para crear una fecha/hora de sistema y para disponer de un TIMESTAMP en los programas Cobol. La primera funcionalidad es muy útil para mostrar información a los usuarios y la segunda resulta imprescindible si queremos mantener una trazabilidad de las modificaciones que se vayan realizando en las tablas DB2. Si todavía no estáis de acuerdo con ello, os aseguramos que tarde o temprano lo estaréis.

Pues nada, eso es todo lo que queríamos comentaros por hoy. Esperamos que esta rutina de captura de fecha de sistema os sea útil y que podáis emplearla frecuentemente en vuestros propios objetos.

Saludos.

martes, 14 de abril de 2015

Universo Cobol alcanza las 20.000 visitas

Todavía tenemos bastante presente el momento en el que alcanzamos las 10.000 visitas en el blog y, a pesar de que no ha transcurrido demasiado tiempo desde entonces, esta semana ya podemos celebrar la llegada al escalón de las 20.000 visitas. No está nada mal para un sitio de esta temática con tan sólo un año y medio de vida.



Por supuesto, todo esto no habría sido posible sin vuestra participación así que, desde aquí, lo único que queremos hacer es agradeceros la acogida inicial que está teniendo nuestro Universo Cobol. Esperamos seguir cumpliendo con vuestras expectativas y poder alcanzar, dentro de poco, las 30.000 visitas.

¡Gracias por vuestro seguimiento!

Saludos.

jueves, 2 de abril de 2015

Sentencia SQL SELECT WHERE 0=1 para DB2

Aunque en un primer momento pueda resultar extraño, no son pocas las veces en las que nos vamos a encontrar, en el acceso a DB2, con una sentencia SELECT en cuya cláusula WHERE aparece la estructura 0 = 1. Esto se suele utilizar cuando necesitamos producir una condición de resultado FALSE.

En general, la cláusula WHERE 0 = 1 es empleada por los programadores Cobol DB2 cuando se precisa generar un resultado FALSE en alguna sentencia SQL. Al realizar el mantenimiento del programa nos resultará extraño encontrarnos con ella, pero en realidad dicha configuración no tiene mayor relevancia.



Por ejemplo, aquí en Universocobol.com os mostramos, simplificada, la sentencia SELECT que nos encontramos hace algunas semanas en uno de nuestros clientes.

SELECT SITUACION, PEDIDO, MATERIAL,
       COORDX, COORDY, TIPO_MATERIAL
FROM JJPEDIT0
WHERE (SITUACION = :W-SITUACION OR 0 = 1)


El SELECT anterior mostrará todos los registros con SITUACION igual al contenido de la variable W-SITUACION. En realidad, la segunda parte del WHERE no tendría ninguna influencia en el resultado final: la condición 0 = 1 siempre equivaldrá a FALSE y, por tanto, la SELECT sólo mostrará los registros que cumplan la primera parte de la sentencia OR (es decir, aquellos con SITUACION = :W-SITUACION).

¿Y qué ocurriría si, en vez del operador OR, empleásemos el operador AND en la cláusula WHERE? Recordemos que 0 = 1 equivale a FALSE y, por tanto, en esta ocasión el impacto sobre el resultado de la sentencia SELECT sería mucho más importante que en el primer caso.

El ejemplo anterior quedaría de la siguiente forma. Ahora el SELECT estaría acompañado por un WHERE que contiene el operador AND.

SELECT SITUACION, PEDIDO, MATERIAL,
       COORDX, COORDY, TIPO_MATERIAL
FROM JJPEDIT0
WHERE (SITUACION = :W-SITUACION AND 0 = 1)


¿Cuál sería el resultado aquí? Pues con esta instrucción el SELECT no podrá devolver ningún registro. La segunda parte del AND siempre será FALSE y, por tanto, nunca se podrá cumplir la condición completa del WHERE, independientemente del resultado de la primera parte (SITUACION = :W-SITUACION).

El uso de esta segunda estrategia (AND en vez de OR) es mucho más restringido, ya que dicha configuración sólo tendrá utilidad cuando queramos que el resultado de una SELECT sea nulo. Se puede emplear para generar algún tipo de prueba específico, pero en pocas situaciones más.



Como vemos, en las sentencias SQL de un programa Cobol nos podemos encontrar con estructuras bastante extrañas en un SELECT. Cuando esto nos ocurra, lo importante es pensar con lógica y determinar qué es lo que se obtendría con la ejecución de la instrucción. Una vez que tengamos claro ese punto, el problema se solucionará tarde o temprano.

Esperamos que lo comentado en el post os sirva, al menos, para disminuir la sorpresa si, en alguna ocasión, os encontráis con un WHERE 0 = 1 en algún programa.

Saludos.

Related Posts Plugin for WordPress, Blogger...