Welcome back. In our previous lesson, we explored how to refactor monolithic programs into modular, reusable subprograms—a foundational skill for modernizing legacy systems. Now, we'll apply that mindset of using the right tool for the right job to another classic mainframe challenge, one that frequently appears in senior developer interviews.
Today's learning outcome is to solve a scenario: Reconcile two large sequential files with mismatched record formats and keys. This task moves beyond simple file reading and writing into the realm of data integration and integrity. You'll learn to design a robust, high-performance batch process that can compare massive datasets, even when they don't neatly align. This is a critical capability for everything from data migration and system-to-system reconciliation to generating audit reports.
The Classic COBOL File Matching Algorithm
For any developer with your experience, the traditional approach to file matching in COBOL is likely familiar. It involves reading from two pre-sorted sequential files in a synchronized loop and comparing their keys to decide which record to process next.
The core logic, often implemented within a PERFORM UNTIL loop, looks something like this:
- Read one record from File 1.
- Read one record from File 2.
- Compare the keys of the two current records.
- If Key 1 < Key 2: The record from File 1 is unique. Process it, then read the next record from File 1 only.
- If Key 1 > Key 2: The record from File 2 is unique. Process it, then read the next record from File 2 only.
- If Key 1 = Key 2: The records match. Process the match, then read the next record from both files.
- Repeat until one or both files reach their end. Handle the remaining records in the file that is not yet at its end.
The following flowchart illustrates this fundamental process.

