Mainframes 360
The one stop destination for System Z professionals

Wednesday, March 26, 2014

COBOL Tables

COBOL Tables

A large manufacturing company wants to produce a sales report. You are asked to write a COBOL report-generation program to print the sales in every month. You'd have to define repetitious variables JANUARY-SALES, FEBRUARY-SALES, .., DECEMBER-SALES. This is very cumbersome.
       01  WS-SALES-DATA.
  05 JANUARY-SALES PIC 9(07)V99.
  05 FEBRUARY-SALES PIC 9(07)V99.
  05 MARCH-SALES PIC 9(07)V99.
  05 APRIL-SALES PIC 9(07)V99.
  05 MAY-SALES PIC 9(07)V99.
  05 JUNE-SALES PIC 9(07)V99.
  05 JULY-SALES PIC 9(07)V99.
  05 AUGUST-SALES PIC 9(07)V99.
  05 SEPTEMBER-SALES PIC 9(07)V99.
  05 OCTOBER-SALES PIC 9(07)V99.
  05 NOVEMBER-SALES PIC 9(07)V99.
  05 DECEMBER-SALES PIC 9(07)V99.
When there are multiple items of the same type, you can use arrays.

A COBOL table or array is simply a data structure consisting of a collection of elements(values), all of which have the same data description, such as a table of monthly sales. Think of a table as a matrix, a grid of elements.

Let's establish an array of monthly sales amounts for the company. The array consists of 12 sales amounts. The OCCURS clause in COBOL defines the size of the array. Note that, the OCCURS clause may not be used on the levels 01, 66 and 77. To define the array in WORKING-STORAGE with an OCCURS clause would require the following coding.
       01  WS-SALES-TABLE.
  05 WS-MONTHLY-SALES PIC 9(07)V99 OCCURS 12 TIMES.

The WS-SALES-TABLE is one group item that contains the table. WS-MONTHLY-SALES names the elements of the table. OCCURS 12 TIMES defines twelve occurrences of WS-MONTHLY-SALES. You'd refer to any element by its position in the array. For example, the sales in June i.e. the sixth element is referred to as WS-MONTHLY-SALES(06).


The elements of a table could be elementary or group data items. For example, you could create a table of employee SSN and their names. In this example, WS-EMPLOYEES-TABLE is a group item containing the table. WS-EMPLOYEE names the elements of the table, so the individual employees would be referred as WS-EMPLOYEE(01), WS-EMPLOYEE(02), WS-EMPLOYEE(03) and so forth. Similarly, the child elements inside WS-EMPLOYEE(03) would be called WS-EMPLOYEE-SSN(03) and WS-EMPLOYEE-NAME(03).
       01  WS-EMPLOYEES-TABLE.
  05 WS-EMPLOYEE OCCURS 05 TIMES.
  10 WS-EMPLOYEE-SSN PIC X(09).
  10 WS-EMPLOYEE-NAME PIC X(30).


Referring to an item in the table
A table name is a collective name for all the elements in it. WS-SALES-TABLE, WS-EMPLOYEES-TABLE are examples of table names. However, to identify the items of a table, you can use two techniques viz., subscripts and indices.

Subscript is the absolute position of the element in the table. As an example, let's assume that a bank's customer accounts information is stored in a table. Every customer account has a cash account no., balance and customer name. CUSTOMER-ACCOUNTS-TABLE is defined as follows.
       01  CUSTOMER-ACCOUNTS-TABLE.
  05 CUSTOMER-ACCOUNT OCCURS 10 TIMES.
  10 CASH-ACCOUNT-NO PIC X(10).
  10 BALANCE PIC 9(07)V99.
  10 CUSTOMER-NAME PIC X(30).
 
  01 WS-ACCOUNT-SUB PIC 9(04) VALUE 1.
You refer to the 3rd element of the CUSTOMER-ACCOUNTS-TABLE, by the element name CUSTOMER-ACCOUNT, followed by the position or subscript 03 in braces() as CUSTOMER-ACCOUNT(03). The 5th account would be CUSTOMER-ACCOUNT(05). In general, the ith element of this table would be CUSTOMER-ACCOUNT(i). This mechanism applies to any children of the CUSTOMER-ACCOUNT item as well. The code snippet below shows this.
           COMPUTE INTEREST = (BALANCE(03) * NUMBER-OF-YEARS * RATE)/100
  DISPLAY CUSTOMER-NAME(03)
  DISPLAY CASH-ACCOUNT-NO(05)
  DISPLAY CUSTOMER-ACCOUNT(03)
Note that when you refer to CUSTOMER-ACCOUNT(03), it behaves as one whole alphanumeric item.

