The PROCESS Statement

PROCESS retrieves, and may update, all the records which satisfy a given condition.  For example,

PROCESS Customer WHERE Customer.Balance > 1000; [Not interested in small creditors

     PRINT (Customer.Name, Customer.Address, Customer.Balance);



PROCESS is an I/O loop that reads each record that you want and processes it with the statements within the statement block. At END PROCESS the record may be updated or copied, and then the next record is read.  When all records have been read (which may be straight away if there are none meeting the WHERE criteria, or the file is empty), statements (if any) following END PROCESS are executed.


For physical sequential files and VSAM the WHERE clause is a filter: every record is read and brought back to the program, but those not meeting the WHERE criteria are immediately discarded and the next record read. For SQL database tables the I/O logic may be able to use indexes and selection logic to avoid returning unwanted records to the program

Statement Format

Process Record-Name [,Record-Name-2 COLLATE(collate expression)]

                [WHERE (condition)] [UNTIL (condition)]  [TS(nbr)] [INDEX(field)]

                [ORDER(field list) | [UPDATE]

                [SQL Options]


The record name, Customer in the example above, must be the name of a record defined previously.  This record must have type VSAM, SQL, TS, or one of the “Physical Sequential” types: F, FB, V, VB, or U. It cannot have type WorkingData, Screen, System, etc. 


For Physical Sequential types and VSAM a PROCESS statement must not appear within the scope of another PROCESS statement or a FOR loop. It may however be within the scope of an IF statement. Thus the following code is invalid unless Record2 is defined as TYPE(SQL): -

Process Record1;

    Process Record2 Where Record2.KeyValue = Record1.KeyValue;

        do something

    End Process;

End Process;


PROCESS will process each record in the sequence that the system reads them. If this is not the correct sequence then you should use ORDER to ensure that records are correctly sequenced.  For TYPE(SQL) this will insert an ORDER BY clause in the SQL SELECT statement. For VSAM and physical-sequential files ORDER will cause a sort to be used.

Statement Options


WHERE specifies a condition that must be true for a record to be considered. For example


        F1 SMALLINT,

        F2 CHAR(5),

        F3 VARCHAR(15),

        DT DATE);


Records for which the value of F1 is not 15 will be discarded. Only those with F1=15 will be passed on to the following statements within the PROCESS block.


Conditions follow the same rules as in IF statements.    See Combining WHERE, UNTIL, INDEX, and ORDER Options if you’ve used more than one of these in your PROCESS statement.


UNTIL specifies a condition that, if true, will terminate the PROCESS loop, causing execution to resume after the END PROCESS statement.  Conditions follow the same rules as in IF statements.  See Combining WHERE, UNTIL, INDEX, and ORDER Options if you’ve used more than one of these in your PROCESS statement.   You may also write an UNTIL condition on the END PROCESS statement: the condition is then tested at the end of the loop, so at least one record will be read (unless there’s an immediate endfile of course).


(CICS programs only).  It is highly unlikely that you will write this option yourself, more likely Jazz has inserted it. 


In a classical CICS program a typical design uses a scrolling area to display a set of records.  For example the screen may display some customer details, and a list of current orders for that customer: -


PROCESS is used to read the set of records, but there may be too many to display on the screen.  For example, here there are only enough lines to display 12 orders.  What if there are more: for example, let’s say a customer has 30 open orders?  Your program needs a way of keeping track of these records so that you can easily go forward or back through them with PF8 (Down) and PF7 (Up).  Jazz does this by creating an in-memory TS file containing the record keys, and it will add a definition of this file to your program, and this option to the PROCESS statement. 



    OrdNbr LIKE Orders.OrdNbr);

PROCESS Orders WHERE(Orders.Ordnbr=CustF.Ordno) TS(2);

This is all automatic, and Jazz looks after creating this file, writing and reading records to it, using it to read particular records from Custf, and creating the visual clues like


at the bottom of your screen.


The statements between PROCESS and END PROCESS are executed as the records are processed from the TS file, i.e. for the up-to-12 records that can be displayed, not for the full 30 records.  This needs to be considered if your logic counts or sums data: the calculated counts and sums will refer to the records displayed, not the full set.


Index names a field that counts the records that are processed by the PROCESS/END PROCESS loop.  For example: -



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

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

    PRINT (WS.RNBR, in1.region, FR.Name, in1.district, IN1.Name, in1.district, IN1.BillingCycle,

        IN1.SalesThisMonth SUM,IN1.SalesYTD SUM)

        BREAK(in1.region, in1.district);



