SEARCH and SEARCH ALL

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 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       

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

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.

1001 1002 1003 ... SEARCH ALL jumps here

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     

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     

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

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 output cobol

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

Edge Cases

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

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.