Indexed Files and VSAM

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.

Index Key: 1001 ---> Key: 1002 ---> Key: 1003 ---> Data Records [1001] Alice, Engineering [1002] Bob, Marketing [1003] Charlie, Sales

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

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               

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

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

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

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.

  • Test 1 — expects "ALTERNATE"
Need a hint? (−25% XP)

The opposite of primary.

Show solution (0 XP)
       IDENTIFICATION DIVISION.
       PROGRAM-ID. CHAL2.
       PROCEDURE DIVISION.
           DISPLAY "ALTERNATE".
           STOP RUN.