Michael Coughlan
Page 28
01 DetailLine.
02 PrnStateName PIC X(14).
88 SuppressStateName VALUE SPACES.
02 PrnBranchId PIC BBX(5).
88 SuppressBranchId VALUE SPACES.
02 PrnSalespersonId PIC BBBBX(6).
02 PrnSalespersonTotal PIC BB$$,$$9.99.
211
Chapter 10 ■ proCessing sequential Files
01 BranchTotalLine.
02 FILLER PIC X(43)
VALUE " Branch Total: ".
02 PrnBranchTotal PIC $$$,$$9.99.
01 StateTotalLine.
02 FILLER PIC X(40)
VALUE " State Total : ".
02 PrnStateTotal PIC $$,$$$,$$9.99.
01 FinalTotalLine.
02 FILLER PIC X(39)
VALUE " Final Total :".
02 PrnFinalTotal PIC $$$,$$$,$$9.99.
01 SalespersonTotal PIC 9(4)V99.
01 BranchTotal PIC 9(6)V99.
01 StateTotal PIC 9(7)V99.
01 FinalTotal PIC 9(9)V99.
01 PrevStateName PIC X(14).
01 PrevBranchId PIC X(5).
01 PrevSalespersonId PIC X(6).
PROCEDURE DIVISION.
Begin.
OPEN INPUT SalesFile
OPEN OUTPUT SalesReport
WRITE PrintLine FROM ReportHeading AFTER ADVANCING 1 LINE
WRITE PrintLine FROM SubjectHeading AFTER ADVANCING 1 LINE
READ SalesFile
AT END SET EndOfSalesFile TO TRUE
END-READ
PERFORM UNTIL EndOfSalesFile
MOVE StateName TO PrevStateName, PrnStateName
MOVE ZEROS TO StateTotal
PERFORM SumSalesForState
UNTIL StateName NOT = PrevStateName
OR EndOfSalesFile
MOVE StateTotal TO PrnStateTotal
WRITE PrintLine FROM StateTotalLine AFTER ADVANCING 1 LINE
END-PERFORM
MOVE FinalTotal TO PrnFinalTotal
WRITE PrintLine FROM FinalTotalLine AFTER ADVANCING 1 LINE
CLOSE SalesFile, SalesReport
STOP RUN.
212
Chapter 10 ■ proCessing sequential Files
SumSalesForState.
WRITE PrintLine FROM SPACES AFTER ADVANCING 1 LINE
MOVE BranchId TO PrevBranchId, PrnBranchId
MOVE ZEROS TO BranchTotal
PERFORM SumSalesForBranch
UNTIL BranchId NOT = PrevBranchId
OR StateName NOT = PrevStateName
OR EndOfSalesFile
MOVE BranchTotal TO PrnBranchTotal
WRITE PrintLine FROM BranchTotalLine AFTER ADVANCING 1 LINE.
SumSalesForBranch.
MOVE SalespersonId TO PrevSalespersonId, PrnSalespersonId
MOVE ZEROS TO SalespersonTotal
PERFORM SumSalespersonSales
UNTIL SalespersonId NOT = PrevSalespersonId
OR BranchId NOT = PrevBranchId
OR StateName NOT = PrevStateName
OR EndOfSalesFile
MOVE SalespersonTotal TO PrnSalespersonTotal
WRITE PrintLine FROM DetailLine AFTER ADVANCING 1 LINE
SET SuppressBranchId TO TRUE
SET SuppressStateName TO TRUE.
SumSalespersonSales.
ADD ValueOfSale TO SalespersonTotal, BranchTotal, StateTotal, FinalTotal
READ SalesFile
AT END SET EndOfSalesFile TO TRUE
END-READ.
Program Notes
The program in Listing 10-1 is fairly straightforward, once you understand that its structure mirrors the structure of the data and the report. It is interesting to contrast this program with a similar program given on the web site The American Programmer2. That program uses the single loop and IF statement approach mentioned earlier. One
objection to this approach is that the three control items are tested for every record in the file.
I draw your attention to the way in which the StateName and BranchId are suppressed after their first
occurrence in Listing 10-1. This is done to make the report look less cluttered. To implement the suppression, the condition-name technique that you have seen in a number of other example programs is used. I could have implemented the suppression using a statement such as MOVE SPACES TO PrnStateName, but it would not have been obvious why the data item was being filled with spaces. The purpose of the statement SET SuppressStateName TO
TRUE is easier to understand.
Test Data and Results
Due to space constraints, Figure 10-3 shows only a portion of the test data file and the report produced from that data is shown.
2The Three Level Subtotal (Control Break) COBOL Program, TheAmericanProgrammer.Com, http://theamericanprogrammer.com/
programming/08-brklv3.shtml.
213
Chapter 10 ■ proCessing sequential Files
Figure 10-3. Fragment of the report produced and part of the test data file
An Atypical Control Break
The program in Listing 10-1 is a typical control-break program, but control-break problems come in a variety of shapes and sizes. For instance, you have probably realized by now that Exercise 1 at the end of the last chapter is a control-break problem but not a typical one. I didn’t provide a solution at the time because I wanted you discover for yourself some of the difficulties with this kind of problem and how easy it is to get dragged into a convoluted solution.
Before I present my solution, let’s look at the specification again.
Specification
The Genealogists Society of Ireland wishes to discover the most popular surname used in each of the 26 counties in the Irish Republic. In order to obtain this information, the society has acquired a file containing a subset of data from the most recent census.
A program is required that will process the census file and produce a report that shows, for each county, the most popular surname and the number of times it occurs.
214
Chapter 10 ■ proCessing sequential Files
The census file is a standard sequential file with fixed-length fields. Each record contains a census number, a surname, and a county name. The file has been sorted and is now ordered on ascending Surname in ascending CountyName. Each record in the file has the following description:
Field
Type
Length Value
CensusNumber
9
8
00000001–99999999
Surname
X
20
-
CountyName
X
9
-
The report should take the format shown in the following report template. The Count field is a count of the number of times the Surname occurs in the county. In the Count field, thousands should be separated using a comma, and the field should be zero-suppressed up to, but not including, the last digit:
Popular Surname Report
CountyName Surname Count
Carlow XXXXXXXXXXXXXXXXXXXX XXX,XXX
Cavan XXXXXXXXXXXXXXXXXXXX XXX,XXX
Clare XXXXXXXXXXXXXXXXXXXX XXX,XXX
:: :: :: :: :: :: :: :: :: :: ::
Westmeath XXXXXXXXXXXXXXXXXXXX XXX,XXX
Wicklow XXXXXXXXXXXXXXXXXXXX XXX,XXX
Wexford XXXXXXXXXXXXXXXXXXXX XXX,XXX
************* end of report ***************
Atypical Control-Break Program
This is not a typical control-break program (see Listing 10-2). Instead of printing the total number of occurrences of the surname when there is a change of surname (as a classic control-break program would do), there is a check to see if this surname is the most popular. A line is printed only when the major control item (the county name) changes.
When that happens, the county name and the most popular surname are printed. There is a trap here for the unwary: when the control break occurs, it is too late to move the county name to the print line, because
at this point the county name in the buffer is the next county. The solution is to move PrevCountyName to the print line or to, as is done in this program, prime the print line with the correct county name before entering the loop that processes all the surnames in that county.
Listing 10-2. Two-Level Control-Break Program Showing the Most Popular Surnames in the Counties of Ireland IDENTIFICATION DIVISION.
PROGRAM-ID. Listing10-2.
AUTHOR. Michael Coughlan.
* Control Break program to process the Census file and produce
* a report that shows, for each county, the most popular surname
* and the number of times it occurs.
* The Records in the sequential Census file are ordered on
* ascending Surname within ascending CountyName.
* The report must be printed in ascending CountyName order
215
Chapter 10 ■ proCessing sequential Files
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT CensusFile ASSIGN TO "Listing10-2TestData.Dat"
ORGANIZATION IS LINE SEQUENTIAL.
SELECT SurnameReport ASSIGN TO "Listing10-2.RPT"
ORGANIZATION IS LINE SEQUENTIAL.
DATA DIVISION.
FILE SECTION.
FD CensusFile.
01 CensusRec.
88 EndOfCensusFile VALUE HIGH-VALUES.
02 CensusNum PIC 9(8).
02 Surname PIC X(20).
02 CountyName PIC X(9).
FD SurnameReport.
01 PrintLine PIC X(45).
WORKING-STORAGE SECTION.
01 ReportHeading.
02 FILLER PIC X(13) VALUE SPACES.
02 FILLER PIC X(22)
VALUE "Popular Surname Report".
01 SubjectHeading.
02 FILLER PIC X(42)
VALUE "CountyName Surname Count".
01 CountySurnameLine.
02 PrnCountyName PIC X(9).
02 FILLER PIC X(3) VALUE SPACES.
02 PrnSurname PIC X(20).
02 PrnCount PIC BBBZZZ,ZZ9.
01 ReportFooter PIC X(43)
VALUE "************* end of report ***************".
01 PrevCountyName PIC X(9).
01 PrevSurname PIC X(20).
01 MostPopularSurname PIC X(20).
01 MostPopularCount PIC 9(6).
01 SurnameCount PIC 9(6).
PROCEDURE DIVISION.
Begin.
OPEN INPUT CensusFile
OPEN OUTPUT SurnameReport
WRITE PrintLine FROM ReportHeading AFTER ADVANCING 1 LINE
WRITE PrintLine FROM SubjectHeading AFTER ADVANCING 1 LINE
216
Chapter 10 ■ proCessing sequential Files
READ CensusFile
AT END SET EndOfCensusFile TO TRUE
END-READ
PERFORM UNTIL EndOfCensusFile
MOVE CountyName TO PrevCountyName, PrnCountyName
MOVE ZEROS TO MostPopularCount
MOVE SPACES TO MostPopularSurname
PERFORM FindMostPopularSurname
UNTIL CountyName NOT EQUAL TO PrevCountyName
OR EndOfCensusFile
MOVE MostPopularCount TO PrnCount
MOVE MostPopularSurname TO PrnSurname
WRITE PrintLine FROM CountySurnameLine AFTER ADVANCING 1 LINE
END-PERFORM
WRITE PrintLine FROM ReportFooter AFTER ADVANCING 2 LINES
CLOSE CensusFile, SurnameReport
STOP RUN.
FindMostPopularSurname.
MOVE Surname TO PrevSurname
PERFORM CountSurnameOccurs VARYING SurnameCount FROM 0 BY 1
UNTIL Surname NOT EQUAL TO PrevSurname
OR CountyName NOT EQUAL TO PrevCountyName
OR EndOfCensusFile
IF SurnameCount > MostPopularCount
MOVE SurnameCount TO MostPopularCount
MOVE PrevSurname TO MostPopularSurname
END-IF.
CountSurnameOccurs.
READ CensusFile
AT END SET EndOfCensusFile TO TRUE
END-READ.
Program Notes
The census file is ordered on ascending Surname in ascending CountyName, and that is the same order required for the printed report. The control items are CountyName and Surname. The data items PrevSurname and PrevCountyName are used to detect the control breaks. Similar to Listing 10-1, the structure of this program echoes the structure of the input file and the output report.
Test Data and Results
Figure 10-4 shows the report produced by the program and a small portion of the test data file used.
217
Chapter 10 ■ proCessing sequential Files
Figure 10-4. The report produced by the program, and part of the test data file
Updating Sequential Files
It is easy to add records to an unordered sequential file because you can simply add them to the end of the file by opening the file for EXTEND. For instance:
OPEN EXTEND UnorderedFile
WRITE UnorderedRec
When a file is OPENed for EXTEND, the Next Record Pointer is positioned at the end of the file. When records are written to the file, they are appended to the end.
Although you can add records to an unordered sequential file, the records in a sequential file cannot be deleted or updated in situ. The only way to delete records from a sequential file is to create a new file, which does not contain them; and the only way to update records is to create a new file that contains the updated records. A record update involves changing the value of one or more of its fields. For instance, you might change the value of the CustomerAddress or CustomerPhoneNumber field of a customer record, or you might change the value of the QtyInStock or ReorderLevel field of a stock record.
■ COBOL Detail although, in standard CoBol, sequential files cannot be deleted or updated in situ many vendors, including Micro Focus, allow this for disk-based files.
218
Chapter 10 ■ proCessing sequential Files
Because updating or deleting records in a sequential file requires you to read all the records in the file and to create a new file that has the changes applied to it, it is computationally too expensive to apply these operations to the file one at a time. Updates to sequential files are normally done in batch mode. That is, all the updates are gathered together into what is often referred to as the transaction file and then applied to the target file in one go or batch. The target file is often referred to as the master file.
As you have seen, you can add records to an unordered sequential file by opening the file for EXTEND and writing the records to the end of it. But if you want to update or delete records, you must have a way of identifying the record you want to update or delete. A key field is normally used to achieve this. A key field is a field in the record whose value can be used to identify that record. For instance, in a stock record, the StockNumber might be used as the key field.
When you apply transaction records to a master file, you compare the key field in the transaction record with that in the master file record. If there is a match, you can apply the delete or update to that master file record. This key-comparison operation is called record matching.
For record matching to work correctly, the transaction file and the master file must be ordered on the same key value. Record matching does not work if either file is unordered or if the files are ordered on different key fields. If you need convincing of this, PERFORM (that is, go there, do the exercise, and then come back) the Language Knowledge Exercise at the end of the chapter. That exercise will help you understand the problems of trying to apply batched transactions to an unordered master file.
Applying Transactions to an Ordered Sequential File
You start this section by looking at programming templates that show how to apply each type of transaction (insertion, deletion, and update) to an ordered sequential file. To c
omplicate matters, most transaction files consist of a mixture of transaction types. Therefore, this section considers the data-declaration implications of mixed transaction types, and you examine an example program that applies a variety of transaction types to an ordered sequential file.
Inserting Records in an Ordered Sequential File
When you want to add records to an unordered sequential file, you just OPEN the file for EXTEND and then write the records to the file. You can’t do that with an ordered sequential file because if you do, the records will no longer be in order.
When you insert records into an ordered sequential file, a major consideration must be to preserve the ordering.
To insert records, you must create a new file that consists of all the records of the old file with the new records inserted into their correct key-value positions. When you are inserting records into an ordered file, you also have to be aware of the possibility that the record you are trying to insert will have the same key value as one already in the file. This is an error condition. For instance, you can’t have two customer records with the same CustomerId value.
Figure 10-5 is a template that outlines the algorithm required to insert records from an ordered transaction file into their correct positions in an ordered master file. There are three files. The transaction file (TF) contains the three records you want to insert. The master file (MF) is the file into which you wish to insert these records. Because the MF
is a sequential file, the only way to insert the records is to create a new file that contains the inserted records. This is the new master file (NMF).
219
Chapter 10 ■ proCessing sequential Files
Figure 10-5. Inserting records into an ordered sequential file
The program starts by opening the files and reading a record from each of the two input files. This is the equivalent of the read-ahead technique that you saw in Chapter 5. Before you enter the loop that processes the files, you start with a record in each file buffer. The loop is executed until the end of both files, because regardless of which file ends first, the remaining records of the other must be written to the NMF.