The report format is given in the template shown in Figure 12-1.
Figure 12-1. Report template for Aromamora Summary Sales Report
Notes
Here are some things to consider:
• The B in the OilId indicates that this is a base oil.
• UnitSize represents the size of the oil container purchased. There are only three sizes for base
oils: 1 (50ml), 2 (100ml), and 3 (200ml).
• ValueOfSales is the sum of the ValueOfSale calculated for each record.
274
Chapter 12 ■ advanCed data deClaration
• ValueOfSale is UnitsSold * UnitCost(OilNum,Unitsize).
• The OilName and UnitCost are obtained from a prefilled table of values (see the program
outline for details). The two-dimensional table required to hold these values is shown in
Figure 12-2.
Figure 12-2. Table of oil names and unit costs
Oil Costs Table
Aromamora sells 14 kinds of base oil. The cost of each type of base oil in each of the three container sizes (50ml, 100ml, and 200ml) is given by the table in Figure 12-2. For instance, almond oil costs $02.00 for the 50ml size, $03.50 for 100ml size, and $06.50 for 200ml size.
Example 12-1 demonstrates how you can translate the information given in Figure 12-1 into a prefilled COBOL
table. You start by laying down in memory the information you want in the table. Obviously you have to omit the dollar sign and decimal point because those are text and you need to do calculations on the data in the table. At this point, you have a block of undifferentiated data in memory as follows:
Almond 020003500650
Aloe vera 047508501625
Apricot kernel 025004250775
Avocado 027504750875
Coconut 027504750895
Evening primrose037506551225
Grape seed 018503250600
Peanut 027504250795
275
Chapter 12 ■ advanCed data deClaration
Jojoba 072513252500
Macadamia 032505751095
Rosehip 052509951850
Sesame 029504250750
Walnut 027504550825
Wheatgerm 045007751425
The final step in creating the table is to use the REDEFINES clause to impose a table definition on the area of memory, as shown in Example 12-1. Once the data is redefined, you can access it using the table. For instance, OilName(9) = Jojoba, and UnitCost(9,2) = 1325.
Example 12-1. Table Definition of the Two-Dimensional Table Shown in Figure 12-1
01 OilsTable.
02 OilTableValues.
03 FILLER PIC X(28) VALUE "Almond 020003500650".
03 FILLER PIC X(28) VALUE "Aloe vera 047508501625".
03 FILLER PIC X(28) VALUE "Apricot kernel 025004250775".
03 FILLER PIC X(28) VALUE "Avocado 027504750875".
03 FILLER PIC X(28) VALUE "Coconut 027504750895".
03 FILLER PIC X(28) VALUE "Evening primrose037506551225".
03 FILLER PIC X(28) VALUE "Grape seed 018503250600".
03 FILLER PIC X(28) VALUE "Peanut 027504250795".
03 FILLER PIC X(28) VALUE "Jojoba 072513252500".
03 FILLER PIC X(28) VALUE "Macadamia 032505751095".
03 FILLER PIC X(28) VALUE "Rosehip 052509951850".
03 FILLER PIC X(28) VALUE "Sesame 029504250750".
03 FILLER PIC X(28) VALUE "Walnut 027504550825".
03 FILLER PIC X(28) VALUE "Wheatgerm 045007751425".
02 FILLER REDEFINES OilTableValues.
03 BaseOil OCCURS 14 TIMES.
04 OilName PIC X(16).
04 UnitCost PIC 99V99 OCCURS 3 TIMES.
Program
This is a typical one-level control-break program. I have kept the program simple (see Listing 12-1) to allow you to focus on the declaration and use of the two-dimensional table. Note that in a real situation, the oil-cost table would not be static as it is in this program. The cost data is likely to change, so for maintenance reasons the table would probably be instantiated from a file. A portion of the sales file used to the test the program and the summary report produced from that file are shown in Figure 12-3 in the next section.
Listing 12-1. Aromamora Base Oils Summary Sales Report
IDENTIFICATION DIVISION.
PROGRAM-ID. Listing12-1.
AUTHOR. Michael Coughlan.
* This program produces a summary report showing the sales of base oils
* to Aromamora customers by processing the OilSalesFile. The OilSalesFile is a
* sequential file ordered on ascending CustomerId. The report is required to be
* printed in ascending CustomerId order.
276
Chapter 12 ■ advanCed data deClaration
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT BaseOilsSalesFile ASSIGN TO "Listing12-1.Dat"
ORGANIZATION IS LINE SEQUENTIAL.
SELECT SummaryReport ASSIGN TO "Listing12-1.Rpt"
ORGANIZATION IS LINE SEQUENTIAL.
DATA DIVISION.
FILE SECTION.
FD BaseOilsSalesFile.
01 SalesRec.
88 EndOfSalesFile VALUE HIGH-VALUES.
02 CustomerId PIC X(5).
02 CustomerName PIC X(20).
02 OilId.
03 FILLER PIC X.
03 OilNum PIC 99.
02 UnitSize PIC 9.
02 UnitsSold PIC 999.
FD SummaryReport.
01 PrintLine PIC X(45).
WORKING-STORAGE SECTION.
01 OilsTable.
02 OilTableValues.
03 FILLER PIC X(28) VALUE "Almond 020003500650".
03 FILLER PIC X(28) VALUE "Aloe vera 047508501625".
03 FILLER PIC X(28) VALUE "Apricot kernel 025004250775".
03 FILLER PIC X(28) VALUE "Avocado 027504750875".
03 FILLER PIC X(28) VALUE "Coconut 027504750895".
03 FILLER PIC X(28) VALUE "Evening primrose037506551225".
03 FILLER PIC X(28) VALUE "Grape seed 018503250600".
03 FILLER PIC X(28) VALUE "Peanut 027504250795".
03 FILLER PIC X(28) VALUE "Jojoba 072513252500".
03 FILLER PIC X(28) VALUE "Macadamia 032505751095".
03 FILLER PIC X(28) VALUE "Rosehip 052509951850".
03 FILLER PIC X(28) VALUE "Sesame 029504250750".
03 FILLER PIC X(28) VALUE "Walnut 027504550825".
03 FILLER PIC X(28) VALUE "Wheatgerm 045007751425".
02 FILLER REDEFINES OilTableValues.
03 BaseOil OCCURS 14 TIMES.
04 OilName PIC X(16).
04 UnitCost PIC 99V99 OCCURS 3 TIMES.
01 ReportHeadingLine PIC X(41)
VALUE " Aromamora Base Oils Summary Sales Report".
277
Chapter 12 ■ advanCed data deClaration
01 TopicHeadingLine.
02 FILLER PIC X(9) VALUE "Cust Id".
02 FILLER PIC X(15) VALUE "Customer Name".
02 FILLER PIC X(7) VALUE SPACES.
02 FILLER PIC X(12) VALUE "ValueOfSales".
01 ReportFooterLine PIC X(43)
VALUE "************** End of Report **************".
01 CustSalesLine.
02 PrnCustId PIC B9(5).
02 PrnCustName PIC BBBX(20).
02 PrnCustTotalSales PIC BBB$$$$,$$9.99.
01 CustTotalSales PIC 9(6)V99.
01 PrevCustId PIC X(5).
01 ValueOfSale PIC 9(5)V99.
PROCEDURE DIVISION.
Print-Summary-Report.
OPEN OUTPUT SummaryReport
OPEN INPUT BaseOilsSalesFile
WRITE PrintLine FROM ReportHeadingLine AFTER ADVANCING 1 LINE
WRITE PrintLine FROM TopicHeadingLine AFTER ADVANCING 2 LINES
READ BaseOilsSalesFile
AT END SET EndOfSalesFile TO TRUE
END-Read
PERFORM PrintCustomer
Lines UNTIL EndOfSalesFile
WRITE PrintLine FROM ReportFooterLine AFTER ADVANCING 3 LINES
CLOSE SummaryReport, BaseOilsSalesFile
STOP RUN.
PrintCustomerLines.
MOVE ZEROS TO CustTotalSales
MOVE CustomerId TO PrnCustId, PrevCustId
MOVE CustomerName TO PrnCustName
PERFORM UNTIL CustomerId NOT = PrevCustId
COMPUTE ValueOfSale = UnitsSold * UnitCost(OilNum, UnitSize)
ADD ValueOfSale TO CustTotalSales
READ BaseOilsSalesFile
AT END SET EndOfSalesFile TO TRUE
END-Read
END-PERFORM
MOVE CustTotalSales TO PrnCustTotalSales
WRITE PrintLine FROM CustSalesLine AFTER ADVANCING 2 LINES.
278
Chapter 12 ■ advanCed data deClaration
Test Data and Results
Due to space constraints, only a portion of the test data file is shown (see Figure 12-3).
Figure 12-3. Partial test data and results produced
279
Chapter 12 ■ advanCed data deClaration
The REDEFINES Clause
So far, I have dealt informally with the REDEFINES clause. You have seen how to use it to create a prefilled table of values, but I have not formally defined what REDEFINES does or explored its other uses.
When a file contains different types of records, you must create a separate record description for each record type in the file’s FD entry. You have seen that all these record descriptions map on to the same area of storage. They are, in effect, redefinitions of the area of storage. What the REDEFINES clause allows you to do is to achieve the same effect for units smaller than a record and in parts of the DATA DIVISION other than the FILE SECTION. The REDEFINES clause lets you give different data descriptions to the same area of storage.
REDEFINES Syntax
The syntax metalanguage for the REDEFINES clause is given in Figure 12-4. Identifier1 is the data item that originally defines the area of storage, and Identifier2 is the data item that redefines it.
Figure 12-4. Syntax metalanguage for the REDEFINES clause
REDEFINES Notes
The metalanguage defines the syntax of the REDEFINES clause, but there are also a number of semantic rules that must be obeyed when you use REDEFINES:
• The REDEFINES clause must immediately follow Identifier2 (that is, REDEFINES must come
before PIC [see Example 12-2]).
• The level numbers of Identifier1 and Identifier2 must be the same and cannot be 66 or 88.
• The data description of Identifier1 cannot contain an OCCURS clause (that is, you can’t
redefine a table element).
• If there are multiple redefinitions of the same area of storage, then they must all redefine the
data item that originally defined the area (see Example 12-5).
• The redefining entries (Identifier2) cannot contain VALUE clauses except in condition
name entries.
• No entry with a level number lower (that is, higher in the hierarchy) than the level number of
Identifier1 and Identifier2 can occur between Identifier1 and Identifier2.
• Entries redefining the area must immediately follow those that originally defined it.
• Only entries subordinate to Identifier1 are allowed between Identifier1 and Identifier2.
• The REDEFINES clause must not be used for records (01 level) described in the FILE SECTION
because multiple 01 entries for the same file are implicit redefinitions of the first 01 level
record.
280
Chapter 12 ■ advanCed data deClaration
REDEFINES Examples
The best way to understand how the REDEFINES clause works is to explore some of the ways it may be used through a number of examples.
REDEFINES Example 1
Some COBOL statements, such as UNSTRING, require their receiving fields to be alphanumeric (PIC X) data items. This is inconvenient if the value of the data item is actually numeric, because then a MOVE is required to place the value into a numeric item. If the value contains a decimal point, this creates even more difficulties.
For example, suppose an UNSTRING statement has just extracted the text value “5432195” from a string, and you want to move this value to a numeric item described as PIC 9(5)V99. An ordinary MOVE is not going to work because the computer will not know that you want the item treated as if it were the value 654321.95.
The REDEFINES clause allows you to solve this problem neatly because you can UNSTRING the number into
TextValue and then treat TextValue as if it were described as PIC 9(5)V99 (see Example 12-2). If TextValue contains the alphanumeric value “65432195”, then NumericValue, which REDEFINES it, sees the value as 654321.95 (see Figure 12-5).
Example 12-2. Redefining an Alphanumeric Item as a Decimal Data Item
01 RedefinesExample1.
02 TextValue PIC X(8).
02 NumericValue REDEFINES TextValue PIC 9(6)V99.
Figure 12-5. Memory model showing the result of redefinition
REDEFINES Example 2
The first example showed how you can use the REDEFINES clause to treat a set of alphanumeric digits as a decimal number. This example explores a similar problem. When a program ACCEPTs a decimal number from a user, the
decimal point is included. This is a problem because this decimal point is a text character. If you move a numeric literal (such as 1234.55) that contains a decimal point into a numeric data item that contains an assumed decimal point (such as PIC 9(5)V99), the actual and assumed decimal points align. This does not happen when you move an item containing the decimal point text character. In fact, if you move an item containing an actual decimal point into a numeric data item and then try to perform an arithmetic calculation on that data item, the program will crash (halt unexpectedly).
281
Chapter 12 ■ advanCed data deClaration
A solution to this problem is given in Example 12-3. When a number containing an actual decimal point is
accepted from the user, the UNSTRING verb is used to split the input string into the digits before the decimal point and those after the decimal point. Although WorkArea contains only numeric digits, because it is a group item, its type is alphanumeric, and so it can’t be used in a calculation. The solution is to redefine WorkArea as WorkNum, which is a numeric data item that can be used in calculations. A model of the redefined data items is given in Figure 12-6.
Example 12-3. Redefining Two Data Items as a Single Numeric Item
WORKING-STORAGE SECTION.
01 InputString PIC X(8).
01 WorkArea.
02 Fnum PIC 9(5) VALUE ZEROS.
02 Snum PIC 99 VALUE ZEROS.
01 WorkNum REDEFINES WorkArea PIC 99999V99.
01 EditedNum PIC ZZ,ZZ9.99.
PROCEDURE DIVISION.
Begin.
DISPLAY "Enter a decimal number - " WITH NO ADVANCING
ACCEPT InputString
UNSTRING InputString DELIMITED BY ".", ALL SPACES
INTO Fnum, Snum
MOVE WorkNum TO EditedNum
DISPLAY "Decimal Number = " EditedNum
ADD 10 TO WorkNum
MOVE WorkNum TO EditedNum
DISPLAY "Decimal Number = " EditedNum
Figure 12-6. Model showing WorkArea, Fnum, and Snum redefined as WorkNum
REDEFINES Example 3
Working with percentages often presents a problem. If the percentage is held as an integer, then calculations are complicated by having to divide by 100. For instance, COMPUTE PercentOfBase = BaseAmount * PercentToApply /100.
On the other hand, if the percentage is held as a decimal fraction, then calculations are made simpler but communication with users is complicated because now they have to input or print the percentage as a decimal fraction rather than a whole number.
282<
br />
Chapter 12 ■ advanCed data deClaration
The solution is to take in the percentage as an integer value and then use REDEFINES to treat it as a decimal fraction. Example 12-4 is a program fragment that shows how this works.
Example 12-4. Using REDEFINES to Allow Different Views of a Percentage Value
DATA DIVISION.
WORKING-STORAGE SECTION.
01 PercentToApply PIC 9(3).
01 Percentage REDEFINES PercentToApply PIC 9V99.
01 BaseAmount PIC 9(5) VALUE 10555.
01 PercentOfBase PIC ZZ,ZZ9.99.
01 PrnPercent PIC ZZ9.
PROCEDURE DIVISION.
Begin.
MOVE 23 TO PercentToApply
COMPUTE PercentOfBase = BaseAmount * Percentage
DISPLAY "23% of 10555 is = " PercentOfBase
MOVE PercentToApply to PrnPercent
DISPLAY "Percentage applied was " PrnPercent "%"
STOP RUN.
REDEFINES Example 4
The REDEFINES clause is also useful when you need to treat a numeric item as if it had its decimal point in a different place. For instance, Example 12-5 shows how you can use the REDEFINES clause to provide time conversions between seconds, milliseconds, microseconds, and nanoseconds.
The main purpose of Example 12-5 is to illustrate the rule that if there are multiple redefinitions of an area of storage, they must all refer to the data item that originally defined the area of storage.
Example 12-5. Time Conversion by Multiple Redefinition
WORKING-STORAGE SECTION.
01 NanoSecs PIC 9(10).
01 MicroSecs REDEFINES NanoSecs PIC 9999999V999.
01 MilliSecs REDEFINES NanoSecs PIC 9999V999999.
01 Seconds REDEFINES NanoSecs PIC 9V999999999.
01 EditedNum PIC Z,ZZZ,ZZZ,ZZ9.99.
PROCEDURE DIVISION.
Begin.
MOVE 1234567895 TO NanoSecs
MOVE NanoSecs TO EditedNum
DISPLAY EditedNum " NanoSecs"
283
Michael Coughlan Page 35