Let's look at how this logic is implemented in code.
Mainframe Forum: COBOL File Matching Tutorial: A Comprehensive Guide. #COBOL
This article from Mainframe Forum provides a clear code snippet for the traditional file matching algorithm.
Focus on the code example provided. Pay particular attention to the paragraph D000-PROCESS-RECDS. Notice how the EVALUATE statement implements the key comparison logic we just discussed.
Limitations of the Classic Approach
While this COBOL-centric approach is perfectly valid and clear, it has a significant drawback when dealing with the "large sequential files" specified in our scenario. The process is I/O-bound, meaning its performance is limited by the speed of reading from disk. For files with millions of records, a program that performs a READ for nearly every record in both files can be inefficient and consume substantial CPU time and I/O resources.
Furthermore, this simple logic assumes the keys and record layouts are identical. Handling mismatched formats and keys within this single COBOL program would require complex conditional logic, making the code harder to write, debug, and maintain. As a senior developer, your goal is to find a more efficient and elegant solution.
A More Powerful Tool: DFSORT's JOINKEYS
For high-volume file reconciliation on the mainframe, the industry-standard tool is DFSORT (or a compatible sort utility). Its JOINKEYS feature is specifically designed to perform high-speed, key-based comparisons and joins on large files, offloading the heavy I/O and comparison work from your COBOL program.
The strategy shifts from doing everything in COBOL to a two-stage process:
- Use
DFSORTto handle the comparison, key matching, and reformatting. - Use COBOL to process the clean, pre-processed output from
DFSORT.
This division of labor leverages the strengths of each tool: DFSORT for raw data manipulation speed and COBOL for implementing complex business rules.
Let's dive into how JOINKEYS works.
File Comparison with DFSORT - Compare Two Datasets - MainframeMaster
This excellent guide from MainframeMaster details the JOINKEYS feature for file comparison.
Read through the following sections to build a solid understanding: The introduction, which frames the problem. The section on key-based comparison, which explains the core mechanism. The overview of UNPAIRED options, which is crucial for identifying adds, deletes, and matches. The discussion on ensuring files are sorted. This is a critical prerequisite. Finally, review the example control statements. This shows you how the pieces fit together.
Solving the "Mismatched" Scenario
Now we can address the specific complexities of our scenario—mismatched formats and keys—using the power of JOINKEYS.
Handling Mismatched Keys
The problem states our files have mismatched keys. This could mean several things, all of which JOINKEYS can handle.
-
Keys are in different positions: This is the simplest case. The
FIELDSparameter on theJOINKEYSstatement allows you to specify the key's position, length, and format independently for each file.//SYSIN DD * JOINKEYS F1=IN1,FIELDS=(10,8,CH,A) // Key starts at pos 10 in File 1 JOINKEYS F2=IN2,FIELDS=(5,8,CH,A) // Key starts at pos 5 in File 2 ... /* -
Keys have different formats (but represent the same value): Imagine one file stores an account number as
PIC X(8)and the other asPIC 9(8) PACKED-DECIMAL(4 bytes). You cannot join them directly. The solution is a pre-processing step. You would useDFSORT'sINRECorOUTRECstatement in a prior step to reformat one of the keys so they match.For example, you could convert the packed decimal key to a character representation before the join step.
-
Keys are composite or need derivation: If a key is made of multiple, non-contiguous fields, you can use
DFSORT'sINRECstatement to build a new, contiguous key at the beginning of the record in a preliminary step. Both files are pre-processed this way, andJOINKEYSthen operates on the standardized key.
Handling Mismatched Record Formats
This is where the REFORMAT statement in JOINKEYS becomes invaluable. It allows you to construct a new output record by cherry-picking fields from both input files.
When a match is found, you have access to the record from F1 and the record from F2. In the REFORMAT statement, you can create a new, consolidated record layout.
For example, let's say File 1 has CUST-ID and CUST-ADDRESS, and File 2 has CUST-ID and CUST-LAST-ORDER-DATE. You can create a single output record containing all three fields.
//SYSIN DD *
JOINKEYS F1=CUSTMAST,FIELDS=(1,10,A)
JOINKEYS F2=ORDHIST,FIELDS=(1,10,A)
// Create a new record from the matched pair
REFORMAT FIELDS=(F1:1,10, // CUST-ID from File 1
F1:11,50, // CUST-ADDRESS from File 1
F2:11,8) // CUST-LAST-ORDER-DATE from File 2
JOIN UNPAIRED,F,F // Process all records
SORT FIELDS=COPY
// Split output into Adds, Deletes, and a consolidated "Changes" file
OUTFIL FNAMES=ADDS,INCLUDE=(1,10,SS,EQ,C'??????????')
OUTFIL FNAMES=DELETES,INCLUDE=(61,8,SS,EQ,C'????????')
OUTFIL FNAMES=CHANGES,SAVE
/*
In the OUTFIL statements above, ?????????? is DFSORT's default placeholder for a key field from a file that did not participate in an unpaired match. By checking for this placeholder, you can route records to different output datasets. Unmatched F1 records go to DELETES, unmatched F2 records go to ADDS, and all others (the matched pairs) are SAVEd to the CHANGES file.
Architecting the Complete Solution
A robust, production-quality solution would be a multi-step JCL job:
- STEP010 - SORTF1: Sort the first input file on its reconciliation key.
- STEP020 - SORTF2: Sort the second input file on its reconciliation key. (These steps can be omitted if the files are guaranteed to be in the correct sequence).
- STEP030 - RECONCILE: Use
PGM=SORTwithJOINKEYScontrol cards.- Define the
JOINKEYSforF1andF2with their respective key definitions. - Use
REFORMATto define the layout of the output record, combining fields as needed. - Use
JOIN UNPAIRED,F,Fto process every record. - Use multiple
OUTFILstatements to create separate output files for:- Records only in File 1 ("deletes" or "unmatched master").
- Records only in File 2 ("adds" or "unmatched transaction").
- Matched records, now in a single, consistent format.
- Define the
- STEP040 - PROCESS: A COBOL program that reads one or more of the output files from STEP030 to apply final business logic (e.g., generate a formatted report, create database update records). This COBOL program is now much simpler because the complex task of synchronization and reformatting has already been done.
This hybrid DFSORT + COBOL architecture is a powerful pattern. It demonstrates a senior developer's ability to select the most efficient tool for each part of a problem, leading to solutions that are faster, more scalable, and often easier to maintain than a pure COBOL approach.
Conclusion
In this lesson, we tackled a complex data reconciliation scenario. We moved from the traditional COBOL file-matching algorithm to a far more powerful and efficient architecture using DFSORT's JOINKEYS feature. This approach is the professional standard for handling large-scale file comparisons on the mainframe.
Key Takeaways:
- Classic vs. Modern: While the classic COBOL algorithm is fundamental, it is not performant for large files.
- Use the Right Tool:
DFSORTis optimized for high-speed I/O and data manipulation. Offload the heavy lifting of comparison and reformatting toDFSORT. JOINKEYSis Your Friend: It handles the core logic of finding adds, deletes, and matches between two pre-sorted files.- Solve Mismatches with
JOINKEYSandREFORMAT: Define keys independently for each file using theFIELDSparameter, and create a unified output record layout using theREFORMATstatement. - Architect a Multi-Step Solution: A complete solution typically involves JCL with steps for sorting,
JOINKEYSreconciliation, and final COBOL-based business rule processing.
In our next lesson, we will continue exploring complex file processing by tackling another common scenario: how to process a multi-record-type file with internal sorting and control break logic.