You can also define a subscript variable like WS-ACCOUNT-SUB and initialize it at run-time, rather than hard-coding the subscript's value. For example, if WS-ACCOUNT-SUB is initialized to 03, CUSTOMER-NAME(WS-ACCOUNT-SUB) refers to the third customer's name. If WS-ACCOUNT-SUB=05, BALANCE(WS-ACCOUNT-SUB) is the 5th account's balance.
           MOVE 03 TO WS-ACCOUNT-SUB
  DISPLAY CUSTOMER-NAME(WS-ACCOUNT-SUB)
  MOVE 05 TO WS-ACCOUNT-SUB
  ADD 100 TO BALANCE(WS-ACCOUNT-SUB)
A diagrammatic representation of the CUSTOMER-ACCOUNTS-TABLE is shown. Array elements are laid out in computer memory in adjacent locations. Because arrays are contiguous in memory, data processing is faster. At run-time, subscripts are converted to a displacement from the start of the table. For example, each CUSTOMER-ACCOUNT element holds 10 + 09 + 30 = 49 bytes. An array of 10 items may be stored at displacements 0, 49, 98, 147,...,490, and an element with subscript i is at an offset 49 x i.


Index is the displacement of an element from the start of the table. Indexes perform better than subscripts, as indexes already store the offset and does not have to be calculated at run-time. Indexes are defined by the INDEXED BY statement on the OCCURS clause.
       01  JOB-ID-TABLE.
  05 JOB-ID PIC X(05) OCCURS 10 TIMES
  INDEXED BY IDX-A

The compiler automatically calculates the value contained in the index as the occurrence number minus 1 multiplied by the size of the table element. Therefore, for the sixth occurrence of JOB-ID, the binary value contained in IDX-A is (6 - 1) x 5 = 25. Consequently, IDX-A variable can be used to address only JOB-ID-TABLE; you can't address any other table using IDX-A except if it has the same number and type of elements.

The SET statement can be used to manipulate an index. While the index is actually a byte offset, COBOL does not expect you to work at that level. Setting an index to 2, will cause the compiler to correctly calculate the byte offset and point to the 2nd element in the table. Likewise, setting an index up by one will move the index to the next element, regardless of the size of the element. The COBOL compiler translates it into a proper offset for you.
           SET INDX-A TO WS-NUMBER
  SET INDX-A TO 2
  SET INDX-A UP BY 1
  SET INDX-A DOWN BY 2

Table processing
Table processing often involves traversing the table and accessing array elements. Suppose, we are interested to find the average sales in the manufacturing organization example. Here's the algorithm. Initialize the index I to 1. Access A[I], followed by incrementing the index to I+1 iteratively, until no more items are left.
       7000-COMPUTE-AVG.
  MOVE ZEROES TO AVG-SALES
  TOTAL
  SET IDX-A TO 1
 
  PERFORM VARYING IDX-A FROM 1 BY 1
  UNTIL IDX-A > 12
  COMPUTE TOTAL = TOTAL + WS-MONTHLY-SALES(IDX-A)
  END-PERFORM
 
  COMPUTE AVG-SALES = (TOTAL/12)
  DISPLAY 'AVERAGE SALES IS ', AVG-SALES

Pre-filled Tables
Tables can be pre-filled with information. Such pre-calculated tables come in very handy. Lookup tables save processing time by searching for an input x and finding the output y from the table, rather than do an expensive computation. Let us take an example. An online CICS program require date conversion from MM-DD-CCCYY to DD-MON-CCYY format, before display on the screen. We must transform 01, 02, 03,...,12 months to JAN,FEB,MAR,...,DEC. In order to achieve this, I create a one large WORKING STORAGE variable that maps month numeral to words. I then construct an array out of this variable by slicing it into twelve parts.
       01  WS-MONTHS-DATA.
  05 FILLER PIC X(05) VALUE '01JAN'.
  05 FILLER PIC X(05) VALUE '02FEB'.
  05 FILLER PIC X(05) VALUE '03MAR'.
  05 FILLER PIC X(05) VALUE '04APR'.
  05 FILLER PIC X(05) VALUE '05MAY'.
  05 FILLER PIC X(05) VALUE '06JUN'.
  05 FILLER PIC X(05) VALUE '07JUL'.
  05 FILLER PIC X(05) VALUE '08AUG'.
  05 FILLER PIC X(05) VALUE '09SEP'.
  05 FILLER PIC X(05) VALUE '10OCT'.
  05 FILLER PIC X(05) VALUE '11NOV'.
  05 FILLER PIC X(05) VALUE '12DEC'.
 
  01 WS-MONTH-MAP REDEFINES WS-MONTHS-DATA.
  05 WS-MONTH-ITEM OCCURS 12 TIMES.
  10 WS-MONTH-NUM PIC 9(02).
  10 WS-MONTH-NAME PIC X(03).
 
  01 WS-FROM-DATE-MM-DD-CCYYY.
  05 WS-FROM-DATE-MM PIC X(02).
  05 FILLER PIC X VALUE '-'.
  05 WS-FROM-DATE-DD PIC X(02).
  05 FILLER PIC X VALUE '-'.
  05 WS-FROM-DATE-CC PIC 9(02).
  05 WS-FROM-DATE-YY PIC 9(02).
 
  01 WS-TO-DATE-DD-MON-CCYY.
  05 WS-TO-DATE-DD PIC X(02).
  05 FILLER PIC X VALUE '-'.
  05 WS-TO-DATE-MON PIC X(03).
  05 FILLER PIC X VALUE '-'.
  05 WS-TO-DATE-CC PIC 9(02).
  05 WS-TO-DATE-YY PIC 9(02).