This assigns the current record count to WS.RNBR.  On completion on the process loop, it will contain the number of records that have been processed.  This count is the count of records that were accepted by the WHERE, so that (in this example) the count will not include any records with IN1.Region <= 5.  When ORDER is used then the INDEX field (WS.RNBR) reflects the sorted order, not the order in which the records were originally read from the file. 


When the TS option is used to display a number of lines on a 3270-type screen then the record count will reflect the line number not the original record count.  For example, consider this code fragment: -


            OrdNbr LIKE Orders.OrdNbr);


            Line SMALLINT);

        PROCESS Orders WHERE (orders.ordcustid = custf.account) TS(2) INDEX(WS.Line);


        END PROCESS orders;

Imagine that for this customer there are 30 current orders but the screen only has room for 10 at a time. WS.Line will have a value from 1 to 10 within the PROCESS / END PROCESS.  A WS.Line = 1 value might correspond to any of the 30 records read, depending on the user’s use of PF7/8 (up/down) and other processing.


The field named should have type SMALLINT in CICS, and SMALLINT or INTEGER for batch. You can use field names “JZ-Nxxx”, for example “JZ-N1” without defining them: these will be automatically defined for you as SMALLINT fields.  You may refer to the INDEX field like any other number, but you should not change its value, and you should not use the same INDEX field in any other PROCESS or FOR statement.


See Combining WHERE, UNTIL, INDEX, and ORDER Options if you’ve used more than one of these in your PROCESS statement.


(Batch programs only)


The ORDER option specifies the sequence in which the input is to be processed.



        Region SMALLINT,

        District SMALLINT,

        Name CHAR(30),

        Balance MONEY(7,2));

PROCESS D1 ORDER(D1.Region, D1.District, D1.Name);

    PRINT (D1.*);



The ORDER option above will sort the records so that Regions will be in order from 1 to 9999.  Within each Region, Districts will be in ascending order. Within each District, Names will be ascending order.  Note that the order is defined by the collating sequence of the system: for CHAR fields on a mainframe using EBCDIC coding, lowercase letters come before uppercase letters which come before numbers, whereas most other systems use ASCII in which numbers come before uppercase letters which come before lowercase letters. To confuse things further, if the sort is done by SQL the ordering may ignore case.


With each field you can specify DESC if you want descending order instead of the default, ascending.  Thus

PROCESS D1 ORDER(D1.Region DESC, D1.District, D1.Name);

causes input to be processed from high D1.Region to low (or negative).  Within this order, D1.District will continue to be processed in ascending order.


BREAK specifies that there will be a control break if the field changes, causing appropriate subtotals to be calculated and printed by PRINT statements within the loop.  Thus

PROGRAM aanexmpl BATCH;  



PROCESS IN1 WHERE (IN1.Region = 1 | IN1.Region = 6)

        ORDER (IN1.Region BREAK, IN1.District BREAK, IN1.Name) INDEX JZ.JZ-INDEX;

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

    PRINT (IN1.Region,FR.Name, IN1.District, IN1.Name, IN1.BillingCycle,

        IN1.SalesThisMonth SUM, IN1.SalesYTD SUM);



produces a report like this: -

Region *--------Region Name---------* District *----Name-----* BillingCycle *SalesThisMonth *--SalesYTD---* 


     1 New Zealand                           1 BLEE,ElliottLin    November           206.02        4,640.25 

     1 New Zealand                           1 HANNAH,Honour      December           206.02        4,640.25 

     1 New Zealand                           1 JAMES,Elizabeth    October            206.02        4,640.25 

     1 District Subtotal                     1                                       618.06       13,920.75 


     1 New Zealand                           2 HARRINGTON,Laur    February           206.02        4,640.25 

     1 New Zealand                           2 HAY,Gordon         May                206.02        4,640.25 

     1 District Subtotal                     2                                       412.04        9,280.50 


Control breaks are only effective directly within PROCESSEND PROCESS.  If the PRINT statement is tucked away in a ROUTINE, then the control breaks are ignored.  With this logic: -

PROCESS IN1 WHERE (IN1.Region = 1 | IN1.Region = 6)

        ORDER (IN1.Region BREAK, IN1.District BREAK, IN1.Name) INDEX JZ.JZ-INDEX;

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

    PERFORM PrintLine;


