Control breaks and subtotals
A control break is the moment, while reading sorted input, when the value of a key field (the control field) changes: the last order for customer C1 has gone by and the first for C2 has arrived. At that moment you print C1's subtotal, add it to the grand total, and start counting again for C2.
It is the single most common pattern in batch reporting, and it only works because the input is sorted on the control field. That's why the SORT step so often comes right before the report step in a job.
The algorithm
- Read the first record; remember its control field as
WS-PREV-.... - For each record: if its control field differs from the previous one, break first: print the previous group's total, roll it into the grand total, reset it, remember the new key.
- Then process the record: print the detail, add to the group total.
- At end of file, do one final break for the last group, then print the grand total.
IDENTIFICATION DIVISION.
PROGRAM-ID. BREAKDEMO.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 ORDER-DATA.
05 FILLER PIC X(7) VALUE "C1 0010".
05 FILLER PIC X(7) VALUE "C1 0025".
05 FILLER PIC X(7) VALUE "C2 0100".
05 FILLER PIC X(7) VALUE "C3 0005".
05 FILLER PIC X(7) VALUE "C3 0005".
01 ORDER-TABLE REDEFINES ORDER-DATA.
05 ORDER-ENTRY OCCURS 5 TIMES.
10 ORD-CUST PIC X(2).
10 FILLER PIC X.
10 ORD-QTY PIC 9(4).
01 WS-I PIC 9.
01 WS-PREV-CUST PIC X(2).
01 WS-CUST-TOTAL PIC 9(5) VALUE 0.
01 WS-GRAND-TOTAL PIC 9(5) VALUE 0.
01 WS-QTY-ED PIC ZZ,ZZ9.
PROCEDURE DIVISION.
MOVE ORD-CUST(1) TO WS-PREV-CUST
PERFORM VARYING WS-I FROM 1 BY 1 UNTIL WS-I > 5
IF ORD-CUST(WS-I) NOT = WS-PREV-CUST
PERFORM CUSTOMER-BREAK
END-IF
MOVE ORD-QTY(WS-I) TO WS-QTY-ED
DISPLAY ORD-CUST(WS-I) " " WS-QTY-ED
ADD ORD-QTY(WS-I) TO WS-CUST-TOTAL
END-PERFORM
PERFORM CUSTOMER-BREAK
MOVE WS-GRAND-TOTAL TO WS-QTY-ED
DISPLAY "ALL " WS-QTY-ED " **"
STOP RUN.
CUSTOMER-BREAK.
MOVE WS-CUST-TOTAL TO WS-QTY-ED
DISPLAY " " WS-QTY-ED " * " WS-PREV-CUST " TOTAL"
ADD WS-CUST-TOTAL TO WS-GRAND-TOTAL
MOVE 0 TO WS-CUST-TOTAL
IF WS-I <= 5
MOVE ORD-CUST(WS-I) TO WS-PREV-CUST
END-IF.
Output:
C1 10
C1 25
35 * C1 TOTAL
C2 100
100 * C2 TOTAL
C3 5
C3 5
10 * C3 TOTAL
ALL 145 **
(The input lives in a table here so the example runs on its own. In a real job it comes from a sorted file, read one record at a time.)
Details that matter
- Label the total with the previous key. When the break fires, the
record you just read already belongs to the next group. Print
WS-PREV-CUST, not the field in the record. - End of file is a break. Forgetting the final break is the classic bug: every group gets its total except the last.
- Empty input. If there were no records there is no group to close. Guard the final break with a record count or a first-record flag, or the report shows a total for a blank key.
- Roll totals upward, then reset. Add the group total into the grand total at the break instead of adding every detail to both. With more levels (next lesson) each level rolls into the one above.
- Asterisks mark levels. One
*on a group total, two on the grand total: an old print-shop convention you will still see in many reports.
On the job
If a control-break report suddenly shows the same group twice, with two partial subtotals, don't debug the COBOL first. Check the sort step: the input probably wasn't sorted on the control field (a changed SORT FIELDS, or a new file concatenated after the sort).
Your task
Sales wants totals per region. The input is already sorted by region.
DD SALESIN — line sequential, 22 bytes: SALE-REGION X(5),
SALE-REP X(10), SALE-AMOUNT 9(5)V99.
DD RPTOUT — the report. The starter has all the layouts, the
headings and the detail printing.
Add the control break: when the region changes, and after the last record, print that region's total line followed by a blank line. At the very end print the grand total.
SALES BY REGION
REGION REP AMOUNT
EAST MURPHY 12,999.99
EAST DOYLE 410.00
EAST TOTAL 13,409.99
NORTH WALSH 8,150.00
...
SOUTH TOTAL 5,125.50
GRAND TOTAL 49,070.49
An empty input file prints the two heading lines and
GRAND TOTAL 0.00 (with the usual spacing), and no region total.