More Batch Programming

In the previous chapter you were introduced to the basic principles and syntax of Jazz, ending up with a program that produced a report by going through a sequential file.  As it went though this file records from a VSAM file were read by key so that the Region Name, not just the code value, could be included in the report. 

 

In this chapter we extend your Jazz skills by showing you how to write batch programs that will create and update files.  It is probable that you’ll also want to read the next chapter, Jazz Logic, as in this chapter we’ll deal with only very simple situations.  The two chapters would have been combined except that Jazz Logic is equally applicable to batch and online programs, whereas this chapter only applies to batch programming.   Later chapters will show you how to write on line programs, either Classical CICS (3270-style) or Web Services.  This chapter focuses on “What can you do with batch programs?”

 

More Batch Programming. 1

Writing Records. 1

Creating Files and zOS JCL. 5

Writing Data with NO Input 8

More Complex Logic. 9

Direct-Access Updating by Batch Programs. 9

Sequential Updating. 10

Sequential Merge Updating. 10

Writing Records

Suppose that we want to create a copy of some of the records from a file.  Typical scenarios:

·         We want to reformat a file, say from sequential to VSAM or SQL

·         We want to create a small file for development and testing

·         We want to change record formats – omitting some fields, changing the format of others, calculating values etc

 

We can do this with a program that is very similar to the report program that we wrote in the previous chapter, but with a WRITE statement rather than PRINT. For example, here we are creating a simple file from some data that we have created using TSO.  We had created a number of records in a user library: -

 

This data is described by record layout In1A: -

 

and we can print the data with a program like this: -

 

Here is the printout: -

 

 Printed at 17 Oct 2014, 20:21:34                  Report1                  Page   1                                                 

 

 Region District *----Name-----* SalesThisMonth SalesYTD Cardholder DateCommenced                                                   

 

     09       03 CustomerName2**       00329.82 01092.63 N          2012-10-17                                                       

     05       09 CustomerName3         00054.02 02370.49 N          2011-12-22                                                      

     04       07 CustomerName4         00617.74 03218.90 N          2012-09-06                                                      

     08       06 CustomerName5         00226.77 02907.83 N          2012-11-16                                                      

     02       04 CustomerName6         00805.15 05062.13 N          2012-03-15                                                      

     03       08 CustomerName7         00429.18 02063.06 N          2011-10-07                                                      

     09       03 CustomerName8         00787.30 05925.17 N          2013-06-04                                                      

     04       10 CustomerName9         00201.13 00214.91 N          2012-05-13                                                      

     08       07 CustomerName10        00691.30 06125.78 N          2011-08-24                                                      

     09       05 CustomerName11        00931.61 04025.08 N          2012-08-15                                                      

     04       09 CustomerName12        00054.88 03855.79 N          2011-12-20                                                      

     09       06 CustomerName13        00071.01 03731.73 N          2013-05-26                                                      

     05       02 CustomerName14        00172.80 05938.50 N          2012-05-17                                                      

 

(Continued for several pages).

 

To load this into a VB file with the fields converted to more appropriate formats and omitting the unwanted FILLER is easy.  We start by preparing a record definition of the output format that we want: -

 

Now we write a program to convert the record formats from In1a to In1.  Logic is basically a PROCESS loop containing the statements: -

    In1.* = IN1a.*;

    WRITE in1;

so here is our complete program: -

PROGRAM PRDta1;

COPY in1a;

COPY In1;

PROCESS in1A;

    PRINT (IN1A.*); 

    In1.* = IN1a.*;

    #207 I Name, SalesThisMonth, SalesYTD, DateCommenced included in generic assignment

    WRITE in1;

    #378 W Batch WRITE used - you may need to edit the JCL

END PROCESS in1A;

 

Well, almost!.  Message #297 tells us that most, but not all, of the fields that we want have been assigned by the generic assignment. However we haven’t assigned the fields Region and District, because in one record they are within a group but in the other they are not.  If we ran the program now we’d find that all the values of Region and District in the output file were initialised to zero, not set to the values from the input records.  We therefore add two more assignment statement, and our program is complete: -

Creating Files and zOS JCL

Previously we’ve written programs that PRINT data from an existing file.  When we’d finished programming we just clicked [Process] and Jazz did it all:  created the COBOL, and then created JCL and submitted a job to zOS to be compiled and run.  Provided that our definitions contain a valid DSNAME option Jazz will generate JCL to access the file and we don’t need to think about this.

 

However for the run step Jazz will assume that the file already exists.  Message #378 warns us that this might not be true, or that the JCL may be incorrect for an output file: -

            Sequential files must have correct DISP and SPACE parameters

            VSAM files must have been created by IDCAMS, and be empty.  Records must be created in sequence.

Instead of simply clicking [Process] to have Jazz create COBOL and JCL and submit a job to compile and run the program, we should interrupt this process.  Right-click [Process] and then click [JCL].  Jazz will check the program, generate COBOL, and then generate JCL.  However the processing stops there and the button [Review JCL] appears.

 

 