ROUTINE Printline;

    PRINT (IN1.Region,FR.Name, IN1.District, IN1.Name, IN1.BillingCycle,

        IN1.SalesThisMonth SUM, IN1.SalesYTD SUM);

END ROUTINE Printline;  

the report looks like this: -

Region *--------Region Name---------* District *----Name-----* BillingCycle *SalesThisMonth *--SalesYTD---*                        


     1 New Zealand                           1 BLEE,ElliottLin    November           206.02        4,640.25                        

     1 New Zealand                           1 HANNAH,Honour      December           206.02        4,640.25                        

     1 New Zealand                           1 JAMES,Elizabeth    October            206.02        4,640.25                        

     1 New Zealand                           2 HARRINGTON,Laur    February           206.02        4,640.25                        

     1 New Zealand                           2 HAY,Gordon         May                206.02        4,640.25                        

     1 New Zealand                           4 GREGORY,Nancy      November           206.02        4,640.25                        

     6 France                               11 EPTHORPE,SARAHE    February           206.02        4,640.25                        

     6 France                               11 GREGORY,Isabel     July               206.02        4,640.25                        

 Grand Total                                                                       8,858.86      199,530.75                        


See Combining WHERE, UNTIL, INDEX, and ORDER Options if you’ve used more than one of these in your PROCESS statement.

Combining WHERE, UNTIL, INDEX, and ORDER Options

If you have used more than one of these options in your PROCESS statement then you may need to be aware of the order in which they are executed, and how they can interact.


The basic logic combining WHERE, UNTIL, and INDEX is a loop, in which logic

1.                  Reads the next (might be first) record and tests for End-of-file

2.                  Checks the UNTIL condition.  If true, End-of-file is set true

3.                  The Process block (statements from PROCESS to END PROCESS) are executed if End-of-file is false and the WHERE condition is true

4.                  If INDEX is used, the first statement of the Process block will increment the INDEX variable.


Note that the order in which the options are processed has nothing to do with the order in which you write them.  You’ll get the same results from

            PROCESS file WHERE condition1 UNTIL condition2 INDEX IX1;

as you do from

            PROCESS file INDEX IX1 UNTIL condition2 WHERE condition1;

or any other ordering of the statement options.


Because UNTIL (and WHERE) come before the Process block, but the index increment implied by an INDEX option is the first statement of the block, if the condition references the index variable then the value tested may be one less than you expect.  Thus

PROCESS IN1 UNTIL(Copy-Stats.Input.Count > 2) INDEX Copy-Stats.Input.Count;

causes THREE records to be processed, NOT two.   It is for this reason that when a conversion program is generated with test mode checked the UNTIL clause is UNTIL(Copy-Stats.Input.Count > 0).


When the PROCESS contains ORDER then unless the file type is SQL it will use an explicit sort with input and output procedures.  Jazz puts UNTIL and WHERE conditions into the input procedure so that unwanted records are discarded as soon as possible, while the Process block is put into the output procedure.  This makes the generated program as efficient as possible, but can lead to some unexpected results.  For example: -

          PROCESS IN1 WHERE(IN1.Region <> 3) INDEX Copy-Stats.Input.Count ORDER(IN1.Region);



With my test data this PROCESS statement will print 14 records for Region=1, 43 records for Region=2, none for Region=3, 26 for Region=4, and finally 3 records for Region=11.  You might think that this simply adding UNTIL IN1.Region = 6 will produce the same results for regions 1, 2, 4 and 5 but then terminate.  Instead I got these results: -

Printed at 06 Sep 2016, 00:18:46                    Report1                    Page   1

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

     1        6 XXXXXXXXXXXXXXX         206.02   4,640.25    June      2013-06-11      

     2        3 YYYYYYYYYYYYYYY         206.02   4,640.25    April     2013-06-11      

     4        7 YYYYYYYYYYYYYYY         206.02   4,640.25    September 2013-06-11      

     4       10 YYYYYYYYYYYYYYY         206.02   4,640.25    June      2013-06-11      

     5        7 Delete                  206.02   4,640.25    ********* 2013-06-11      

     5        8 XXXXXXXXXXXXXXX         206.02   4,640.25    May       2013-06-11      

     7        9 YYYYYYYYYYYYYYY         206.02   4,640.25    November  2013-06-11      

     8        4 XXXXXXXXXXXXXXX         206.02   4,640.25    April     2013-06-11      

     9        7 XXXXXXXXXXXXXXX         206.02   4,640.25    March     2013-06-11      

     9       11 STURGESS,Shirle         206.02   4,640.25    June      2013-06-11      

     9        4 XXXXXXXXXXXXXXX         206.02   4,640.25    March     2013-06-11      

    10        6 MCNAUGHTON,Vera         206.02   4,640.25    February  2013-06-11      

    11        4 SIMPSON,Laura           206.02   4,640.25    April     2013-06-11      

    11        4 SIMPSON,Laura           206.02   4,640.25    April     2013-06-11    


