01 BranchSalesRec.
88 EndOfSalesFile VALUE HIGH-VALUES.
02 BranchId PIC 9(7).
02 StateNum PIC 99.
02 CandySales PIC 9(7)V99.
To save file space, a two-digit numeric value is used to represent the state instead of a state name.
249
Chapter 11 ■ Creating tabular Data
The program to perform this task is very simple. All you have to do is set up a variable to hold the total candy sales and then add CandySales from each record to TotalCandySales. A fragment of the program required to do this is given in Example 11-1.
Example 11-1. PROCEDURE DIVISION of a Program to Sum Total Candy Sales
PROCEDURE DIVISION.
Begin.
OPEN INPUT SalesFile
READ SalesFile
AT END SET EndOfSalesFile TO TRUE
END-READ
PERFORM UNTIL EndOfSalesFile
ADD CandySales TO TotalCandySales
READ SalesFile
AT END SET EndOfSalesFile TO TRUE
END-READ
END-PERFORM.
DISPLAY "Total candy sales for the US : ", TotalCandySales
CLOSE SalesFile
STOP RUN.
Second Specification
The program to solve the problem set in the first specification is simple. But suppose the specification is changed so that instead of being asked for the country’s total candy sales, you are asked to calculate the total sales for each state.
One approach to this new problem would be to sort the file on StateNum. This would turn the requirement into a simple control-break problem (that is, process all the records for one state, output the result, and then go on to the next). But the issue with this solution is that sorting is a comparatively slow, disk-intensive procedure. You want to avoid having to adopt this solution if possible. Is there any other way to solve the problem?
You could create 50 variables (one for each state) to hold the sales totals. Then, in the program, you could use an EVALUATE statement to add CandySales to the appropriate total. For example:
EVALUATE StateNum
WHEN 1 ADD CandySales TO State1SalesTotal
WHEN 2 ADD CandySales TO State2SalesTotal
WHEN 3 ADD CandySales TO State3SalesTotal
..... 47 more WHEN branches
END-EVALUATE
This solution is not very satisfactory. You need a specific WHEN branch to process each state, and you have to declare 50 data items to hold the sales totals. And when you want to display the results, you must use 50 DISPLAY
statements:
DISPLAY "State 1 total is ", State1SalesTotal
DISPLAY "State 2 total is ", State2SalesTotal
DISPLAY "State 3 total is ", State3SalesTotal
..... 47 more DISPLAY statements
250
Chapter 11 ■ Creating tabular Data
But this poor attempt at a solution does contain the germ of an idea of how to solve the problem. It is interesting to note that the processing of each WHEN branch is the same: CandySales is added to the sales total for a particular state. You could replace all 50 WHEN branches with one statement if you could generalize to something like this: ADD the CandySales to the StateSalesTotal location indicated by the StateNum.
There is also something interesting about the 50 data items. They all have exactly the same PICTURE, and
they all have, more or less, the same name: StateSalesTotal. The only way you can distinguish between one
StateSalesTotal and another is by attaching a number to the name: State1SalesTotal, State2SalesTotal,
State3SalesTotal, and so on.
When you see a group of data items that all have the same name and the same description and are only
distinguished from one another by a number attached to the name, you know that you have a problem crying out for a table-based solution.
Using a Table for the State Sales Totals
In COBOL, you declare a table by specifying the type (or structure) of a single item (element) of the table and then specifying that the data item is to be repeated a given number of times. For instance, StateSalesTable may be defined as follows:
01 StateSalesTable.
02 StateSalesTotal PIC 9(8)V99 OCCURS 50 TIMES.
StateSalesTable can be represented diagrammatically as shown in Figure 11-1. All the elements of the table have the name StateSalesTotal; you can refer to a specific one by using that name followed by an integer value in brackets. So, StateSalesTotal(3) refers to the third element of the table, and StateSalesTotal(13) refers to the thirteenth element.
Figure 11-1. Diagrammatic representation of StateSalesTable
But when you refer to an element, you don’t have to use a numeric literal. You can use anything that evaluates to a numeric value between 1 and the size of the table—even a simple arithmetic expression.
So the solution to the problem of summing the candy sales for each state is to use a table to hold a
StateSalesTotal for each state and to use StateNum to access the correct element in the table.
Once you realize that you can use a table to hold the sales totals and StateNum as an index into the table, the solution to the problem becomes very simple. A program to read the sales file, sum the sales, and display the results is given in Listing 11-1. In this example, to keep the program simple and focus on table creation and handling, I chose to display the results rather than write them to a print file.
251
Chapter 11 ■ Creating tabular Data
Listing 11-1. Summing Candy Sales for Each State
IDENTIFICATION DIVISION.
PROGRAM-ID. Listing11-1.
AUTHOR. Michael Coughlan
* Program to sum the CandySales for each branch of YoreCandyShoppe
* and display the results in StateNum order
* Using as input the Sequential BranchSalesFile ordered on ascending BranchId
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT BranchSalesFile ASSIGN TO "Listing11-1BranchSales.dat"
ORGANIZATION IS LINE SEQUENTIAL.
DATA DIVISION.
FILE SECTION.
FD BranchSalesFile.
01 BranchSalesRec.
88 EndOfSalesFile VALUE HIGH-VALUES.
02 BranchId PIC 9(7).
02 StateNum PIC 99.
02 CandySales PIC 9(7)V99.
WORKING-STORAGE SECTION.
01 StateSalesTable.
02 StateSalesTotal PIC 9(8)V99 OCCURS 50 TIMES.
01 StateIdx PIC 99.
01 PrnStateSales PIC $$$,$$$,$$9.99.
PROCEDURE DIVISION.
Begin.
MOVE ZEROS TO StateSalesTable
OPEN INPUT BranchSalesFile
READ BranchSalesFile
AT END SET EndOfSalesFile TO TRUE
END-READ
PERFORM UNTIL EndOfSalesFile
ADD CandySales TO StateSalesTotal(StateNum)
READ BranchSalesFile
AT END SET EndOfSalesFile TO TRUE
END-READ
END-PERFORM
DISPLAY " YoreCandyShoppe Sales by State"
DISPLAY " ------------------------------"
PERFORM VARYING StateIdx FROM 1 BY 1
UNTIL StateIdx GREATER THAN 50
MOVE StateSalesTotal(StateIdx) TO PrnStateSales
252
Chapter 11 ■ Creating tabular Data
DISPLAY "State ", StateIdx
" sales total is " PrnStateSales
END-PERFORM
CLOSE BranchSalesFile
STOP RUN.
Third Specification: Group Items as Table Elements
The elements of a table do not have to be elementary items. An element can be a group item. In other words, each element can be subdivided into two or more subordinate items.
Suppose the specification of the YoreCandyShoppe sales-report program chan
ges so that in addition to
summing the candy sales for each state, the program should count the number of branches and compute the average sales for the state. Final country totals should also be produced, showing Total-US-Sales, US-BranchCount, and Average-US-Sales.
One solution to this problem would be to set up two separate tables: one to hold state sales and another to hold the count of the number of branches in the state (see Example 11-2).
Example 11-2. The Two-Table Solution
01 StateSalesTable.
02 StateSalesTotal PIC 9(8)V99 OCCURS 50 TIMES.
01 StateBranchesTable.
02 StateBranchCount PIC 9(5) OCCURS 50 TIMES.
Then all that would be required to calculate the average sales for the state would be a statement such as
COMPUTE AverageStateSales = StateSalesTotal(StateNum) / StateBranchCount(StateNum)
This is probably the way you would solve the problem in most languages. But in COBOL you can also set
up a single table in which each element is defined as a group item that consists of the StateSalesTotal and the StateBranchCount (see Example 11-3).
Example 11-3. Solution Using the Group Item as a Table Element
01 StateSalesTable.
02 StateTotals OCCURS 50 TIMES.
03 StateSalesTotal PIC 9(8)V99.
03 StateBranchCount PIC 9(5).
To calculate the average sales, you can use the same COMPUTE statement as before:
COMPUTE AverageStateSales = StateSalesTotal(StateNum) / StateBranchCount(StateNum)
A diagrammatic representation of this table description is shown in Figure 11-2. Each element of the table now consists of two parts: StateSalesTotal and StateBranchCount. These are subordinate to the StateTotals element.
Data-manipulation opportunities abound. All these data names allow you to manipulate the data in the table at different levels of granularity. You can use the following commands:
• MOVE ZEROS TO StateSalesTable: See Figure 11-2. Fills the whole table with zeros.
• MOVE StateTotals(2) TO StateTotals(5): See Figure 11-2. Copies the contents of one
element, including both subordinate items, to another element.
253
Chapter 11 ■ Creating tabular Data
• DISPLAY StateBranchCount(3): Displays the contents of the StateBranchCount part of
element 3.
• ADD CandySales TO StateSalesTotal(3): Adds CandySales to the contents of the
StateSalesTotal part of element 3.
Figure 11-2. Table elements as group items. Element 3 is exploded to show details
Tabular Data Program
Listing 11-2 is a solution to the problem posed by the changed specification. It uses the table defined in Example 11-3.
Listing 11-2. Table Elements as Group Items
IDENTIFICATION DIVISION.
PROGRAM-ID. Listing11-2.
AUTHOR. Michael Coughlan
* Program that for each state and for the whole US
* sums the CandySales for each branch of YoreCandyShoppe
* counts the number of branches
* calculates the average sales per state and displays the results in StateNum order
* Uses as input the Sequential BranchSalesFile ordered on ascending BranchId
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT BranchSalesFile ASSIGN TO "Listing11-2BranchSales.dat"
ORGANIZATION IS LINE SEQUENTIAL.
DATA DIVISION.
FILE SECTION.
FD BranchSalesFile.
01 BranchSalesRec.
88 EndOfSalesFile VALUE HIGH-VALUES.
02 BranchId PIC 9(7).
02 StateNum PIC 99.
02 CandySales PIC 9(7)V99.
254
Chapter 11 ■ Creating tabular Data
WORKING-STORAGE SECTION.
01 StateSalesTable.
02 StateTotals OCCURS 50 TIMES.
03 StateSalesTotal PIC 9(8)V99.
03 StateBranchCount PIC 9(5).
01 StateIdx PIC 99.
01 ReportHeading1 PIC X(35)
VALUE " YoreCandyShoppe Sales by State".
01 ReportHeading2 PIC X(35)
VALUE " ------------------------------".
01 ReportHeading3 PIC X(47)
VALUE "State Branches StateSales AverageSales".
01 DetailLine.
02 PrnStateNum PIC BZ9.
02 PrnBranchCount PIC B(3)ZZ,ZZ9.
02 PrnStateSales PIC B(5)$$$,$$$,$$9.99.
02 PrnAveageSales PIC BB$$$,$$$,$$9.99.
01 US-Totals.
02 US-TotalSales PIC 9(9)V99.
02 US-BranchCount PIC 9(6).
02 PrnUS-TotalSales PIC $,$$$,$$$,$$9.99.
02 PrnUS-BranchCount PIC B(9)ZZZ,ZZ9.
02 PrnUS-AverageSales PIC BBBB$$$,$$$,$$9.99.
PROCEDURE DIVISION.
Begin.
MOVE ZEROS TO StateSalesTable
OPEN INPUT BranchSalesFile
READ BranchSalesFile
AT END SET EndOfSalesFile TO TRUE
END-READ
PERFORM UNTIL EndOfSalesFile
ADD CandySales TO StateSalesTotal(StateNum), US-TotalSales
ADD 1 TO StateBranchCount(StateNum), US-BranchCount
READ BranchSalesFile
AT END SET EndOfSalesFile TO TRUE
END-READ
END-PERFORM
PERFORM PrintResults
CLOSE BranchSalesFile
STOP RUN.
PrintResults.
DISPLAY ReportHeading1
DISPLAY ReportHeading2
255
Chapter 11 ■ Creating tabular Data
DISPLAY ReportHeading3
PERFORM VARYING StateIdx FROM 1 BY 1
UNTIL StateIdx GREATER THAN 50
MOVE StateIdx TO PrnStateNum
MOVE StateSalesTotal(StateIdx) TO PrnStateSales
MOVE StateBranchCount(StateIdx) TO PrnBranchCount
COMPUTE PrnAveageSales = StateSalesTotal(StateIdx) / StateBranchCount(StateIdx)
DISPLAY DetailLine
END-PERFORM
MOVE US-TotalSales TO PrnUS-TotalSales
MOVE US-BranchCount TO PrnUS-BranchCount
COMPUTE PrnUS-AverageSales = US-TotalSales / US-BranchCount
DISPLAY "YoreCandyShoppe branches in the US = " PrnUS-BranchCount
DISPLAY "YoreCandyShoppe sales in the US = " PrnUS-TotalSales
DISPLAY "YoreCandyShoppe average US sales = " PrnAveageSales.
Multidimensional Tables
Listing 11-2 uses a table in which each element is a group item that consists of the StateSalesTotal and the StateBranchCount. But the table is still a single-dimensional table. Sometimes the solution to a problem demands a multidimensional table approach. A multidimensional table is one in which each element of the table is itself a table.
This section considers multidimensional tables in the context of a specification change for the YoreCandyShoppe sales report.
Suppose each YoreCandyShoppe branch is asked to provide more granular sales data. Instead of reporting sales for the entire year, each branch must now report sales for each month. To do this, the sales record for each branch must be changed to accommodate a 12-element table of sales data. The new record description is given in Example 11-4.
Example 11-4. New Record Description That Records Candy Sales for Each Month
01 BranchSalesRec.
88 EndOfSalesFile VALUE HIGH-VALUES.
02 BranchId PIC 9(7).
02 StateNum PIC 99.
02 SalesForMonth PIC 9(5)V99 OCCURS 12 TIMES.
The report produced from the sales file must reflect this more granular data and is now required to show the following:
• Total sales for each state
• The count of the number of branches in the state
• Average sales per branch for each state
<
br /> • Sales per month for each state
• Final country totals showing Total-US-Sales, US-BranchCount, and Average-US-Sales
In the program that implemented the previous specification, the sales for each state and the number of branches in each state were recorded in a 50-element table. In this version, instead of the total sales for the year, you have to record the sales per month. To do that, you need a two-dimensional table as described in Example 11-5.
256
Chapter 11 ■ Creating tabular Data
Example 11-5. Two-dimensional Table to Record Sales per Month and the Number of Branches in the State 01 StateSalesTable.
02 State OCCURS 50 TIMES.
03 StateBranchCount PIC 9(5).
03 StateMonthSales PIC 9(5)V99 OCCURS 12 TIMES.
COBOL DetaIL
if you wanted to manipulate the table at a further level of granularity, you could describe the table as
01 StateSalesTable.
02 State OCCURS 50 TIMES.
03 StateBranchCount PIC 9(5).
03 StateSales.
04 StateMonthSales PIC 9(5)V99 OCCURS 12 TIMES.
The table description in Example 11-5 highlights a difference between COBOL tables and arrays. In other
languages, two arrays would be required to record this information: a two-dimensional table to record the state sales per month and a one-dimensional table to record the number of branches per state. You can also record the data using two tables in COBOL, as shown in Example 11-6; but COBOL’s hierarchical structuring allows you to combine both tables so that each element of the first dimension consists of the BranchCount and a 12-element table containing the sales for each month.
Example 11-6. A Two-Table Solution
01 StateSalesTable.
02 State OCCURS 50 TIMES.
03 StateMonthSales PIC 9(5)V99 OCCURS 12 TIMES.
01 StateBranchesTable.
02 State OCCURS 50 TIMES.
Michael Coughlan Page 32