There are 12 occurrences of WS-MONTH-ITEM. A date conversion routine can input WS-MONTH-NUM to look-up the corresponding WS-MONTH-NAME in the table.
       8500-CONVERT-DATE-TO-DISP-FMT.
  MOVE WS-FROM-DATE-DD TO WS-TO-DATE-DD
  MOVE WS-FROM-DATE-CC TO WS-TO-DATE-CC
  MOVE WS-FROM-DATE-YY TO WS-TO-DATE-YY
  MOVE WS-FROM-DATE-MM TO WS-MONTH-NUM
  MOVE WS-MONTH-NAME(WS-MONTH-NUM) TO WS-TO-DATE-MON.
  8500-EXIT. EXIT.

A central store for static data
A well-designed program has the static data centralized at one place. I frequently store static data, constants like rates, prices, categories, options in COBOL arrays in WORKING STORAGE, rather than hard-coding at many places. Let's establish an example to compute the tax of a US citizen. Tax in the United States is computed as follows.
Tax BracketTax Rate
$0 – $8,92510%
$8,926 – $36,25015%
$36,251 – $87,85025%
$87,851 – $183,25028%
$183,251 – $398,35033%
$398,351 – $400,00035%
$400,001+39.6%
Suppose, a single tax-payer has $350,000 income. Tax amount of the first bracket is $8,925 x 10% = $892.50. Tax amount of the second bracket is ($36,250 - $8,925) x 15% = $4098. Tax amount of the third bracket is (87,850 - 36,250) x 25% = $12,900. Likewise, tax amount of the fourth and fifth brackets are $26,712 and $55027.50. With that background, let's establish a TAX-RATES-TABLE that includes static data such as tax rates, the floor and ceiling values for each tax bracket.
       01  TAX-RATE-DATA.
  05 FILLER PIC X(23) VALUE '00000000000089250001000'.
  05 FILLER PIC X(23) VALUE '00089260000362500001500'.
  05 FILLER PIC X(23) VALUE '00362510000878500002500'.
  05 FILLER PIC X(23) VALUE '00878510001832500002800'.
  05 FILLER PIC X(23) VALUE '01832510003983500003300'.
  05 FILLER PIC X(23) VALUE '03983510004000000003500'.
  05 FILLER PIC X(23) VALUE '04000010099999999903960'.
 
  01 TAX-RATES-TABLE REDEFINES TAX-RATE-DATA.
  05 TAX-RATE OCCURS 07 TIMES INDEXED BY IDX-A.
  10 TAX-SLAB-FLOOR PIC S9(07)V9(2).
  10 TAX-SLAB-CEIL PIC S9(07)V9(2).
  10 TAX-SLAB-RATE PIC S9(03)V9(2).

A generic sub-routine can be written to compute tax as follows. Its easy to see, that if the tax rates are revised, only the TAX-RATES-TABLE needs modification, the 7000-COMPUTE-TAX sub-routine remains as is.
       7000-COMPUTE-TAX.
  MOVE ZEROES TO TOTAL-TAX
  MARGINAL-TAX
  MOVE 'N' TO STOP-PROCESS-SW
 
  PERFORM VARYING TAX-RATE-IDX FROM 1 BY 1
  UNTIL IDX-A > 07 OR STOP-PROCESSING
  IF INCOME > TAX-SLAB-CEIL(IDX-A)
  COMPUTE MARGINAL-TAX =
  ((TAX-SLAB-CEIL(IDX-A)
  - TAX-SLAB-FLOOR(IDX-A)) * TAX-SLAB-RATE(IDX-A))/100
  ELSE
  COMPUTE MARGINAL-TAX =
  ((INCOME
  - TAX-SLAB-FLOOR(IDX-A)) * TAX-SLAB-RATE(IDX-A))/100
  SET STOP-PROCESSING TO TRUE
  END-IF
  COMPUTE TOTAL-TAX = TOTAL-TAX + MARGINAL-TAX
  END-PERFORM.
  7000-EXIT. EXIT.

To many people who are thrown to work at a mainframe computer on their first job, they feel lost. Mainframe people seem to speak a completely different language and that doesn't make life easy. What's more, the books and manuals are incredibly hard to comprehend.

"What on earth is a Mainframe?" is an absolute beginner's guide to mainframe computers. We'll introduce you to the hardware and peripherals. We'll talk about the operating system, the software installed on a mainframe. We'll also talk about the different people who work on a mainframe. In a nutshell, we'll de-mystify the mainframe.

Readers based in India, can buy the e-book for Rs. 50 only or the print book. International readers based in the US and other countries can click here to purchase the e-book.