In the unsorted test data the first record for which IN1.Region = 6 was the 15th.  Because UNTIL (and WHERE) are before the sort only the first 14 records were passed through it to the Process block containing PRINT(In1.*), giving the results shown.



PROCESS IN1 WHERE(IN1.Region <> 3) INDEX Copy-Stats.Input.Count
            ORDER(IN1.Region) UNTIL Copy-Stats.Input.Count > 2;

effectively ignores the UNTIL condition.  This is because the statement

Copy-Stats.Input.Count += 1; 

implied by the INDEX option is the first statement of the PROCESS block, and therefore is put in the output procedure.  The value of Copy-Stats.Input.Count is always zero before the sort, so this condition is always false.


For file type SQL any sorting is handled implicitly by the database (DB2, Oracle), so WHERE and UNTIL are handled as they are for a basic unsorted file, whether or not the PROCESS contains ORDER. 


With the UPDATE option the record which has been read by the PROCESS may be updated at the END statement, before the next record is read.  The file type must be VSAM, SQL, or XIO with an XREWRITE option.  This option is invalid for other file types (F, FB, V, VB, U).


The PROCESS statement only reads existing records, unlike GET it does not create new ones, so PROCESS file …. END PROCESS file UPDATE will not create any new records.  Thus you write logic like this: -


            Vsamfile.field = newvalue;



Note that it is the UPDATE on the END statement that is important, UPDATE can be omitted on the PROCESS statement itself.


Within the PROCESS loop you may not change the record’s key value, as this would create a new (and possibly duplicate) record.


For VSAM and XIO you may not use UPDATE and ORDER together.  With ORDER Jazz will generate a sort and the original record is no longer available at the point in the program where the update would be done.   This restriction does not apply to SQL.

Preventing Updates

As in a GET statement, you can prevent updates with UPDATE filename CANCEL: -

PROCESS Vfile WHERE (Vfile.charfield = r2.charfield) UPDATE;

IF Vfile.Region = 5;




Options COLLATE, and the SQL Options are not implemented yet.

Cancelling COPY and UPDATE

Within a Process block you can turn off the COPY or UPDATE that would otherwise occur at END PROCESS by setting $UpdatePending to False, as you would with a GET: -


    IF condition THEN

            Input.$UpdatePending = False;

    END IF;


COLLATE: Merge-Updating Sequential Files

You cannot update records within a sequential file: instead you create a copy with changes. For this, a special form of the PROCESS statement will be provided, naming two input files.  The first of these must be the Input Master File, the second is the Transaction File. A new option, “COLLATE”, names the keys that have to be matched. You cannot use a WHERE option[1].


This is most easily explained through an example. First, here is the basic form of the PROCESS loop with both master and transaction files: -

PROCESS InputMaster, Transactions

              COLLATE(InputMaster.Account = Transactions.Account)




Statement Rules

·         The first file is considered to be the master file, and should have only one record for each key value.  If there are duplicate records (i.e. same key value) in the master file, then all updates will be applied to the first record, and others will be copied unchanged.

·         The second file is the transaction file. It may contain any number of records for any particular key value, and may include key values that are not represented on the input master file.

·         The COLLATE option gives a condition that is of the form: -

·         Name in File1 = Name in File2.

·         The comparison operator must be “=”.  Comparisons like “>”, “<=” and so on are not permitted.

·         The comparison must be valid. If “A = B” would be invalid in an IF statement, then COLLATE(A = B) is invalid.

·         The comparison must be given in this order: A must be defined within the first file, and B within the second.  A and B may be the names of fields or of groups, but may not refer to fields or groups that have any dimension.

·         You cannot use expressions within the comparison: COLLATE(A = B/100) is invalid.

·         The logic critically depends on the master file and transaction file being presented to the program in ascending key sequence, and the generated program will include logic to check this. Key values at the beginning of files are considered to be lower than any possible record, and at end of file are considered to be higher than any possible record. Logic generated by Jazz allows records to have key values of 0, LOW-VALUES and HIGHVALUES, and the smallest and largest possible values that the key field(s) can contain, unless these values are prohibited by RANGE, MAX, or MIN options in their data definition.

