Serial SEARCH
Looking something up in a table is one of the most common jobs in batch
COBOL: turn a branch code into a name, a product code into a price, a
postcode into a sales region. You could write the loop yourself with
PERFORM VARYING, but COBOL has a statement for it: SEARCH.
IDENTIFICATION DIVISION.
PROGRAM-ID. SRCHDEMO.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-BRANCH-VALUES.
05 FILLER PIC X(9) VALUE "D01DUBLIN".
05 FILLER PIC X(9) VALUE "C02CORK ".
05 FILLER PIC X(9) VALUE "G03GALWAY".
01 WS-BRANCH-TABLE REDEFINES WS-BRANCH-VALUES.
05 WS-BRANCH OCCURS 3 TIMES INDEXED BY BR-IX.
10 WS-BR-CODE PIC X(3).
10 WS-BR-NAME PIC X(6).
01 WS-WANTED PIC X(3).
PROCEDURE DIVISION.
MOVE "C02" TO WS-WANTED
PERFORM 1000-FIND-BRANCH
MOVE "X99" TO WS-WANTED
PERFORM 1000-FIND-BRANCH
STOP RUN.
1000-FIND-BRANCH.
SET BR-IX TO 1
SEARCH WS-BRANCH
AT END
DISPLAY WS-WANTED " NOT FOUND"
WHEN WS-BR-CODE(BR-IX) = WS-WANTED
DISPLAY WS-WANTED " IS " WS-BR-NAME(BR-IX)
END-SEARCH.
Output:
C02 IS CORK
X99 NOT FOUND
How SEARCH works
- You search the table item that has the OCCURS (
WS-BRANCH), not a field inside it. - The table must have an
INDEXED BYindex.SEARCHuses the first one. - It tests each
WHENcondition against the current element. On the first match it runs thatWHEN's statements and stops. The index is left pointing at the match, so you can use it afterwards. - If no element matches, it runs the
AT ENDstatements.AT ENDis optional, but leave it out and a failed lookup does nothing at all. That is almost never what you want. - You can have several
WHENs. Whichever condition is true first wins.
Always SET the index first
SEARCH starts from wherever the index currently points. It does not
reset it. Take out the SET BR-IX TO 1 above and look up C02 then D01:
the first search leaves the index at element 2, so the second one starts
there, never looks at element 1, and reports D01 NOT FOUND, even though
D01 is in the table.
This bug is easy to miss in testing, because a single lookup works fine. It only shows up when lookups run in a certain order.
Starting part-way through
Because SEARCH starts at the current index, you can deliberately begin
part-way through, for example SET BR-IX TO 5 to skip header entries.
That is rare. SET ... TO 1 before every serial SEARCH is the rule.
Cost
A serial search checks elements one by one. For a table of 6 or 60 that is nothing. For 50,000 entries searched once per input record over millions of records, it adds up to hours of CPU time. The next lesson shows the fix.
On the job
Put the most frequently used codes first in a serial table. If 80% of transactions are for one branch, putting it at element 1 makes most searches end after one comparison. Old programs are sometimes ordered this way on purpose, so ask before you "tidy" a table into alphabetical order.
Your task
The hardware counter needs a price checker. The starter has a hard-coded
product table with an index PROD-IX:
| Code | Description | Price |
|---|---|---|
| A100 | BOLT PACK | 4.99 |
| A200 | NUT PACK | 3.49 |
| B150 | HINGE | 7.25 |
| C300 | PADLOCK | 12.00 |
| D410 | CHAIN 1M | 8.75 |
| E500 | TOOLBOX | 29.99 |
Read product codes, one per line, until END. For each code display the
code, description and price, or NOT FOUND. At the end display how many
codes were not found:
C300 PADLOCK 12.00
A100 BOLT PACK 4.99
Z999 NOT FOUND
NOT FOUND: 001
Complete 1000-LOOK-UP with a serial SEARCH. Format the price with
WS-PRICE-OUT.