Previous Topic: FOR Statement (VSAM Files)Next Topic: FOR NEW Statement (VSAM Files)


FOR EACH/FIRST Statement (VSAM Files)

The FOR EACH and FOR FIRST statements process a set of records (or a single record) from a VSAM file. The FOR construct is iterative. With each iteration, it returns the next record in the requested set. All of the statements in the scope of the FOR apply to each record selected. FOR FIRST and FOR EACH are the only constructs that update or delete a record.

This statement has the following format:

<<label>>
    [EACH            ]
FOR [ALL             ]   dataview_name[NO UPDATE]
    [[THE] FIRST [n] ]
      [WHERE wherecondition ]
      [ORDERED BY  [ASCENDING ] [FIELDNAME ]]
      [            [DESCENDING]              ]
               statements
        [WHEN NONE     ]
        [    statements]
        [WHEN ERROR     ]
        [     statements]
ENDFOR
<<label>>

An optional 1‑ to 15‑character name of the FOR construct. You can use this label to refer to the construct in QUIT and PROCESS NEXT statements and as the operand of certain functions such as $COUNT.

EACH|ALL

Indicate that the statements in the scope of the FOR construct apply to every record that satisfies the where condition. You can use the reserved words EACH and ALL interchangeably.

[THE] FIRST [n] (Default)

Specifies that the statements in the scope of the FOR construct apply to the first n records that satisfy the where condition. The value specified for n can be a numeric literal or the name of a numeric field. The default is FIRST 1. You can add the reserved word THE for readability. When you use FOR FIRST n with a where condition and an ORDERED BY clause, all records that satisfy the where condition are ordered and then the first n ordered records are selected.

dataview‑name

Specifies the name of the VSAM dataview.

NO UPDATE (Optional)

Specifies that the records processed by this FOR construct are not updated. This applies even if the dataview is defined as updateable (Update Intent = Y in the parameter definition). If used, this clause must immediately follow the dataview name. The use of this clause for browsing provides significant efficiency gains.

WHERE clause (Optional)

Specifies that the statements in the scope of the FOR construct apply to those records that satisfy the specified condition. If the WHERE clause is omitted, all records in the data set are processed. The WHERE clause can also determine the index where the data set is accessed (see the description of the ORDERED BY clause for more detailed information).

where‑condition

A condition (as defined in the beginning of chapter 2) with the following further qualifications:

  • The left‑hand operand of each relational‑expression must be the identifier of a field or group in the dataview referenced or the function $RBA (for an ESDS data set) or $RRN (for an RRDS data set).
  • If the left‑hand operand is an alphanumeric field, the right‑hand operand must be an alphanumeric expression.
  • If the left‑hand operand is a numeric field, the right‑hand operand can be a numeric expression, an alphanumeric expression, or a non‑alpha group that is not a panel group or dynamic matching parameter. When the right‑hand operand is not a numeric expression, a warning is issued when the program is compiled and, if the right‑hand operand cannot be converted to numeric, a run‑time error occurs.
  • If the left‑hand operand is the function $RRN, the value of the right‑hand operand must be greater than or equal to one. If the left‑hand operand is the function $RBA, the value of the right‑hand operand must be greater than or equal to zero.
  • A field name used as the left‑hand operand of a relational‑expression in a where condition does not need to be qualified with a dataview name since it refers implicitly to the dataview in the FOR clause. However, reserved words used as operands must always be qualified. The $RBA and $RRN functions must not include the dataview name.
  • The right‑hand operand of a relational‑expression can be any arithmetic or alphanumeric expression, but cannot reference any fields in the dataview named in the FOR clause. In particular, subscripts that qualify dataview array elements cannot depend on data in the dataview.
  • If the condition is a condition name (a type C field), it must be from the dataview being referenced.
  • Simple conditions cannot be Boolean functions or flags.
ORDERED BY clause (Optional)

Identifies the index by which a KSDS data set is accessed and determines the logical order in which the records in any VSAM file are processed. If this clause is omitted, the index is determined as follows:

If the ORDERED BY clause is omitted and a WHERE clause is specified that uses more than one key, an error message is issued when the program is compiled.

ASCENDING/ DESCENDING (Optional)

Specifies whether processing proceeds from low to high values (ASCENDING) or high to low values (DESCENDING). ASCENDING is the default. The effect of ASCENDING/DESCENDING depends on the type of the VSAM data set:

ESDS data set

Records are retrieved moving through the file in a forward (ASCENDING) or backward (DESCENDING) direction, according to the relative byte address (RBA). You cannot specify a field‑name to determine the order.

RRDS data set

Records are retrieved according to the relative record number, moving from the lowest number to the highest (ASCENDING) or from the highest number to the lowest (DESCENDING). You cannot specify a field‑name to determine the order.

KSDS data set

Records are retrieved according to ascending or descending values in the field specified as field‑name. Records are retrieved in collating sequence. If the specified field‑name identifies a signed numeric (type N) field, the collating sequence cannot return the records in the expected algebraic order.

field‑name

The name of a field or group of fields defined as a primary or alternate key. You can only specify the field‑name for a KSDS file. You can specify only one field‑name. It cannot be subscripted.