·         If there is an ORDER clause, it applies only to the transaction file. The master file (file1 in our example) is assumed to be in the correct order.

·         Either the PROCESS statement or the corresponding END PROCESS statement will have a COPY option naming the output file, for example

      PROCESS File1, Trans COLLATE(File1.Key = Trans.Key) COPY(File2);

·         If a definition for the output file (“File2” in the example above) can’t be found, then Jazz will create and insert in the Jazz copy library a definition named “File2” like this: -

COPY File1;


Users will need to add further information like the output file’s DSN

·         If the statement includes NoAdd

      PROCESS File1, Trans COLLATE(File1.Key = Trans.Key) COPY(File2) NOADD;

then a record from the transaction file (Trans) that does not collate with a record from the masterfile (File1) won’t cause a new record to be added to the output file (File2). Instead it will be reported in a transaction error report.

How PROCESS file1, file2 works.

Like any PROCESS statement,

PROCESS File1, Trans COLLATE(File1.Key = Trans.Key) COPY(File2);

defines an I/O loop in which a record is read and processed, but when the PROCESS statement names two files, the loop iteration may read from either file. Only one record is read per iteration, so the program EITHER reads the next File1 record OR the next Trans record. It behaves like this: -

·         Initially the program reads the first record from both files. Whenever the file1 record (i.e. File1) is low, i.e. File1.Key < Trans.Key, the Process block is (normally) skipped and the program goes straight to the End Process statement. If this has a COPY option, the record is copied to output.

·         When the transaction record is not found on the masterfile an initialised record is created. The collating field will be set to the value sought, other fields will be initialised according to their default values (normally zeros and blanks). This record can then be updated, and will be written (unless you cancel the copy by setting $UpdatePending to false) when a higher key value is read from either file on a later iteration of the PROCESS loop.

An example of Sequential Updating

For our example we’ll define the input master file like this: -



       Balance MONEY(7,2));

and the transaction file will have a similar definition.


Our masterfile contains these records: -














In the Trans file there are the following records:-












The logic to be applied is: -

·         If a transaction is for an account that isn’t in the master file, create a new record.

·         There may be any number of transactions per master file record.


Here is the complete program: -


COPY File1

COPY Trans


PROCESS File1, Trans COLLATE(File1.Key = Trans.Key) COPY(File2);

    File1.Balance += Trans.Balance;



Output Records will be: -















SQL Options

When the input data has type SQL then the PROCESS statement will support extra options, making available most of the power of SQL’s SELECT statement. Details will be finalised when SQL is implemented, but the following is envisaged: -

1.                  The input may consist of one or several SQL tables.  If there is more than one table, then 2nd (etc) tables are related through a JOIN condition is required.  For example

PROCESS Table1 JOIN-type Table2 ON (Join-relationship)

a.      Join-Relationship is one or more equality conditions relating the two table, for example

                                                   i.      PROCESS Custf JOIN Orders ON (Custf.Account = Orders.OrdCustid)

Comparison operators other than “=” are invalid

If there are multiple conditions, they must be related by the boolean operator & (AND)

b.      JOIN-type is JOIN | LEFTJOIN. 

                                                   i.      JOIN => if there are no records satisfying the JOIN relationship, then nothing is returned, and the PROCESS/ENDPROCESS is terminated

                                                 ii.      LEFTJOIN => If there are no records satisfying the JOIN relationship, then the PROCESS is executed once for the Table1 record, with an initialised Table2 record

                                                iii.      There are no current plans to implement RIGHTJOIN

2.                  GROUP (field [,field]…) causes the data to be grouped by the field(s) named.   If ORDER is also present then the GROUP fields must be all or a left subset of the fields named in ORDER.  If ORDER is absent then GROUP implies ORDER with the same field list.

3.                  HAVING (condition).   Like WHERE, except that this applies to the group so that it can check for conditions like $COUNT > 0 and $SUM(field) > 0.  HAVING is only valid if GROUP is also present.

4.                  In HAVING conditions the SQL aggregate functions supported by the database are available: they are referenced as $COUNT, $SUM, $MAX, $MIN and $AVG.


[1] It may prove possible to allow WHERE.  Like the ORDER option, this would apply only to the Transaction file.  Is this useful?