Click this and you’ll see the JCL that will be submitted. 

//IBMUSER6 JOB  ,CLASS=A,MSGCLASS=H,NOTIFY=&SYSUID,COND=(8,LT)

//*** COMPILE BATCH PROGRAM OR SUBPROGRAM

//  SET MEMBER=CRDTA1

//  SET SOURCE=IBMUSER.MANAJAZZ.SRCLIB

//  SET COPYLIB=IBMUSER.MANAJAZZ.CPYLIB

//COMPILE EXEC IGYWCL

//COBOL.SYSIN    DD DSN=IBMUSER.MANAJAZZ.SRCLIB(CRDTA1),DISP=SHR

//COBOL.SYSLIB   DD DSN=IBMUSER.MANAJAZZ.CPYLIB,DISP=SHR

//LKED.SYSLIB DD

//        DD

//        DD DSN=IBMUSER.MANAJAZZ.LOADLIB,DISP=SHR

//LKED.SYSLMOD   DD DSN=IBMUSER.MANAJAZZ.LOADLIB(CRDTA1),

//  UNIT=,SPACE=

//*** RUN (RECENTLY COMPILED) BATCH PROGRAM

//GO      EXEC PGM=CRDTA1

//STEPLIB  DD DSN=IBMUSER.MANAJAZZ.LOADLIB,DISP=SHR

//SYSOUT   DD SYSOUT=*

//SYSUDUMP DD SYSOUT=*

//SORTLIB  DD DSN=SYS1.SORTLIB,DISP=SHR

//SORTWK01 DD UNIT=SYSDA,SPACE=(CYL,(20,5))

//* INSERTED DD STATEMENTS BASED ON PROGRAM

//IN1A     DD DSNAME=IBMUSER.MANAJAZZ.SRCLIB(DTDTA1),DISP=SHR

//IN1      DD DSNAME=IBMUSER.FILES.IN1,DISP=SHR

 

Physical Sequential Files

Find the DD statement for the output file: in this case it’s the last statement in the job.  Change this to have DISP=(NEW,CATLG), and add a suitable SPACE parameter.  You may also need to give other parameters like VOL.

VSAM files

For VSAM the file should be created with the utility IDCAMS before you write data to it.  For example: -

SQL files (tables)

As with VSAM, tables in an SQL database (DB2, ORACLE, or SQL-Server) must be created before they can be processed by Jazz.  Refer to the chapter “Using Jazz with SQL” for more information.

Writing Data with NO Input

Instead of reading and re-formatting a character file you may wish to simply create new records by program logic.  Here I want to create three records in a test file, defined like this: -

DEFINE Parts VSAM DATA(

    Partnbr INTEGER KEY,

    PartName CHAR(30) DKEY,

    StandardPrice DECIMAL(7,2))

    DSNAME 'ibmuser.vsam.Parts';

 

Instead of PROCESS I wrote FOR IX = 1 TO 3; and within the FOR loop I wrote statements to create the data that I wanted for these three records.  Here is the complete program: -

PROGRAM PartsCR BATCH;

COPY Parts;

FOR JZ.IX = 1 TO 3;

    parts.partnbr = JZ.IX * 2;

    #361 E Assignment to a key field

    Parts.partname = 'Part Name';

    Parts.StandardPrice = JZ.IX * 5;

    WRITE Parts;

    #378 W Batch WRITE used - you may need to edit the JCL

    #348 W VSAM file must be empty

    PRINT (Parts.*) ;

END FOR;

 

Using IDCAMS I created the VSAM file: -

      DEFINE CLUSTER (NAME (IBMUSER.VSAM.PARTS) TRACK(10 10) -  

          VOLUMES(VPWRKA) RECORDSIZE(30 100) KEYS(4 0) INDEXED) 

 

I then processed the program above to JCL.   Jazz created JCL including a DD statement for the alternate index (AIX) required by the Duplicate Key defined with Partname: -

            PartName CHAR(30) DKEY,

I haven’t created this AIX, so I removed the highlighted line from the GO step: -

//GO      EXEC PGM=PARTSCR

//STEPLIB  DD DSN=IBMUSER.MANAJAZZ.LOADLIB,DISP=SHR

//SYSOUT   DD SYSOUT=*

//SYSUDUMP DD SYSOUT=*

//SORTLIB  DD DSN=SYS1.SORTLIB,DISP=SHR

//SORTWK01 DD UNIT=SYSDA,SPACE=(CYL,(20,5))

//* Inserted DD statements based on program

//PARTS    DD DSNAME=IBMUSER.VSAM.PARTS,DISP=SHR

//PARTS1   DD DSNAME=IBMUSER.VSAM.PARTS1,DISP=SHR

//REPORT1  DD SYSOUT=*

 

With this change the program ran, producing this output: -

 

Printed at 24 Apr 2016, 22:56:41        Report1       Page   1

*--Partnbr---* *----------PartName----------* StandardPrice  

             2 Part Name                               5.00  

             4 Part Name                              10.00  

             6 Part Name                              15.00 

 