statements

PDL statements. The statements in the logical scope of a FOR construct can reference any field in the record most recently processed by the FOR. However, it is the programmer's responsibility to check the record type before referencing any fields in a variable‑segment record.

WHEN NONE

An optional postscript that specifies that when none of the records meets the where condition, the statements following the WHEN NONE execute.

WHEN ERROR(Optional)

Specifies statements to execute when a dataview error is encountered in the scope of the FOR construct. If WHEN ERROR is not specified, errors are processed by the user‑defined or default error procedure.

The statements specified following a WHEN ERROR clause can access $ERROR functions and should resolve the error with either a PROCESS NEXT or DO ERROR statement. If processing falls through to the ENDFOR, the $ERROR functions are no longer available.

Note: Only dataview errors are handled by the WHEN ERROR clause. System and internal errors are handled by the user‑specified or default error procedure.

ENDFOR

A reserved word that marks the end of the FOR construct. If FOR statements are nested, the most recent undelimited FOR construct is delimited by the first occurrence of ENDFOR. Each FOR in a nested FOR construct must have a corresponding ENDFOR.

Updates are actually written to the file at the ENDFOR of the current iteration of the FOR construct.

You can reference fields processed by each iteration of the FOR construct in PDL statements. The identifier is the field name or the field name with the dataview name as qualifier. For example, the field ACCT‑NO in the ACCT dataview can be compared to the field ACCT‑NO in the panel named PNL1:

IF ACCT.ACCT‑NO EQ PNL1.ACCT‑NO ...

You cannot make such references before the FOR executes.

Sometimes it is convenient to first process a dataview record and then refers to its fields rather than coding the actions in the FOR. For example, you can delegate finding the appropriate record to a lower level procedure, as shown in following examples in this section.

Statements outside the logical scope of the FOR construct can access, but not update, data in the dataview record. Data in the last record processed is available after the ENDFOR until another FOR accesses the same dataview, except when the record was deleted or no records were found (WHEN NONE).

The access key used to retrieve records should not be updated in the scope of a FOR statement based on that key. If the access key is the primary key, VSAM does not allow you to change it. If the access key is an alternate key, you can update it in CA Ideal; however the results can be unpredictable:

You must make updates (changes and deletes) in the logical scope of the FOR. Any update of a dataview field in the scope of a FOR logically updates the file. For changes (but not for deletes), the actual update takes place at the ENDFOR for the current iteration. Therefore, any QUIT or PROCESS NEXT executed in the scope of a FOR abandons the update of the current record even for fields whose values already were changed. A checkpoint in the logical scope of the FOR does not commit the current update, because the update does not take place until the ENDFOR.

VSAM records are updated only through the primary index. Before updating a record, CA Ideal rereads the record with exclusive control, using the primary key. CA Ideal can then verify that the record was not deleted or changed since the original access before updating or deleting the record.

During updates, CA Ideal writes a record only when it can determine that the data was altered. CA Ideal always processes CHECKPOINT and BACKOUT requests, even if no actual changes were made to the file. In a CICS environment, the CHECKPOINT and BACKOUT statements execute the CICS SYNCPOINT and SYNCPOINT ROLLBACK commands. In non‑CICS environments, the BACKOUT statement has no effect. The CHECKPOINT statement performs a TCLOSE operation that flushes certain VSAM buffers if there was any VSAM access before the CHECKPOINT.

If statements outside the logical scope of the FOR construct attempt to update the record (with a SET, MOVE, and so on), a run‑time error results.

Make sure that the above actions do not occur in procedures or subprograms called from in the FOR construct.

In the scope of a FOR statement that accesses a VSAM data set, a non‑ideal subprogram should not access that VSAM data set or issue a CICS SYNCPOINT.

The FOR EACH and FOR FIRST statements read the set of records that satisfies the conditions specified in the WHERE clause and then process those records according to the statements entered in the FOR construct. If the WHERE clause is complex, the set of records to read is determined by establishing a search interval based on the conditions in the WHERE clause.

Conditions connected by an OR can result in an overly large search interval. For example, if the WHERE clause specifies the highest value of a key field and the lowest value of a key field, connected by an OR, the search interval includes the entire file.

You can nest any of the FOR constructs as long as each FOR construct refers to a different dataview. Do not nest a FOR construct for a given dataview in another FOR construct for the same dataview.

If more than one record in a file is needed simultaneously, either use two dataviews for the same file or save necessary information in working data.

When a QUIT is executed in the logical scope of a FOR, the next statement executed is the statement after the ENDFOR. When FOR and LOOP constructs are nested, any construct can be abandoned by referencing the optional label in a QUIT statement. See the QUIT statement in this chapter.

You can use the WHERE and ORDERED BY clauses in either order.

You cannot delete records from an ESDS file; therefore, you cannot specify the DELETE statement in a FOR construct that accesses an ESDS file.

When updating an ESDS data set, you cannot change the length of a record. With CA Ideal, you can decrease the logical length of a record (either by reducing the number of occurrences in a variable‑occurrence record or by changing the record to a shorter variable‑segment record with the SET $REC‑SEGMENT command), but the remaining length of the updated record is padded with binary zeros. Do not use this technique when record‑types are identified by record‑length since the original length is retrieved when the record is read.

