COBOL COBOL 2014 (ISO/IEC 1989:2014), built with GnuCOBOL 3.x Β·
β verified by execution on 2026-08-16
Finding specific data inside an array is a universal task in programming. In COBOL, this is achieved through two distinct statements: SEARCH and SEARCH ALL.
While their names sound remarkably similar, they perform completely different computer science algorithms under the hood. Understanding when to use which is a critical skill for any mainframe developer.
The Sequential SEARCH
The standard SEARCH statement performs a linear, serial search. It starts at a given index and checks every element one by one until it finds a match or runs out of rows.
Because it scans sequentially, the programmer must manually define where the scan should begin. You initialize the index using a SET statement before running the SEARCH.
cobolβ verified output
IDENTIFICATION DIVISION. PROGRAM-ID. SRCH-SERIAL. DATA DIVISION. WORKING-STORAGE SECTION. 01 CUSTOMER-TABLE. 05 CUSTOMER-RECORD OCCURS 3 TIMES INDEXED BY CUST-IDX. 10 CUST-ID PIC 9(4). 10 CUST-NAME PIC X(10). PROCEDURE DIVISION. MOVE 1003 TO CUST-ID(1). MOVE "ALICE" TO CUST-NAME(1). MOVE 1001 TO CUST-ID(2). MOVE "BOB" TO CUST-NAME(2). MOVE 1002 TO CUST-ID(3). MOVE "CAROL" TO CUST-NAME(3). SET CUST-IDX TO 1 SEARCH CUSTOMER-RECORD AT END DISPLAY "NOT FOUND" WHEN CUST-ID(CUST-IDX) = 1001 DISPLAY "FOUND: " CUST-NAME(CUST-IDX) END-SEARCH STOP RUN.
Output
FOUND: BOB
Your output
Notice how we had to SET CUST-IDX TO 1. Without this, the search might start at a random position or fail entirely depending on the compiler.
You can also use the VARYING clause to automatically increment a secondary variable alongside the main index while the search progresses. This is very useful when you need to track how many attempts it took, or keep a separate subscript in sync.
cobolβ verified output
IDENTIFICATION DIVISION. PROGRAM-ID. SRCH-VARY. DATA DIVISION. WORKING-STORAGE SECTION. 01 CUSTOMER-TABLE. 05 CUSTOMER-RECORD OCCURS 3 TIMES INDEXED BY CUST-IDX. 10 CUST-ID PIC 9(4). 10 CUST-NAME PIC X(10). 01 LOOP-COUNT PIC 9 VALUE 1. PROCEDURE DIVISION. MOVE 1003 TO CUST-ID(1). MOVE "ALICE" TO CUST-NAME(1). MOVE 1001 TO CUST-ID(2). MOVE "BOB" TO CUST-NAME(2). MOVE 1002 TO CUST-ID(3). MOVE "CAROL" TO CUST-NAME(3). SET CUST-IDX TO 1 SEARCH CUSTOMER-RECORD VARYING LOOP-COUNT AT END DISPLAY "NOT FOUND" WHEN CUST-ID(CUST-IDX) = 1002 DISPLAY "FOUND AT LOOP: " LOOP-COUNT END-SEARCH STOP RUN.
Output
FOUND AT LOOP: 3
Your output
The High-Speed SEARCH ALL
While SEARCH is simple, scanning a massive table of 50,000 records one by one is incredibly slow. To solve this, COBOL provides SEARCH ALL.
SEARCH ALL executes a highly optimized binary search algorithm. Instead of checking every row, it starts in the middle. If the target is smaller, it chops the table in half and checks the middle of the lower half, repeating until the record is found. A binary search can find any record out of 32,768 rows in just 15 attempts.
To use SEARCH ALL, the table must declare an ASCENDING KEY or DESCENDING KEY. Additionally, because the algorithm manages the jumping around on its own, it completely manages the index. No prior SET statement is required!
cobolβ verified output
IDENTIFICATION DIVISION. PROGRAM-ID. SRCH-ALL. DATA DIVISION. WORKING-STORAGE SECTION. 01 CUSTOMER-TABLE. 05 CUSTOMER-RECORD OCCURS 3 TIMES ASCENDING KEY IS CUST-ID INDEXED BY CUST-IDX. 10 CUST-ID PIC 9(4). 10 CUST-NAME PIC X(10). PROCEDURE DIVISION. MOVE 1001 TO CUST-ID(1). MOVE "BOB" TO CUST-NAME(1). MOVE 1002 TO CUST-ID(2). MOVE "CAROL" TO CUST-NAME(2). MOVE 1003 TO CUST-ID(3). MOVE "ALICE" TO CUST-NAME(3). SEARCH ALL CUSTOMER-RECORD AT END DISPLAY "NOT FOUND" WHEN CUST-ID(CUST-IDX) = 1002 DISPLAY "FOUND: " CUST-NAME(CUST-IDX) END-SEARCH STOP RUN.
Output
FOUND: CAROL
Your output
If your table has multiple keys defined, you can check all of them in your WHEN statement by combining them with AND. They must be checked in the exact order they are defined in the OCCURS clause.
cobolβ verified output
IDENTIFICATION DIVISION. PROGRAM-ID. SRCH-MULT. DATA DIVISION. WORKING-STORAGE SECTION. 01 EMP-TABLE. 05 EMP-RECORD OCCURS 3 TIMES ASCENDING KEY IS DEPT-NO EMP-ID INDEXED BY EMP-IDX. 10 DEPT-NO PIC X(3). 10 EMP-ID PIC 9(3). 10 EMP-NAME PIC X(10). PROCEDURE DIVISION. MOVE "HR " TO DEPT-NO(1). MOVE 101 TO EMP-ID(1). MOVE "ALICE" TO EMP-NAME(1). MOVE "IT " TO DEPT-NO(2). MOVE 201 TO EMP-ID(2). MOVE "BOB" TO EMP-NAME(2). MOVE "IT " TO DEPT-NO(3). MOVE 202 TO EMP-ID(3). MOVE "CAROL" TO EMP-NAME(3). SEARCH ALL EMP-RECORD AT END DISPLAY "NOT FOUND" WHEN DEPT-NO(EMP-IDX) = "IT " AND EMP-ID(EMP-IDX) = 202 DISPLAY "FOUND: " EMP-NAME(EMP-IDX) END-SEARCH STOP RUN.
Output
FOUND: CAROL
Your output
The Silent Killer: Unsorted Data
There is a massive catch when using SEARCH ALL. COBOL trusts that the data actually loaded in memory matches the ASCENDING KEY rule you defined.
If the data is out of order, COBOL will not crash. It will not throw a runtime error. The binary search will execute, make an incorrect mathematical guess about where to jump next, completely skip over your record, and silently return the AT END logic.
Run this example to witness the bug in action. We are searching for ID 1003, and it clearly exists, but it fails because the data wasnβt inserted in numerical order:
cobolβ verified output
IDENTIFICATION DIVISION. PROGRAM-ID. SRCH-BUG. DATA DIVISION. WORKING-STORAGE SECTION. 01 CUSTOMER-TABLE. 05 CUSTOMER-RECORD OCCURS 3 TIMES ASCENDING KEY IS CUST-ID INDEXED BY CUST-IDX. 10 CUST-ID PIC 9(4). 10 CUST-NAME PIC X(10). PROCEDURE DIVISION. *> Data is NOT sorted by CUST-ID MOVE 1003 TO CUST-ID(1). MOVE "ALICE" TO CUST-NAME(1). MOVE 1001 TO CUST-ID(2). MOVE "BOB" TO CUST-NAME(2). MOVE 1002 TO CUST-ID(3). MOVE "CAROL" TO CUST-NAME(3). SEARCH ALL CUSTOMER-RECORD AT END DISPLAY "NOT FOUND" WHEN CUST-ID(CUST-IDX) = 1003 DISPLAY "FOUND: " CUST-NAME(CUST-IDX) END-SEARCH STOP RUN.
Output
NOT FOUND
Your output
When building production systems, you must either guarantee the source file is pre-sorted before loading it into memory, or use COBOLβs internal SORT verb to organize the table before executing a SEARCH ALL.
Test your understanding of this silent failure. What happens when this program runs? Look closely at the AT END clause.
Predict the outputcobol
Read the code. What exactly will it print? Commit to an answer before you look.
IDENTIFICATION DIVISION. PROGRAM-ID. TST. DATA DIVISION. WORKING-STORAGE SECTION. 01 TBL. 05 T OCCURS 3 TIMES ASCENDING KEY K INDEXED BY I. 10 K PIC 9. PROCEDURE DIVISION. MOVE 3 TO K(1). MOVE 1 TO K(2). MOVE 2 TO K(3). SEARCH ALL T AT END DISPLAY K(1) K(2) K(3) WHEN K(I) = 3 DISPLAY "FOUND" END-SEARCH. STOP RUN.
Output
312
You predicted
Edge Cases
Duplicate Keys: If multiple table elements have the same key value, SEARCH ALL will find one of them, but which specific duplicate it lands on is mathematically unpredictable depending on the table size.
Missing SET: A serial SEARCH begins at the current index. If you forget to SET it to 1, the search begins wherever the index was left previously, missing all earlier elements.
Nested Tables: When running a serial search against a multi-dimensional nested array, you must initialize the outer dimensionβs index manually before running a SEARCH on the inner dimension.
cobolβ verified output
IDENTIFICATION DIVISION. PROGRAM-ID. SRCH-MULTI. DATA DIVISION. WORKING-STORAGE SECTION. 01 COMPANY. 05 DEPT OCCURS 2 TIMES INDEXED BY D-IDX. 10 DEPT-NAME PIC X(5). 10 EMP OCCURS 2 TIMES INDEXED BY E-IDX. 15 EMP-NAME PIC X(10). PROCEDURE DIVISION. MOVE "HR " TO DEPT-NAME(1). MOVE "ALICE" TO EMP-NAME(1 1). MOVE "BOB" TO EMP-NAME(1 2). MOVE "IT " TO DEPT-NAME(2). MOVE "CAROL" TO EMP-NAME(2 1). MOVE "DAVE" TO EMP-NAME(2 2). SET D-IDX TO 2 SET E-IDX TO 1 SEARCH EMP AT END DISPLAY "NOT FOUND" WHEN EMP-NAME(D-IDX E-IDX) = "DAVE" DISPLAY "FOUND IN DEPT 2" END-SEARCH STOP RUN.
Output
FOUND IN DEPT 2
Your output
Check yourself
What is the primary difference in how SEARCH and SEARCH ALL find data?
Reveal answer
They operate completely differently. SEARCH does a serial scan starting from the current index. SEARCH ALL does a binary search over the entire table. β SEARCH performs a linear, serial search (checking one by one). SEARCH ALL performs a highly optimized binary search (checking the middle, then dividing the search area).
What is a strict requirement for the data in a table before using SEARCH ALL?
Reveal answer
You can only use SEARCH ALL if the table's OCCURS clause specifies ASCENDING KEY or DESCENDING KEY, and the actual data in memory must be sorted to match. β A binary search fundamentally relies on the data being strictly sorted. If the data isn't in order, the algorithm will look in the wrong places.
If you use SEARCH ALL on unsorted data, what will COBOL do?
Reveal answer
There is no runtime error. The binary search algorithm will execute, make incorrect assumptions, and usually silently return an AT END (not found). β COBOL trusts the programmer. It will apply binary logic to the unsorted data, jumping around incorrectly, and likely fail to find the target even if it exists.
Which operators can you use in the WHEN clause of a SEARCH ALL statement?
Reveal answer
SEARCH ALL only supports equality conditions (=) in its WHEN clause. β Because it is a binary search optimizing for an exact match along a sorted key, SEARCH ALL only permits checking for equality.
Challenges
Challenge 1 +10 XP
Fix this code so that the serial SEARCH correctly finds "Charlie" by starting at the beginning of the table.
cobolβ solution verified at build time
COBOL has no in-browser runtime, so this one is pen-and-paper: work out what the
fixed code should be, then open the solution and compare. The expected output
below is real β it came from compiling the solution with GnuCOBOL when this page
was built.
Test 1 β expects "FOUND\n"
Need a hint? (β25% XP)
Before a serial SEARCH, you must SET the index to 1. If you leave it at 3 (or an undefined value), it won't scan the whole table.
Show solution (0 XP)
IDENTIFICATION DIVISION.PROGRAM-ID. CHAL1.DATA DIVISION.WORKING-STORAGE SECTION.01 NAMES-TABLE. 05 NAME-ITEM PIC X(10) OCCURS 3 TIMES INDEXED BY N-IDX.PROCEDURE DIVISION. MOVE "Alice" TO NAME-ITEM(1) MOVE "Bob" TO NAME-ITEM(2) MOVE "Charlie" TO NAME-ITEM(3) SET N-IDX TO 1 SEARCH NAME-ITEM AT END DISPLAY "NOT FOUND" WHEN NAME-ITEM(N-IDX) = "Charlie" DISPLAY "FOUND" END-SEARCH STOP RUN.
π Bug Hunt+15 XP
This program is supposed to find the CHAIR product (ID 100) using SEARCH ALL, but it prints 'NOT FOUND' instead. The developer who wrote it didn't realize SEARCH ALL requires the data to be perfectly sorted in memory according to its ASCENDING KEY. Run the code to see it fail, then reorder the MOVE statements so the binary search works.
This code runs. It just does the wrong thing. Read it, find the defect,
fix it β the tests below decide when you are right.
cobolβ solution verified at build time
COBOL has no in-browser runtime, so this one is pen-and-paper: work out what the
fixed code should be, then open the solution and compare. The expected output
below is real β it came from compiling the solution with GnuCOBOL when this page
was built.
Test 1 β expects "FOUND: CHAIR \n"
Need a hint? (β25% XP)
SEARCH ALL expects the data loaded in memory to strictly follow the ASCENDING KEY rule. Swap the order of the MOVE statements so PROD-ID 100 comes first, 200 second, and 300 third.
Show solution (0 XP)
IDENTIFICATION DIVISION.PROGRAM-ID. CHAL2.DATA DIVISION.WORKING-STORAGE SECTION.01 PRODUCT-TABLE. 05 PROD-REC OCCURS 3 TIMES ASCENDING KEY IS PROD-ID INDEXED BY P-IDX. 10 PROD-ID PIC 9(3). 10 PROD-NAME PIC X(10).PROCEDURE DIVISION. MOVE 100 TO PROD-ID(1). MOVE "CHAIR" TO PROD-NAME(1). MOVE 200 TO PROD-ID(2). MOVE "DESK" TO PROD-NAME(2). MOVE 300 TO PROD-ID(3). MOVE "TABLE" TO PROD-NAME(3). SEARCH ALL PROD-REC AT END DISPLAY "NOT FOUND" WHEN PROD-ID(P-IDX) = 100 DISPLAY "FOUND: " PROD-NAME(P-IDX) END-SEARCH STOP RUN.