IDCAMS was then used to create the AIX with

 DEFINE AIX (NAME(IBMUSER.VSAM.PARTS1.AIX) -              

             RELATE(IBMUSER.VSAM.PARTS) -                   

             TRACK(10 10) VOLUMES(VPWRKA) KEYS(30 4) -      

             NONUNIQUEKEY UPGRADE REUSE)

 DEFINE PATH (NAME(IBMUSER.VSAM.PARTS1) -                  

           PATHENTRY(IBMUSER.VSAM.PARTS1.AIX) UPDATE)     

 BLDINDEX INDATASET(IBMUSER.VSAM.PARTS) -                    

            OUTDATASET(IBMUSER.VSAM.PARTS1.AIX)   

Parts is now set up as described in the Jazz definition, directly accessible with either Parts.Partnbr and Parts.PartName         

More Complex Logic

We can easily enhance our logic to select particular records with PROCESS file WHERE …, to use GET to retrieve matching records from other files and include fields from these records in our output, and to calculate data such as record counters and running accumulators.  These are left as exercises for the reader.

Direct-Access Updating by Batch Programs

In the previous chapter we used GETWHERE to retrieve a record by key value: -

PROGRAM AAnExmpl BATCH; 

COPY IN1;

COPY FR; 

PROCESS IN1 WHERE (IN1.Region > 5) ORDER(IN1.Region, IN1.District, IN1.Name);

    GET FR WHERE (FR.Region = IN1.Region);

    PRINT(IN1.Region, Fr.Name, IN1.SalesThisMonth SUM, IN1.SalesYTD SUM)

        BREAK(IN1.Region, IN1.District);

END PROCESS IN1;

 

We can use GET to update records like this: -

    GET FR WHERE (condition) UPDATE;

UPDATE means that when the next FR record is needed (and at end of program), if it has changed since it was read then it will be re-written to the file. This makes file updating very simple.  Suppose, for example, we want to add the value of IN1.SalesThisMonth into FR2.TotalSales.  We could achieve this like this: -

 PROCESS IN1 WHERE (IN1.Region > 5) ORDER(IN1.Region);             [Note 1

    GET FR2 WHERE (FR2.Region = IN1.Region) UPDATE;                      [Note 2, 4

    FR2.TotalSales  += IN1.SalesThisMonth;                                                 [Note 3

END PROCESS IN1;

 

Let’s look at how this logic works: -

1.                  PROCESS reads the input file IN1, sorting it by IN1.Region and selecting only records for which IN1.Region is greater than 5.

2.                  If there is at least one record meeting the WHERE criteria, then statements within the PROCESS/END are executed.   For the first such record, GET FR reads the appropriate record

3.                  The assignment statement adds IN1.SalesThisMonth to FR.SalesThisMonth.  The operator  += makes this statement the same as if we had written  FR.SalesThisMonth  = IN1.SalesThisMonth + FR.SalesThisMonth; 

4.                  When we reach the GET statement for the second record, it may have the same or a different value of IN1.Region.  If it has the same value then nothing happens and the program continues with the record that has already been read.  This means that if there are two or more records for the same value then we continue to add the input data into the record, without any further I/O.  However if the value has changed then the program will rewrite the previous record if it was found by the previous GET, or write a new record if the previous GET didn’t find a record and so created a new one.   We will get the correct results whether or not we use ORDER, but our program will minimize I/O and so be much more efficient if we do.

5.                  When the program finishes it will check to see if there is a pending update, and write/rewrite the FR2 record if so.

 

Note that this program would work correctly if ORDER were omitted from the PROCESS statement, but without grouping the IN1 records by IN1.Region the GET statement might have to rewrite and read FR records with every IN1 record.  Without ORDER the program may take much longer to run!

Sequential Updating

You can update a VSAM or SQL file by adding UPDATE to the PROCESS statement: -

PROGRAM PartsUP BATCH;

COPY Parts;

DEFINE W DATA(PreviousPrice LIKE Parts.standardprice);

PROCESS Parts UPDATE;

    W.PreviousPrice = Parts.StandardPrice;

    Parts.standardPrice += 5;

    PRINT (Parts.*, W.PreviousPrice) ;

END PROCESS Parts;

 

As the PRINT shows, prices have been increased by 5: -

Printed at 25 Apr 2016, 00:02:16              Report1               Page   1

*--Partnbr---* *----------PartName----------* StandardPrice PreviousPrice  

             2 Part Name                              10.00          5.00  

             4 Part Name                              15.00         10.00  

             6 Part Name                              20.00         15.00 

Of course the update logic might be more sophisticated than simply adding 5 to the price, but this remains a simple process provided that we are updating existing records in a VSAM file.

Sequential Merge Updating

Jazz doesn’t yet support full “Merge Updating”, where a transaction file is merged with a masterfile creating a new masterfile that may contain new records, and several transactions may update the same masterfile record. Enhancements to the PROCESS statement for this are planned: refer to the description of the PROCESS statement for details.  However this is not a priority for implementation