COBOL COBOL 2014 (ISO/IEC 1989:2014), built with GnuCOBOL 3.x ·
✓ verified by execution on 2026-08-16
Sequential files are great for processing millions of records one after the other. But what if you need to instantly look up a single customer’s balance out of a ten-million-record file? Reading from the beginning until you find them would be incredibly slow.
This is where Indexed Files come in. In the mainframe world, these are almost universally powered by VSAM (Virtual Storage Access Method), specifically the KSDS (Key-Sequenced Data Set) flavor.
How Indexed Files Work
An indexed file consists of two parts: the actual data records, and an index. The index is like the index at the back of a textbook. It maps a specific Record Key (like an Employee ID or Customer Number) directly to the physical location of that record on the disk.
When you define an indexed file in COBOL, you specify ORGANIZATION IS INDEXED, and you must designate one of the fields in your record structure as the RECORD KEY.
cobol✓ verified output
IDENTIFICATION DIVISION. PROGRAM-ID. WR-IDX. ENVIRONMENT DIVISION. INPUT-OUTPUT SECTION. FILE-CONTROL. SELECT EMP-FILE ASSIGN TO "emp.dat" ORGANIZATION IS INDEXED ACCESS MODE IS DYNAMIC RECORD KEY IS EMP-ID FILE STATUS IS WS-STATUS. DATA DIVISION. FILE SECTION. FD EMP-FILE. 01 EMP-RECORD. 05 EMP-ID PIC 9(4). 05 EMP-NAME PIC X(20). WORKING-STORAGE SECTION. 01 WS-STATUS PIC XX. PROCEDURE DIVISION. OPEN OUTPUT EMP-FILE MOVE 1001 TO EMP-ID MOVE "Alice" TO EMP-NAME WRITE EMP-RECORD CLOSE EMP-FILE DISPLAY "Wrote Record" STOP RUN.
Output
Wrote Record
Your output
Notice the ACCESS MODE IS DYNAMIC. This is the most powerful mode for indexed files. It allows you to mix random access (jumping straight to a specific key) and sequential access (reading the next record in order) within the same program.
Reading Randomly
To read a specific record, you move the target value into the RECORD KEY field and execute a READ. The INVALID KEY clause handles the scenario where the key doesn’t exist in the file.
cobol✓ verified output
IDENTIFICATION DIVISION. PROGRAM-ID. RD-IDX. ENVIRONMENT DIVISION. INPUT-OUTPUT SECTION. FILE-CONTROL. SELECT EMP-FILE ASSIGN TO "emp.dat" ORGANIZATION IS INDEXED ACCESS MODE IS DYNAMIC RECORD KEY IS EMP-ID FILE STATUS IS WS-STATUS. DATA DIVISION. FILE SECTION. FD EMP-FILE. 01 EMP-RECORD. 05 EMP-ID PIC 9(4). 05 EMP-NAME PIC X(20). WORKING-STORAGE SECTION. 01 WS-STATUS PIC XX. PROCEDURE DIVISION. OPEN OUTPUT EMP-FILE MOVE 1001 TO EMP-ID MOVE "Alice" TO EMP-NAME WRITE EMP-RECORD CLOSE EMP-FILE OPEN INPUT EMP-FILE MOVE 1001 TO EMP-ID READ EMP-FILE INVALID KEY DISPLAY "Not Found" NOT INVALID KEY DISPLAY EMP-NAME END-READ CLOSE EMP-FILE STOP RUN.
Output
Alice
Your output
The START Verb
Sometimes you don’t know the exact key you want, or you want to read all records starting from a specific point. For instance, “process all employees with an ID greater than 5000”.
The START verb does not actually retrieve a record. It simply positions the file pointer. You must follow it with a READ ... NEXT RECORD statement to fetch the data.
Mental Model Verification
Let’s verify how START affects the record buffer conceptually.
When you execute START, the system moves the internal file pointer but does not populate your data buffer. Trace this simulation:
Predict the outputcobol
Read the code. What exactly will it print? Commit to an answer before you look.
IDENTIFICATION DIVISION. PROGRAM-ID. START-SIM. DATA DIVISION. WORKING-STORAGE SECTION. 01 FILE-POINTER PIC 9 VALUE 1. 01 RECORD-BUFFER PIC X(5) VALUE "EMPTY". PROCEDURE DIVISION. MOVE 5 TO FILE-POINTER. DISPLAY FILE-POINTER "-" RECORD-BUFFER. STOP RUN.
Output
5-EMPTY
You predicted
Notice the buffer remains "EMPTY". Just like this simulation, the START verb sets the pointer (e.g., to record 5) but your program’s FD record buffer is not populated until you issue a READ.
INVALID KEY Behavior
Let’s look at how INVALID KEY reacts to missing records. Keep in mind that Employee ID 9999 has never been written to the file.
cobol✓ verified output
IDENTIFICATION DIVISION. PROGRAM-ID. INV-KEY. ENVIRONMENT DIVISION. INPUT-OUTPUT SECTION. FILE-CONTROL. SELECT E-FILE ASSIGN TO "emp2.dat" ORGANIZATION IS INDEXED ACCESS IS DYNAMIC RECORD KEY IS E-ID. DATA DIVISION. FILE SECTION. FD E-FILE. 01 E-REC. 05 E-ID PIC 9(4). PROCEDURE DIVISION. OPEN OUTPUT E-FILE. CLOSE E-FILE. OPEN INPUT E-FILE. MOVE 9999 TO E-ID. READ E-FILE INVALID KEY DISPLAY "ERROR" NOT INVALID KEY DISPLAY "FOUND" END-READ. STOP RUN.
Output
ERROR
Your output
When the READ executes, COBOL consults the index. Since 9999 is missing, the INVALID KEY path triggers, bypassing the NOT INVALID KEY branch completely.
Alternate Keys
While every indexed file has one unique primary RECORD KEY, you can define multiple ALTERNATE RECORD KEYs. This allows you to look up records by secondary fields, like a department code or a last name. Alternate keys can even allow duplicates if you specify WITH DUPLICATES.
Check yourself
What is the primary difference between a sequential file and an indexed file?
Reveal answer
Indexed files allow you to fetch a specific record instantly using a key, while sequential files require reading from the beginning. — Indexed files maintain an internal index mapping keys to data locations, allowing rapid random access.
If you want to position the file pointer to the first record with a key greater than '1000' without actually reading the data yet, which verb do you use?
Reveal answer
START — The START verb establishes the current record pointer based on a key condition. It must be followed by a READ NEXT to actually retrieve the record.
What happens if you try to WRITE a record to an indexed file using a primary RECORD KEY that already exists?
Reveal answer
The WRITE statement fails and triggers an INVALID KEY condition. — Primary keys in indexed files must be unique. Attempting to write a duplicate primary key triggers an INVALID KEY error.
Challenges
🐞 Bug Hunt+50 XP
Bug Hunt: This program tries to print the first employee whose ID is greater than 1000 (which should be Alice). But it prints Bob, the last person written! Find and fix the missing statement so it actually reads the record.
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: Alice "
Show solution (0 XP)
*> COBOL Program IDENTIFICATION DIVISION. PROGRAM-ID. STARTBUG. ENVIRONMENT DIVISION. INPUT-OUTPUT SECTION. FILE-CONTROL. SELECT EMP-FILE ASSIGN TO "emp.dat" ORGANIZATION IS INDEXED ACCESS MODE IS DYNAMIC RECORD KEY IS EMP-ID FILE STATUS IS WS-STATUS. DATA DIVISION. FILE SECTION. FD EMP-FILE. 01 EMP-RECORD. 05 EMP-ID PIC 9(4). 05 EMP-NAME PIC X(20). WORKING-STORAGE SECTION. 01 WS-STATUS PIC XX. PROCEDURE DIVISION. OPEN OUTPUT EMP-FILE MOVE 1001 TO EMP-ID MOVE "Alice" TO EMP-NAME WRITE EMP-RECORD MOVE 1005 TO EMP-ID MOVE "Bob" TO EMP-NAME WRITE EMP-RECORD CLOSE EMP-FILE OPEN INPUT EMP-FILE MOVE 1000 TO EMP-ID START EMP-FILE KEY IS GREATER THAN EMP-ID INVALID KEY DISPLAY "Error" END-START READ EMP-FILE NEXT RECORD AT END DISPLAY "EOF" END-READ DISPLAY "FOUND: " EMP-NAME CLOSE EMP-FILE STOP RUN.
Challenge 2 +50 XP
If you want to read records based on a secondary field like Last Name instead of the primary ID, you use an ________ Record Key.
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.