You can improve efficiency in the following ways:

Examples

In the following example, assume that the BALANCE field is defined as an alternate key for the DELINQUENT‑ACCT dataview. Since the BALANCE field is used in the WHERE clause and there is no ORDERED BY clause, the BALANCE field is used as the access key.

FOR EACH DELINQUENT‑ACCT   
    WHERE BALANCE > 200      
      DO CONTACT‑COLLECTOR
ENDFOR

In the following example, the access key is determined by the ORDERED BY clause since neither the QOH nor the PRICE field are key fields.

FOR FIRST INVENTORY‑ITEM
    WHERE QOH > 50 AND PRICE < 500
    ORDERED BY ITEM‑NAME
        DO PROCESS‑ITEM
ENDFOR

For the INVEN dataview used in the following example, the PRICE field is defined as an alternate key and is used as the access key, based on its use in the WHERE clause. Since PRICE is not a unique key, if the P‑5‑CHEAP‑ITEMS procedure includes an update or a TRANSMIT statement, processing cannot resume after the first iteration of the FOR and an error occurs.

FOR THE FIRST 5 INVEN
    WHERE PRICE < 100
        DO P‑5‑CHEAP‑ITEMS
ENDFOR

In the following example, the EMPLOYEE file is accessed using the unique key EMP‑NO; however, each record in the file is read in sequence to find the records that satisfy the WHERE clause, since the key field EMP‑NO is not specified in the WHERE clause. If EMP‑NO in the PAY‑REC dataview is also a key field, the PAY‑REC dataview is accessed randomly (only the record that matches the EMPLOYEE record is read).

FOR EACH EMPLOYEE
    ORDERED BY EMP‑NO
        WHERE DEPT = 'D'
            FOR EACH PAY‑REC
                WHERE PAY‑REC.EMP‑NO = EMPLOYEE.EMP‑NO
                   DO PROCESS‑PAY
            ENDFOR
ENDFOR

The following example shows the use of the WHEN NONE clause and indicates when you can access and update dataview fields.

FOR FIRST ACCT
   WHERE PAST‑DUE > 90
      ORDERED BY ACCT‑NO
                 : you can refer to or update ACCT.field here
         WHEN NONE
      DO NO‑DELINQ‑ACCT
       ENDFOR
    : you can now refer to ACCT.field if present
    : you cannot update ACCT.field here (unless this
    : is a procedure performed by a DO statement
    : in the FOR, but not in the WHEN NONE)

The following example shows the use of FOR statement is included in the procedure FIND‑CUSTOMER. You cannot update the record in the subsequent IF statement, although you can reference, display, or list the fields.

DO FIND‑CUSTOMER
       IF CUST‑FOUND
                    : you can refer to "CUST.field" here
ELSE
    DO CUST‑NOT‑FOUND
ENDIF
<<FIND‑CUSTOMER>> PROCEDURE
   FOR THE FIRST CUST
      WHERE CUST‑NO = TRANS‑CUST‑NO
         SET CUST‑FOUND = TRUE
      WHEN NONE
         SET CUST‑FOUND = FALSE   ENDFOR

In the following example, the PSS‑MASTER dataview is accessed by relative byte address using the $RBA function to determine the starting record. The dataview name is not specified for the function when it is used in the WHERE clause.

FOR FIRST 21 PSS‑MASTER
    WHERE $RBA GE START‑ADDRESS	:function is not qualified
        statements
ENDFOR
SET LAST‑ADDRESS = $RBA(PSS‑MASTER)	:function is qualified

In the following example, two FOR constructs were specified to retrieve the records that satisfy the condition EMP‑ID < 101 OR EMP‑ID > 846. In this example, the PSS‑MASTER file is a KSDS file with the key field EMP‑ID.

FOR EACH PSS‑MASTER
   WHERE EMP‑ID < 101
       statements
ENDFOR
FOR EACH PSS‑MASTER
   WHERE EMP‑ID > 846
       statements
ENDFOR

With the FOR EACH statement, the results of the two FOR statements above are the same as that for a single FOR EACH statement with the combined condition WHERE EMP‑ID < 101 OR EMP‑ID > 846. However, with a FOR FIRST statement, the results might not be the same since, with the single construct, only the first record that satisfies the combined condition is retrieved, but with a double construct, two records are retrieved.

In the following example, two FOR constructs access a weekly transaction file called TRAN‑FILE. The FOR FIRST construct loads an RBA table from the first record that contains the RBAs used for each day's transactions. The FOR EACH construct retrieves Tuesday's records by comparing the $RBA function to the RBA table values for Tuesday and Wednesday.

FOR FIRST TRAN‑FILE
   SET W‑RBA‑TABLE = TRAN‑FILE.TABLE‑SEG BY NAME
ENDFOR
statements
FOR EACH TRAN‑FILE
   WHERE $RBA GE W‑RBA‑TABLE.TUESDAY
      AND $RBA LT W‑RBA‑TABLE.WEDNESDAY + 1
          statements
ENDFOR