Table handling in COBOL involves working with arrays (called tables) that contain multiple occurrences of the same data format. Tables are defined using the OCCURS clause, accessed using subscripts or indices, and searched using SEARCH or SEARCH ALL statements. Understanding table handling is essential for processing collections of data, implementing lookup tables, managing lists, and working with structured data sets in mainframe COBOL programs.
A table (array) in COBOL is a data structure containing multiple occurrences of the same format:
Tables are useful for storing lists of customers, products, transaction records, lookup values, and any collection of related data.
The OCCURS clause defines a table by specifying how many times a data item repeats:
123level-number data-name OCCURS integer TIMES [INDEXED BY index-name-1 [index-name-2 ...]].
Rules for OCCURS:
123456789101112131415161718192021WORKING-STORAGE SECTION. 01 CUSTOMER-TABLE. 05 CUSTOMER-ENTRY OCCURS 100 TIMES INDEXED BY CUST-INDEX. 10 CUSTOMER-ID PIC 9(5). 10 CUSTOMER-NAME PIC X(30). 10 CUSTOMER-BALANCE PIC 9(8)V99. PROCEDURE DIVISION. MAIN-PARA. *> Access first customer SET CUST-INDEX TO 1 MOVE 12345 TO CUSTOMER-ID(CUST-INDEX) MOVE 'JOHN SMITH' TO CUSTOMER-NAME(CUST-INDEX) MOVE 1000.50 TO CUSTOMER-BALANCE(CUST-INDEX) *> Access fifth customer SET CUST-INDEX TO 5 DISPLAY "Customer 5: " CUSTOMER-NAME(CUST-INDEX) STOP RUN.
This defines a table of 100 customer entries, each containing ID, name, and balance. The INDEXED BY clause creates an index for efficient access.
Table elements can be accessed using subscripts or indices:
12345678910111213141516171819WORKING-STORAGE SECTION. 01 PRODUCT-TABLE. 05 PRODUCT-NAME OCCURS 50 TIMES PIC X(20). 01 SUBSCRIPT PIC 9(3) VALUE 1. PROCEDURE DIVISION. MAIN-PARA. *> Access using subscript MOVE 1 TO SUBSCRIPT MOVE 'WIDGET A' TO PRODUCT-NAME(SUBSCRIPT) ADD 1 TO SUBSCRIPT MOVE 'WIDGET B' TO PRODUCT-NAME(SUBSCRIPT) *> Display element MOVE 1 TO SUBSCRIPT DISPLAY "Product 1: " PRODUCT-NAME(SUBSCRIPT) STOP RUN.
Subscripts are numeric data items (PIC 9) used to access table elements. They use 1-based indexing (first element is at position 1).
1234567891011121314151617181920WORKING-STORAGE SECTION. 01 PRODUCT-TABLE. 05 PRODUCT-NAME OCCURS 50 TIMES INDEXED BY PROD-INDEX PIC X(20). PROCEDURE DIVISION. MAIN-PARA. *> Access using index SET PROD-INDEX TO 1 MOVE 'WIDGET A' TO PRODUCT-NAME(PROD-INDEX) SET PROD-INDEX UP BY 1 MOVE 'WIDGET B' TO PRODUCT-NAME(PROD-INDEX) *> Display element SET PROD-INDEX TO 1 DISPLAY "Product 1: " PRODUCT-NAME(PROD-INDEX) STOP RUN.
Indices are special data items defined with INDEXED BY. They use SET statements (not MOVE) and are more efficient than subscripts for large tables.
| Aspect | Subscript | Index |
|---|---|---|
| Definition | Regular numeric data item (PIC 9) | Defined with INDEXED BY clause |
| Assignment | MOVE value TO subscript | SET index TO value |
| Increment | ADD 1 TO subscript | SET index UP BY 1 |
| Efficiency | Less efficient for large tables | More efficient, optimized by compiler |
| Required for | General table access | SEARCH and SEARCH ALL statements |
| Operations | Can use arithmetic (ADD, SUBTRACT) | Use SET statements only |
Tables are typically processed using PERFORM loops:
12345678910111213141516171819WORKING-STORAGE SECTION. 01 SALES-TABLE. 05 SALES-AMOUNT OCCURS 100 TIMES INDEXED BY SALES-INDEX PIC 9(8)V99. 01 TOTAL-SALES PIC 9(10)V99 VALUE ZERO. 01 TABLE-SIZE PIC 9(3) VALUE 100. PROCEDURE DIVISION. MAIN-PARA. *> Process all table elements PERFORM VARYING SALES-INDEX FROM 1 BY 1 UNTIL SALES-INDEX > TABLE-SIZE ADD SALES-AMOUNT(SALES-INDEX) TO TOTAL-SALES END-PERFORM DISPLAY "Total Sales: " TOTAL-SALES STOP RUN.
This loop processes all elements in the table, summing the sales amounts.
123456789101112131415161718WORKING-STORAGE SECTION. 01 COUNTER-TABLE. 05 COUNTER-VALUE OCCURS 50 TIMES INDEXED BY CTR-INDEX PIC 9(5) VALUE ZERO. PROCEDURE DIVISION. MAIN-PARA. *> Initialize all elements to zero INITIALIZE COUNTER-TABLE *> Or initialize with specific values PERFORM VARYING CTR-INDEX FROM 1 BY 1 UNTIL CTR-INDEX > 50 MOVE CTR-INDEX TO COUNTER-VALUE(CTR-INDEX) END-PERFORM STOP RUN.
SEARCH performs a linear (sequential) search through a table:
1234567SET index TO starting-position SEARCH table-name [AT END imperative-statement] [WHEN condition-1 imperative-statement-1] [WHEN condition-2 imperative-statement-2] ... END-SEARCH.
SEARCH requires:
12345678910111213141516171819202122232425262728WORKING-STORAGE SECTION. 01 CUSTOMER-TABLE. 05 CUSTOMER-ENTRY OCCURS 100 TIMES INDEXED BY CUST-INDEX. 10 CUSTOMER-ID PIC 9(5). 10 CUSTOMER-NAME PIC X(30). 01 SEARCH-ID PIC 9(5) VALUE 12345. 01 FOUND-FLAG PIC X VALUE 'N'. 88 CUSTOMER-FOUND VALUE 'Y'. PROCEDURE DIVISION. MAIN-PARA. SET CUST-INDEX TO 1 SEARCH CUSTOMER-ENTRY AT END DISPLAY "Customer not found" MOVE 'N' TO FOUND-FLAG WHEN CUSTOMER-ID(CUST-INDEX) = SEARCH-ID DISPLAY "Customer found: " CUSTOMER-NAME(CUST-INDEX) MOVE 'Y' TO FOUND-FLAG END-SEARCH IF CUSTOMER-FOUND *> Process found customer END-IF STOP RUN.
SEARCH starts from the index position and searches sequentially until it finds a match or reaches the end of the table.
SEARCH ALL performs a binary search on a sorted table:
1234SEARCH ALL table-name [AT END imperative-statement] [WHEN condition imperative-statement] END-SEARCH.
SEARCH ALL requirements:
12345678910111213141516171819202122WORKING-STORAGE SECTION. 01 PRODUCT-TABLE. 05 PRODUCT-ENTRY OCCURS 1000 TIMES INDEXED BY PROD-INDEX. 10 PRODUCT-CODE PIC 9(5). 10 PRODUCT-NAME PIC X(30). 10 PRODUCT-PRICE PIC 9(6)V99. *> Table must be sorted by PRODUCT-CODE 01 SEARCH-CODE PIC 9(5) VALUE 54321. PROCEDURE DIVISION. MAIN-PARA. SEARCH ALL PRODUCT-ENTRY AT END DISPLAY "Product not found" WHEN PRODUCT-CODE(PROD-INDEX) = SEARCH-CODE DISPLAY "Product: " PRODUCT-NAME(PROD-INDEX) DISPLAY "Price: " PRODUCT-PRICE(PROD-INDEX) END-SEARCH STOP RUN.
SEARCH ALL is much faster than SEARCH for large sorted tables because it uses binary search, which eliminates half the remaining elements with each comparison.
| Aspect | SEARCH | SEARCH ALL |
|---|---|---|
| Search type | Linear (sequential) | Binary |
| Table requirement | Can be unsorted | Must be sorted |
| Speed | Slower for large tables | Faster for large tables |
| Starting position | Set index before SEARCH | Not needed (searches entire table) |
| Use when | Unsorted tables or finding first match | Large sorted tables |
| Efficiency | O(n) - checks each element | O(log n) - eliminates half each time |
Dynamic tables allow variable sizes at runtime using DEPENDING ON:
1234level-number data-name OCCURS 1 TO max-size TIMES DEPENDING ON size-field [INDEXED BY index-name].
123456789101112131415161718192021222324WORKING-STORAGE SECTION. 01 TABLE-SIZE PIC 9(4) VALUE 100. 01 CUSTOMER-TABLE. 05 CUSTOMER-ENTRY OCCURS 1 TO 1000 TIMES DEPENDING ON TABLE-SIZE INDEXED BY CUST-INDEX. 10 CUSTOMER-ID PIC 9(5). 10 CUSTOMER-NAME PIC X(30). PROCEDURE DIVISION. MAIN-PARA. *> Set table size MOVE 50 TO TABLE-SIZE *> Process only the active portion PERFORM VARYING CUST-INDEX FROM 1 BY 1 UNTIL CUST-INDEX > TABLE-SIZE *> Process CUSTOMER-ENTRY(CUST-INDEX) END-PERFORM *> Resize table ADD 25 TO TABLE-SIZE STOP RUN.
Dynamic tables provide flexibility when the number of elements is not known at compile time. The size-field determines how many elements are actually used.
COBOL supports multi-dimensional tables (tables of tables):
1234567891011121314151617WORKING-STORAGE SECTION. 01 SALES-TABLE. 05 REGION-ENTRY OCCURS 10 TIMES INDEXED BY REGION-INDEX. 10 REGION-NAME PIC X(20). 10 MONTH-SALES OCCURS 12 TIMES INDEXED BY MONTH-INDEX PIC 9(8)V99. PROCEDURE DIVISION. MAIN-PARA. *> Access sales for region 3, month 6 SET REGION-INDEX TO 3 SET MONTH-INDEX TO 6 DISPLAY "Sales: " MONTH-SALES(REGION-INDEX, MONTH-INDEX) STOP RUN.
This creates a two-dimensional table: 10 regions, each with 12 months of sales data.
Follow these best practices:
12345678910111213*> State code lookup table 01 STATE-TABLE. 05 STATE-ENTRY OCCURS 50 TIMES INDEXED BY STATE-INDEX. 10 STATE-CODE PIC X(2). 10 STATE-NAME PIC X(20). *> Search for state name by code SET STATE-INDEX TO 1 SEARCH STATE-ENTRY WHEN STATE-CODE(STATE-INDEX) = SEARCH-CODE MOVE STATE-NAME(STATE-INDEX) TO RESULT-NAME END-SEARCH
123456789*> Accumulate totals by category 01 CATEGORY-TOTALS. 05 CATEGORY-AMOUNT OCCURS 20 TIMES INDEXED BY CAT-INDEX PIC 9(10)V99 VALUE ZERO. *> Add to category total SET CAT-INDEX TO CATEGORY-NUMBER ADD TRANSACTION-AMOUNT TO CATEGORY-AMOUNT(CAT-INDEX)
12345*> Process all table elements PERFORM VARYING TABLE-INDEX FROM 1 BY 1 UNTIL TABLE-INDEX > TABLE-SIZE *> Process ELEMENT(TABLE-INDEX) END-PERFORM
Think of a table like a row of mailboxes:
So table handling is like organizing and finding things in a row of mailboxes - you put things in numbered boxes and use the numbers to find them later!
Complete these exercises to reinforce your understanding:
Create a table of 10 product names. Initialize all elements, then display each product name using a PERFORM loop.
Create a customer table with ID and name. Use SEARCH to find a customer by ID and display their name.
Create a sorted product table (sorted by product code). Use SEARCH ALL to find a product by code and display its price.
Create a dynamic table that can hold 1 to 100 items. Read a number of items, set the table size, then process only that many items.
Create a two-dimensional table representing sales by region (5 regions) and month (12 months). Calculate and display total sales for each region.
1. What clause defines a table in COBOL?
2. What is required for SEARCH and SEARCH ALL statements?
3. What is the difference between SEARCH and SEARCH ALL?
4. How do you set an index value in COBOL?
5. What makes a table dynamic in COBOL?
6. What level can OCCURS be used at?