Michael Coughlan
Page 31
updates. to accommodate this change in the specification two new record types will have to be added to the transaction file. these new transaction records are the AddToStock record indicated by a type code of 4 and the SubtractFromStock record indicated by a type code of 5.
the record descriptions for the MF and the new version of the tF are given here.
the stock MF is a sequential file ordered on ascending GadgetId. each record has the following description.
StockMaster Record
Field
Type
Length
Value
GadgetId
9
6
000001–999999
GadgetName
X
30
-
QtyInStock
9
4
0000–9999
239
Chapter 10 ■ proCessing sequential Files
Field
Type
Length
Value
Price
9
6
0000.00–9999.99
the tF is a sequential file ordered on ascending GadgetId. in each set of records with the same GadgetId the records are ordered in sequence in which the transactions occurred in the real world. records in the tF have the following descriptions:
Insertion record
Field
Type
Length
Value
TypeCode
9
1
1
GadgetId
9
6
000001–999999
GadgetName
X
30
-
QtyInStock
9
4
0000–9999
Price
9
6
0000.00–9999.99
Deletion record
Field
Type
Length
Value
TypeCode
9
1
2
GadgetId
9
6
000001–999999
PriceChange record
Field
Type Length
Value
TypeCode
9
1
3
GadgetId
9
6
000001–999999
Price
9
6
0000.00–9999.99
AddToStock record
Field
Type
Length
Value
TypeCode
9
1
4
GadgetId
9
6
000001–999999
QtyToAdd
9
4
0000–9999
SubtractFromStock record
Field
Type Length
Value
TypeCode
9
1
5
GadgetId
9
6
000001–999999
QtyToSubtract 9
4
0000–9999
240
Chapter 10 ■ proCessing sequential Files
TestData
To test your program you can use the test data shown below in Figure 10-17.
Figure 10-17. Test data including add and subtract from stock transactions
Notes
There is an additional error conditions to be noted. If the GadgetId-TF < GadgetId-MF and the type code is 4 or 5 then an error has occurred (no matching master file record) but it is also an error if the transaction is a SubtractFromStock record but the QtyInStock in the MF is less than the QtyToSubtract in the SubtractFromStock record
prOGraMMING eXerCISe - aNSWer
Listing 10-5. Update with added AddToStock and SubtractFromStock transactions
IDENTIFICATION DIVISION.
PROGRAM-ID. Listing10-5.
AUTHOR. Michael Coughlan
* File Update program based on the algorithm described by Barry Dwyer in
* "One more time - How to update a Master File"
* Applies the transactions ordered on ascending GadgetId-TF to the
* MasterStockFile ordered on ascending GadgetId-MF.
* Within each key value records are ordered on the sequence in which
241
Chapter 10 ■ proCessing sequential Files
* events occurred in the outside world.
* All valid, real world, transaction sequences are accommodated
* This version includes additions and subtractions from QtyInStock
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT MasterStockFile ASSIGN TO "Listing10-5Master.dat"
ORGANIZATION IS LINE SEQUENTIAL.
SELECT NewStockFile ASSIGN TO "Listing10-5NewMast.dat"
ORGANIZATION IS LINE SEQUENTIAL.
SELECT TransactionFile ASSIGN TO "Listing10-5Trans.dat"
ORGANIZATION IS LINE SEQUENTIAL.
DATA DIVISION.
FILE SECTION.
FD MasterStockFile.
01 MasterStockRec.
88 EndOfMasterFile VALUE HIGH-VALUES.
02 GadgetID-MF PIC 9(6).
02 GadgetName-MF PIC X(30).
02 QtyInStock-MF PIC 9(4).
02 Price-MF PIC 9(4)V99.
FD NewStockFile.
01 NewStockRec.
02 GadgetID-NSF PIC 9(6).
02 GadgetName-NSF PIC X(30).
02 QtyInStock-NSF PIC 9(4).
02 Price-NSF PIC 9(4)V99.
FD TransactionFile.
01 InsertionRec.
88 EndOfTransFile VALUE HIGH-VALUES.
02 TypeCode-TF PIC 9.
88 Insertion VALUE 1.
88 Deletion VALUE 2.
88 UpdatePrice VALUE 3.
88 StockAddition VALUE 4.
88 StockSubtraction VALUE 5.
02 RecordBody-IR.
03 GadgetID-TF PIC 9(6).
03 GadgetName-IR PIC X(30).
03 QtyInStock-IR PIC 9(4).
03 Price-IR PIC 9(4)V99.
01 DeletionRec.
02 FILLER PIC 9(7).
242
Chapter 10 ■ proCessing sequential Files
01 PriceChangeRec.
02 FILLER PIC 9(7).
02 Price-PCR PIC 9(4)V99.
01 AddToStock.
02 FILLER PIC 9(7).
02 QtyToAdd PIC 9(4).
01 SubtractFromStock.
02 FILLER PIC 9(7).
02 QtyToSubtract PIC 9(4).
WORKING-STORAGE SECTION.
01 ErrorMessage.
02 PrnGadgetId PIC 9(6).
02 FILLER PIC XXX VALUE " - ".
02 FILLER PIC X(46).
88 InsertError VALUE "Insert Error - Record already exists".
88 DeleteError VALUE "Delete Error - No such record in Master".
88 PriceUpdateError VALUE "Price Update Error - No such record in Master".
88 QtyAddError VALUE "Stock Add Error - No such record in Master".
88 QtySubtractError VALUE "Stock Subtract Error - No such record in Master".
88 InsufficientStock VALUE "Stock Subtract Error - Not enough stock".
01 FILLER PIC X VALUE "n".
88 RecordInMaster VALUE "y".
88 RecordNotInMaster VALUE "n".
01 CurrentKey PIC 9(6).
PROCEDURE DIVISION.
Begin.
OPEN INPUT MasterStockFile
OPEN INPUT TransactionFile
OPEN OUTPUT NewStockFile
PERFORM ReadMasterFile
PERFORM ReadTransFile
PERFORM ChooseNextKey
PERFORM UNTIL EndOfMasterFile AND EndOfTran
sFile
PERFORM SetInitialStatus
PERFORM ProcessOneTransaction
UNTIL GadgetID-TF NOT = CurrentKey
* CheckFinalStatus
IF RecordInMaster
WRITE NewStockRec
END-IF
PERFORM ChooseNextKey
END-PERFORM
CLOSE MasterStockFile, TransactionFile, NewStockFile
STOP RUN.
243
Chapter 10 ■ proCessing sequential Files
ChooseNextKey.
IF GadgetID-TF < GadgetID-MF
MOVE GadgetID-TF TO CurrentKey
ELSE
MOVE GadgetID-MF TO CurrentKey
END-IF.
SetInitialStatus.
IF GadgetID-MF = CurrentKey
MOVE MasterStockRec TO NewStockRec
SET RecordInMaster TO TRUE
PERFORM ReadMasterFile
ELSE SET RecordNotInMaster TO TRUE
END-IF.
ProcessOneTransaction.
* ApplyTransToMaster
EVALUATE TRUE
WHEN Insertion PERFORM ApplyInsertion
WHEN UpdatePrice PERFORM ApplyPriceChange
WHEN Deletion PERFORM ApplyDeletion
WHEN StockAddition PERFORM ApplyAddToStock
WHEN StockSubtraction PERFORM ApplySubtractFromStock
END-EVALUATE.
PERFORM ReadTransFile.
ApplyInsertion.
IF RecordInMaster
SET InsertError TO TRUE
DISPLAY ErrorMessage
ELSE
SET RecordInMaster TO TRUE
MOVE RecordBody-IR TO NewStockRec
END-IF.
ApplyDeletion.
IF RecordNotInMaster
SET DeleteError TO TRUE
DISPLAY ErrorMessage
ELSE SET RecordNotInMaster TO TRUE
END-IF.
ApplyPriceChange.
IF RecordNotInMaster
SET PriceUpdateError TO TRUE
DISPLAY ErrorMessage
ELSE
MOVE Price-PCR TO Price-NSF
END-IF.
244
Chapter 10 ■ proCessing sequential Files
ApplyAddToStock.
IF RecordNotInMaster
SET QtyAddError TO TRUE
DISPLAY ErrorMessage
ELSE
ADD QtyToAdd TO QtyInStock-NSF
END-IF.
ApplySubtractFromStock.
IF RecordNotInMaster
SET QtySubtractError TO TRUE
DISPLAY ErrorMessage
ELSE
IF QtyInStock-NSF < QtyToSubtract
SET InsufficientStock TO TRUE
DISPLAY ErrorMessage
ELSE
SUBTRACT QtyToSubtract FROM QtyInStock-NSF
END-IF
END-IF.
ReadTransFile.
READ TransactionFile
AT END SET EndOfTransFile TO TRUE
END-READ
MOVE GadgetID-TF TO PrnGadgetId.
ReadMasterFile.
READ MasterStockFile
AT END SET EndOfMasterFile TO TRUE
END-READ.
245
Chapter 11
Creating Tabular Data
This chapter and the next return to the DATA DIVISION to explore more data-declaration concepts. In this chapter, I discuss how to create and manipulate tabular data. I compare and contrast COBOL tables with the arrays used in many other programming languages. Chapter 12 covers more advanced data declaration using the USAGE, REDEFINES, and RENAMES clauses.
The chapter starts with a discussion of the similarities and differences between arrays and tables. You then see how COBOL tables are declared using the OCCURS clause and manipulated using subscripts. I introduce a
scenario to explain why tabular data is required and end the scenario with an example program that uses a simple one-dimensional table as part of the solution.
The middle section of the chapter introduces the concept of group items as table elements and demonstrates this in an example program. Multidimensional tables are then introduced. You learn the best way to depict a multidimensional COBOL table graphically; and I again address the contrast between arrays and tables, which is more pronounced with multidimensional tables. I present an example program using a two-dimensional table as part of its solution and introduce a scenario requiring a three-dimensional table.
In the chapter’s final section, I show how to create prefilled tables using the REDEFINES clause. You see this demonstrated in an example program that uses a table prefilled with the names of the American states. I also discuss some of the table declaration changes introduced with the ANS 85 standard.
Tables vs. Arrays
Most programming languages have a facility to create tabular information. Tabular information consists of multiple occurrences of a homogeneous data item.
Most programming languages use the term array to describe these multiple-occurrence data items, but COBOL
uses the term table. This is not just a difference of nomenclature. In most languages (including Basic, Pascal, Java, FORTRAN, and Ada), arrays look and work similarly; but COBOL tables, although they have some similarities to arrays, have a number of minor and major differences.
Table/Array Definition
Tables and arrays are so similar that you can use the same definition for them. A table/array may be defined as a contiguous sequence of memory locations that all have the same name and that are uniquely identified by that name and by their position in the sequence. The position index is called a subscript, and the individual components of the table/array are referred to as elements.
247
Chapter 11 ■ Creating tabular Data
Table/Array Differences
If the same definition can be used for tables and arrays, what is the difference between them? The first difference affects the C language derivatives (C++, Java, and C#). In these languages, arrays start at element 0 and go to the maximum size of the array minus one. This arrangement is a rich source of programming errors for beginner
programmers who have difficulty coming to grips with this displaced referencing: for instance, element[9] is the tenth element in the array. In COBOL, tables start at element 1 (not 0) and go to the maximum size of the table. In a COBOL
table, element(9) is the ninth element of the table.
A major difference between COBOL tables and arrays is that COBOL tables are declared using record
descriptions. The nature of a record description is that there is a hierarchical relationship between the items in the record. Consequently, one item in a multidimensional table must always be subordinate to another. Arrays have no such hierarchical relationship. An array is simply a matrix of cells that are referenced using row and column subscripts. The hierarchical structuring of COBOL tables allows data-manipulation opportunities that are not available to languages that use arrays.
Declaring Tables
Tables are declared using an extension to the PICTURE clause, called the OCCURS clause. The metalanguage for the basic OCCURS clause is as follows:
OCCURS TableSize#1 TIMES
To declare a table, you define the type and size of the table element, and then you use the OCCURS clause to specify how many times the element occurs. In the following NFL-Stadium example, the type and size of the element are defined by its subordinate data items. Each element is alphanumeric and 35 characters (30 + 5) in size:
01 SoccerStadiumName PIC X(25) OCCURS 20 TIMES.
01 NFL-Stadium OCCURS 31 TIMES.
02 NFL-StadiumName PIC X(30).
02 NFL-StadiumCapacity PIC 9(5).
OCCURS Clause Rules
Here are the rules for the OCCURS clause:
• Any data item whose description includes an OCCURS clause must be subscripted when
referred to. For example:
DISPLAY SoccerStadiumName(15)
MOVE NFL-Stadium(12) TO
NFL-Stadium(7)
• Any data item that is subordinate to a group item whose description contains an OCCURS clause
must be subscripted when referred to. For example:
DISPLAY NFL-StadiumName(7)
DISPLAY NFL-StadiumCapacity(7)
248
Chapter 11 ■ Creating tabular Data
Subscript Rules
Now let’s look at the subscript rules:
• A subscript is a bracketed numeric index (or something that evaluates to one) that points to a
particular element (or part of an element) of the table. The subscript immediately follows the
element name.
• The numeric index must be a positive integer, a data name that represents one, or a simple
expression that evaluates to one.
• The numeric index is a value between one and the number of elements in the table, inclusive.
• When more than one subscript is used, they must be separated from one another by commas.
• One subscript must be specified for each dimension of the table. There must be one subscript
for a one-dimensional table, two subscripts for a two-dimensional table, and three for a
three-dimensional table.
• The first subscript applies to the first OCCURS clause, the second applies to the second OCCURS
clause, and so on.
• Subscripts must be enclosed in rounded brackets: ().
Here are some examples:
MOVE ZEROS TO StateSalesTotal(35)
ADD BranchSales TO StateSalesTotal(StateNum)
ADD BranchSales TO StateSalesTotal(StateNum + 1)
ADD BranchSales TO StateSalesTotal(StateNum - 2)
ADD MonthlyBranchSales TO StateSalesTotal(StateNum, MonthNum)
DISPLAY "Stadium Name is " StadiumName(24)
DISPLAY "Stadium Capacity is " StadiumCapacity(24)
Why Use Tabular Data?
Let’s start this introduction to tabular data by setting up a hypothetical problem. In the course of exploring the problem and a number of variations, I will show how tables are defined and used in COBOL.
First Specification
YoreCandyShoppe is a franchise that sells old-time candy at branches all over the United States. A program is required that will sum the candy sales for all the YoreCandyShoppe branches in the country. The sales data is obtained from a sales file containing the candy sales for each branch. The sales file is a sequential file sequenced on ascending BranchId. Each record of the file may be described using the following record description: