Teach Yourself COBOL in 21 days, Second Edition

Previous chapterNext chapterContents


- Day 17 -
Alternate Keys

On Day 11, "Indexed File I/O," you learned that it is possible to set up more than one key in a file, in order to retrieve records in a different order than they were entered. You saw examples of how to retrieve in alphabetical and numerical order, and today's lesson teaches you the specifics of completing this task. You also learn about the following topics:

What Is an Alternate Key?

Although an indexed file must have a unique primary key, it is possible to put other keys in the file. These keys do not have to be unique and can be arranged to return records in a different order than the primary key. Alternate keys are useful for retrieving records in alphabetical order by name, in zip code order, or any of a variety of other sort orders. A sales file could include an alternate key on the customer territory to group information in territory order. An employee file could contain an alternate key on the employment date to provide a sort on seniority of the employee.

Today, you use one of these alternate keys to provide a method of accessing the vendor file in alphabetical vendor name order.

New Term: An alternate index or alternate key is an additional index in an indexed file that defines an alternative path along which records can be retrieved. The term path is used because when you use an alternate index to read records one after the other in a file, the records are read in a different sequence than they would be read using a standard READ NEXT.

An indexed file containing alternate indexes still must have a primary key, but it usually is called an alternate indexed file to indicate the existence of the extra keys.

An alternate indexed file is defined in the same way as an indexed file, but some additional information appears in the SELECT statement for the file.

Listing 17.1 is a COPY file containing an FD for an alternate indexed file. Note that it is no different from earlier versions of the FD for the vendor file, except that it includes a comment that VENDOR-NAME is an alternate key with duplicates. The comment does not create an alternate key, and is only there for reference.

TYPE: Listing 17.1. The FD for an alternate indexed file.

000100*--------------------------------
000200* FDVND04.CBL
000300* Primary Key - VENDOR-NUMBER
000400* Alternate - NAME with duplicates
000500
000600* NAME, ADDRESS-1, CITY, STATE,
000700*   and PHONE are required fields.
000800*
000900* VENDOR-STATE must be looked up
001000*   and must exist in the STATE-FILE
001100*   to be valid.
001200* VENDOR-ADDRESS-2 not always used
001300*   so may be SPACES
001400* VENDOR-PHONE is usually the
001500*   number for VENDOR-CONTACT
001600* All fields should be entered in
001700*   UPPER case.
001800*--------------------------------
001900 FD  VENDOR-FILE
002000     LABEL RECORDS ARE STANDARD.
002100 01  VENDOR-RECORD.
002200     05  VENDOR-NUMBER            PIC 9(5).
002300     05  VENDOR-NAME              PIC X(30).
002400     05  VENDOR-ADDRESS-1         PIC X(30).
002500     05  VENDOR-ADDRESS-2         PIC X(30).
002600     05  VENDOR-CITY              PIC X(20).
002700     05  VENDOR-STATE             PIC X(2).
002800     05  VENDOR-ZIP               PIC X(10).
002900     05  VENDOR-CONTACT           PIC X(30).
003000     05  VENDOR-PHONE             PIC X(15).
003100

Listing 17.2 is a COPY file containing a SELECT statement for an alternate indexed file, and this is where the alternate key is actually defined.

TYPE: Listing 17.2. The SELECT for an alternate indexed file.

000100*--------------------------------
000200* SLVND02.CBL
000300*--------------------------------
000400     SELECT VENDOR-FILE
000500         ASSIGN TO "vendor"
000600         ORGANIZATION IS INDEXED
000700         RECORD KEY IS VENDOR-NUMBER
000800         ALTERNATE KEY
000900            IS VENDOR-NAME WITH DUPLICATES
001000         ACCESS MODE IS DYNAMIC.
001100

ANALYSIS: At lines 000800 and 000900, the alternate key is identified as VENDOR-NAME. The WITH DUPLICATES clause on line 000900 indicates that more than one record can exist with the same value in this field. Allowing duplicates in an alternate key field for names is a standard practice because names can be duplicated. If the WITH DUPLICATES clause is omitted, VENDOR-NAME tends to act like a second primary key on the file, and it forces each value in the key to be unique.

If an alternate indexed file is defined with an alternate key in which duplicates are not allowed, attempting to write a record with a key that already is on file has the same effect as attempting to write a file with a duplicate primary key. An INVALID KEY file error condition occurs because a record with that key value already is on file.

Creating a File with Alternate Indexes

Adding the definition in a SELECT statement does not by itself make an alternate indexed file. The file must have been created (opened in output mode) by a program containing that definition for the file.

After the file is created, you can treat it like any other indexed file. Records written to it will be stored correctly with the alternate index values.

Opening a file in output mode destroys the original file if one exists. This presents a problem. You already have created a vendor file without an alternate index and put several records in it. Now you need a new file with the alternate key included. It is possible to convert the original file without losing the data by using the records in the original file and writing them to the new file that has been created.

The method used for this is a program that opens the original file for reading, opens the new file for output, and then copies records from the old file to the new file.

What Is in a Data File?

Recall that when using a COBOL program to open a file, I said that the file must be opened with the same file definition that was used to create it. Now that you are more familiar with COBOL, I am going to change that statement.

In order to sort this out, you need to delve into the inner workings of an indexed file. STATE-FILE is used in this example because the records are shorter and easier to display, but the principle is the same for the vendor file.

What's in a data file? Regardless of whether the file is indexed, the primary content of a data file is data. Figure 17.1 represents the state code file containing five records. The figure also includes a ruler for the 22 bytes in each record.

Figure 17.1.
Five records in a state codes file.

The file contains five records of 22 bytes each. The first two bytes of each record is the two-character code for that state. The remaining 20 bytes contain the state name followed by spaces to the end of the record.

An indexed file contains the data records that have been stored in the file, but it also contains additional information. This additional data is information about the file organization, record length, primary key position and length, and any alternate keys. This information is used internally by the COBOL file system but never shows up as data when reading a file through a COBOL program.

Exactly where this information is stored in the file isn't important as long as you know that it is stored there. Figure 17.2 shows this data to indicate that it is in the file, but not as a data record.

Figure 17.2.
Five records in an indexed state codes file.

With this in mind, it is possible to clarify my earlier statement about opening a file. A file must be opened with a file definition that matches the file organization, record length, and key structure of the actual file. Usually, this is the file definition that was used to create it, but it does not have to be if it matches the file organization, record length, and key structure of that file definition.

Note that the COBOL name for the file (STATE-FILE) and the fields in the record (STATE-CODE, STATE-NAME, and so on) are not actually stored in the data file. They are stored in the COBOL program.

Listings 17.3 and 17.4 are the original SELECT and FD for the state code file.

TYPE: Listing 17.3. The original SELECT for the state code file.

000100*--------------------------------
000200* SLSTATE.CBL
000300*--------------------------------
000400     SELECT STATE-FILE
000500         ASSIGN TO "STATE"
000600         ORGANIZATION IS INDEXED
000700         RECORD KEY IS STATE-CODE
000800         ACCESS MODE IS DYNAMIC.
000900

TYPE: Listing 17.4. The original FD for the state code file.

000100*--------------------------------
000200* FDSTATE.CBL
000300* Primary Key - STATE-CODE
000400* NAME is required
000500* NAME and CODE should be upper case
000600*--------------------------------
000700 FD  STATE-FILE
000800     LABEL RECORDS ARE STANDARD.
000900 01  STATE-RECORD.
001000     05  STATE-CODE               PIC X(2).
001100     05  STATE-NAME               PIC X(20).
001200

Listings 17.5 and 17.6 are another SELECT and FD that also could be used to access the same state code file. Although the data names have been changed, the file organization, record length, and key structure are the same in both definitions.

TYPE: Listing 17.5. A SELECT that will work for the state code file.

000400     SELECT ST-FILE
000500         ASSIGN TO "STATE"
000600         ORGANIZATION IS INDEXED
000700         RECORD KEY IS ST-CODE
000800         ACCESS MODE IS DYNAMIC.
000900

TYPE: Listing 17.6. An FD that will work for the state code file.

000700 FD  ST-FILE
000800     LABEL RECORDS ARE STANDARD.
000900 01  ST-RECORD.
001000     05  ST-CODE               PIC X(2).
001100     05  ST-NAME               PIC X(20).
001200

If you used Listings 17.5 and 17.6 in a program, you would use the data names from these listings to access the file. When you open the file, you would OPEN I-O ST-FILE. To write the record, you would use WRITE ST-RECORD, but the file would still be accessed correctly.

Listings 17.7 and 17.8 are an extreme (and possibly silly) example of changing things around. The record originally was defined as two fields. What was STATE-CODE is now named THE-KEY, and the 20-character field that was called STATE-NAME is broken up arbitrarily into three smaller fields. Even with the changes, the file organization, record length, and key structure remain the same. This pair of file definitions could be used to open and process the state code file. Of course, using FIRST-5-OF-NAME for anything represents only the first five characters of the original STATE-NAME. 2ND-5-OF-NAME or LAST-10-OF-NAME represents other portions of the original STATE-NAME. It is useless to do something like this, but this extreme example illustrates the point very well.

TYPE: Listing 17.7. Another SELECT statement that would work.

000400     SELECT THE-FILE
000500         ASSIGN TO "STATE"
000600         ORGANIZATION IS INDEXED
000700         RECORD KEY IS THE-KEY
000800         ACCESS MODE IS DYNAMIC.
000900

TYPE: Listing 17.8. An extreme example of changing the FD.

000700 FD  THE-FILE
000800     LABEL RECORDS ARE STANDARD.
000900 01  THE-RECORD.
001000     05  THE-KEY                PIC X(2).
001100     05  FIRST-5-OF-NAME        PIC X(5).
001200     05  2ND-5-OF-NAME          PIC X(5).
001300     05  LAST-10-OF-NAME        PIC X(10).

There are times when a redefinition of the fields in a record can be useful. Suppose that one program that uses the STATE-FILE calculates shipping charges based on whether the shipping address is within the continental United States. The program currently has logic such as that shown in Listing 17.9. It is not a good practice to hard-code special conditions like this, but you will find it in programs that you are asked to modify all the time.

TYPE: Listing 17.9. The wrong way to test for shipping charges.

021000* Add extra shipping for Alaska and Hawaii
021100     IF STATE-CODE = "HI" OR STATE-CODE = "AK"
021200         PERFORM CHARGE-EXTRA-SHIPPING
021300     ELSE
021400         PERFORM CHARGE-REGULAR-SHIPPING.

This code keeps working until the business expands and you need to include state codes for U.S. territories and Canadian provinces. There are just too many of these to hard-code into the program.

By inspecting the STATE-FILE you find that none of the states uses the full 20 characters of STATE-NAME, and in every record, the 20th character of the STATE-NAME is a space.

A new FD could be defined that separates out the 20th character to use it as a continental flag as in Listing 17.10.

TYPE: Listing 17.10. Adding a continental flag to the STATE-FILE.

000100*--------------------------------
000200* FDSTATC.CBL
000300* Primary Key - STATE-CODE
000400* NAME is required
000500* NAME and CODE should be upper case
000600*--------------------------------
000700 FD  STATE-FILE
000800     LABEL RECORDS ARE STANDARD.
000900 01  STATE-RECORD.
001000     05  STATE-CODE               PIC X(2).
001100     05  STATE-NAME               PIC X(19).
001200     05  STATE-CONTINENTAL        PIC X(1).

After setting the flag correctly to Y or N for each state, you could test the flag to determine shipping charges as in Listing 17.11.

TYPE: Listing 17.11. The right way to test for shipping charges.

021000* Add extra shipping for non-continental US
021100     IF STATE-CONTINENTAL = "N"
021200         PERFORM CHARGE-EXTRA-SHIPPING
021300     ELSE
021400         PERFORM CHARGE-REGULAR-SHIPPING.

This method of allocating unused characters in existing fields for new fields is a very common practice.

File and data conversions such as adding STATE-CONTINENTAL, and the following conversion of the VENDOR-FILE are very typical tasks that might be assigned to a junior programmer. Being able to do conversions is one way you will be able to perform useful (and paid) work.

Creating New Files from Old Files

You have several things to deal with before you can create a new vendor file out of the existing one. The data names in a program must not be identical, so it is necessary to create a temporary SELECT and FD that can be used to open the original file, but use different data names.

Another problem has to do with the way files are stored on a disk. Two files cannot have the same physical name on a disk. The new file will be called "vendor", so for the purpose of conversion, the old file is renamed "ovendor".

Under MS-DOS, enter the following:

DIR vendor*

If you are using Micro Focus Personal COBOL, two files are named vendor: VENDOR and VENDOR.IDX. Under ACUCOBOL, one file is named VENDOR. Under other COBOL compilers, two files might be named VENDOR.DAT and VENDOR.IDX. If your system uses two files, both of them must be renamed.

Enter one or more of these commands, depending on the filenames:

RENAME VENDOR OVENDOR
RENAME VENDOR.IDX OVENDOR.IDX
RENAME VENDOR.DAT OVENDOR.DAT

Listing 17.12, fdovnd01.cbl, is a COPY file of an FD that is created by copying fdvnd03.cbl to fdovnd01.cbl and then changing each data name that starts with VENDOR- so that it starts with OLD-VENDOR-.

Listing 17.13, slvnd01.cbl, is a COPY file of a SELECT statement that is created in a similar way, by copying slvnd01.cbl to slovnd01.cbl and then changing each data name that starts with VENDOR- so that it starts with OLD-VENDOR-.

Create these two files and modify each of them to match Listings 17.12 and 17.13.

TYPE: Listing 17.12. The FD for the original vendor file.

000100*--------------------------------
000200* FDOVND01.CBL
000300* Primary Key - OLD-VENDOR-NUMBER
000400* Original before alt path added
000500* NAME, ADDRESS-1, CITY, STATE,
000600*   and PHONE are required fields.
000700*
000800* OLD-VENDOR-STATE must be looked up
000900*   and must exist in the STATE-FILE
001000*   to be valid.
001100* OLD-VENDOR-ADDRESS-2 not always used
001200*   so may be SPACES
001300* OLD-VENDOR-PHONE is usually the
001400*   number for OLD-VENDOR-CONTACT
001500* All fields should be entered in
001600*   UPPER case.
001700*--------------------------------
001800 FD  OLD-VENDOR-FILE
001900     LABEL RECORDS ARE STANDARD.
002000 01  OLD-VENDOR-RECORD.
002100     05  OLD-VENDOR-NUMBER            PIC 9(5).
002200     05  OLD-VENDOR-NAME              PIC X(30).
002300     05  OLD-VENDOR-ADDRESS-1         PIC X(30).
002400     05  OLD-VENDOR-ADDRESS-2         PIC X(30).
002500     05  OLD-VENDOR-CITY              PIC X(20).
002600     05  OLD-VENDOR-STATE             PIC X(2).
002700     05  OLD-VENDOR-ZIP               PIC X(10).
002800     05  OLD-VENDOR-CONTACT           PIC X(30).
002900     05  OLD-VENDOR-PHONE             PIC X(15).
003000

For the SELECT statement, be sure to change line 000500 to read ASSIGN TO "ovendor".

TYPE: Listing 17.13. The SELECT for the original vendor file.

000100*--------------------------------
000200* SLOVND01.CBL
000300*--------------------------------
000400     SELECT OLD-VENDOR-FILE
000500         ASSIGN TO "ovendor"
000600         ORGANIZATION IS INDEXED
000700         RECORD KEY IS OLD-VENDOR-NUMBER
000800         ACCESS MODE IS DYNAMIC.

After these new file definitions are created, it is possible to write a program that converts the old file to the new by reading each record in the old file and writing it to the new file (shown in Listing 17.14).

TYPE: Listing 17.14. Creating the new alternate indexed file.

000100 IDENTIFICATION DIVISION.
000200 PROGRAM-ID. NEWVND01.
000300*------------------------------------------------
000400* Create new Vendor File with Alt key from old.
000500*------------------------------------------------
000600 ENVIRONMENT DIVISION.
000700 INPUT-OUTPUT SECTION.
000800 FILE-CONTROL.
000900
001000     COPY "SLOVND01.CBL".
001100
001200     COPY "SLVND02.CBL".
001300 DATA DIVISION.
001400 FILE SECTION.
001500
001600     COPY "FDOVND01.CBL".
001700
001800     COPY "FDVND04.CBL".
001900
002000 WORKING-STORAGE SECTION.
002100
002200 77  OLD-VENDOR-FILE-AT-END   PIC X VALUE "N".
002300
002400 PROCEDURE DIVISION.
002500 PROGRAM-BEGIN.
002600     PERFORM OPENING-PROCEDURE.
002700     PERFORM MAIN-PROCESS.
002800     PERFORM CLOSING-PROCEDURE.
002900
003000 PROGRAM-DONE.
003100     STOP RUN.
003200
003300 OPENING-PROCEDURE.
003400     OPEN OUTPUT VENDOR-FILE.
003500     OPEN I-O OLD-VENDOR-FILE.
003600
003700 CLOSING-PROCEDURE.
003800     CLOSE VENDOR-FILE.
003900     CLOSE OLD-VENDOR-FILE.
004000
004100 MAIN-PROCESS.
004200     PERFORM READ-NEXT-OLD-VENDOR-RECORD.
004300     PERFORM PROCESS-ONE-RECORD
004400         UNTIL OLD-VENDOR-FILE-AT-END = "Y".
004500
004600 READ-NEXT-OLD-VENDOR-RECORD.
004700     MOVE "N" TO OLD-VENDOR-FILE-AT-END.
004800     READ OLD-VENDOR-FILE NEXT RECORD
004900         AT END
005000         MOVE "Y" TO OLD-VENDOR-FILE-AT-END.
005100
005200 PROCESS-ONE-RECORD.
005300     MOVE OLD-VENDOR-RECORD TO VENDOR-RECORD.
005400     PERFORM WRITE-VENDOR-RECORD.
005500
005600     PERFORM READ-NEXT-OLD-VENDOR-RECORD.
005700
005800 WRITE-VENDOR-RECORD.
005900     WRITE VENDOR-RECORD
006000         INVALID KEY
006100         DISPLAY "ERROR WRITING VENDOR RECORD".
006200
006300

ANALYSIS: Listing 17.14 is a fairly straightforward program. In the OPENING-PROCEDURE at line 003300, it opens the VENDOR-FILE in output mode. This creates the new vendor file with the alternate index. It then opens OLD-VENDOR-FILE (ovendor) in I/O mode.

The loop control in MAIN-PROCESS at line 004100 follows the procedure of reading the first old vendor record and then performing the processing loop (PROCESS-ONE-RECORD) until OLD-VENDOR-FILE-AT-END = "Y".

The processing loop, PROCESS-ONE-RECORD at line 005200, moves the OLD-VENDOR-RECORD to VENDOR-RECORD, writes the new record, and then at the end of the loop at line 005600, reads the next old vendor record.

Code and compile this program. Be sure that you have renamed the vendor file to "ovendor" before you run it. The new file named "vendor" will contain an alternate index. You won't notice any difference in the file yet, although the file might be larger on the disk.

Maintaining a File with Alternate Keys

You have to modify the maintenance program for the vendor file, vndmnt02.cbl, to allow for the new key in the file. The maintenance for a file with alternate keys can be identical to the maintenance for a standard indexed file. To modify vndmnt02.cbl so that it will maintain the new vendor file, copy it to vndmnt03.cbl. Then make the changes at lines 001100 and 001800 of Listing 17.15, a fragment of vndmnd03.cbl, to include the new SELECT and FD for the vendor file.

TYPE: Listing 17.15. Maintaining a file with alternate keys.

000100 IDENTIFICATION DIVISION.
000200 PROGRAM-ID. VNDMNT03.
000300*--------------------------------
000400* Add, Change, Inquire and Delete
000500* for the Vendor File.
000600*--------------------------------
000700 ENVIRONMENT DIVISION.
000800 INPUT-OUTPUT SECTION.
000900 FILE-CONTROL.
001000
001100     COPY "SLVND02.CBL".
001200
001300     COPY "SLSTATE.CBL".
001400
001500 DATA DIVISION.
001600 FILE SECTION.
001700
001800     COPY "FDVND04.CBL".
001900
002000     COPY "FDSTATE.CBL".
002100

Make the changes and compile the program so that you have a maintenance program for the new vendor file.

What Is a Key Path?

Listing 17.16 illustrates a customer record with a primary key of CUSTOMER-NUMBER. This is a fairly straightforward record layout, except that the phone number has been broken down additionally into area code, prefix, and number.

TYPE: Listing 17.16. A customer record.

001000 01  CUSTOMER-RECORD.
001100     05  CUSTOMER-NUMBER          PIC 9(5).
001200     05  CUSTOMER-NAME            PIC X(20).
001300     05  CUSTOMER-ADDRESS-1       PIC X(20).
001400     05  CUSTOMER-ADDRESS-2       PIC X(20).
001500     05  CUSTOMER-ZIP             PIC 9(5).
001600     05  CUSTOMER-PHONE.
001700         10  CUSTOMER-AREA-CODE   PIC 9(3).
001800         10  CUSTOMER-PREFIX      PIC 9(3).
001900         10  CUSTOMER-PHONE-NO    PIC 9(4).

A field can be named as an alternate index so that, as each record is added to the file, information also is stored in the alternate index area of the file to indicate the correct order of records for that alternate index.

If you displayed the records in a customer file in primary key order, the file might look somewhat haphazard. This would be reading and displaying the file along the primary key path.

If you request that the name field be used as an index, and that records be presented to you in the order of the name index, they arrive in the order shown in Figure 17.3, along the CUSTOMER-NAME key path.

Figure 17.3.
Records sorted on the name field.

If you also request that the zip code and area code be used as an index for the records, you can have the records arrive in zip code order (as in Figure 17.4) along the CUSTOMER-ZIP key path, or in area-code order (as in Figure 17.5) along the CUSTOMER-AREA-CODE key path.

Figure 17.4.
Records sorted on the zip code field.

Figure 17.5.
Records sorted on the area code field.

In all the examples, the alternate indexed fields were fields in which duplicates might, and in fact do, occur. Amphora Inc. and Malio Bros. both have the same zip code and area code. In a large customer base, it is possible that customer names would be duplicated.

New Term: The key path is the natural sorted order of records as they are read from the file using a particular key.

Listing 17.17 shows an example of the syntax that is used to define a file such as the customer file with multiple keys.

TYPE: Listing 17.17. The SELECT with multiple alternate keys.

000400     SELECT CUSTOMER-FILE
000500         ASSIGN TO "CUSTOMER"
000600         ORGANIZATION IS INDEXED
000700         RECORD KEY IS CUSTOMER-NUMBER
000800         ALTERNATE KEY IS CUSTOMER-NAME WITH DUPLICATES
000900         ALTERNATE KEY IS CUSTOMER-ZIP WITH DUPLICATES
001000         ALTERNATE KEY IS CUSTOMER-AREA-CODE WITH DUPLICATES
001100         ACCESS MODE IS DYNAMIC.
001200

Accessing a File on Key Paths

When an alternate indexed file is opened, the key path is set to the primary key. If you READ NEXT through the file, the records will be returned in the order of the primary key. COBOL includes a command that enables you to change the key path--the START command.

The first step to using a START command is to move a value to the field that will be the new key. In the case of the vendor file, the alternate key is the VENDOR-NAME. To set the file up to be able to read all records in VENDOR-NAME order, move the lowest possible value to the VENDOR-NAME (MOVE SPACES TO VENDOR-NAME). After the key is set up, you are ready for the second step. Start the file using the START command.

Listing 17.18 is an example of starting on a new key. What you want to do is start the file at the first record that contains a VENDOR-NAME that is greater than or equal to the spaces that are now in VENDOR-NAME. It seems that you should be able to say START GREATER THAN OR EQUAL. You can under the new COBOL-85 standard, but before that option was added you were limited to a little more awkward syntax. It uses the complement of greater than or equal to which is NOT LESS THAN. In this example, NOT < (NOT LESS THAN) is used to start at the first record whose VENDOR-NAME is equal to or greater than spaces.

TYPE: Listing 17.18. Starting on the first record of a new key.

020000     MOVE SPACE TO VENDOR-NAME.
020100     START VENDOR-FILE
020200        KEY NOT < VENDOR-NAME
020300         INVALID KEY
020400          MOVE "Y" TO FILE-AT-END.

You can do some interesting positioning tricks using the START command. In Listing 17.19, the key is initialized to an A followed by all Zs, and the start is GREATER THAN the key value. This causes the start to position the file at the first record with a VENDOR-NAME greater than "AZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ", which probably would be the first vendor name that started with B.

TYPE: Listing 17.19. Starting partly through the key path.

020000     MOVE "AZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ" TO VENDOR-NAME.
020100     START VENDOR-FILE
020200        KEY > VENDOR-NAME
020300         INVALID KEY
020400          MOVE "Y" TO FILE-AT-END.

The START command originally supported three conditions for determining the starting position, EQUALS, GREATER THAN, and NOT LESS THAN. The COBOL-85 standard added GREATER THAN OR EQUAL TO ( >= ). Some vendors have extended these to include positioning for LESS THAN, NOT GREATER THAN, and LESS THAN OR EQUAL TO. Here is the syntax for the standard three and the COBOL-85 option:

START file name
 KEY NOT < key name
  [ INVALID KEY
    do something ]
START file name
 KEY > key name
  [ INVALID KEY
    do something ]
START file name
 KEY EQUALS key name
  [ INVALID KEY
    do something ]
START file name
 KEY GREATER THAN OR EQUAL TO key name
  [ INVALID KEY
    do something ]

The following are examples:

MOVE SPACE TO VENDOR-NAME.
START VENDOR-FILE
 KEY NOT < VENDOR-NAME
  INVALID KEY
  MOVE "Y" TO FILE-AT-END.
MOVE "AZZZZZZZZZZZZZZZZZZZ"
    TO VENDOR-NAME.
START VENDOR-FILE
 KEY > VENDOR-NAME
  INVALID KEY
  MOVE "Y" TO FILE-AT-END.
MOVE "JONES AND SONS"
     TO VENDOR-NAME.
START VENDOR-FILE
 KEY EQUALS VENDOR-NAME
  INVALID KEY
  MOVE "N" TO RECORD-FOUND.
START VENDOR-FILE
 KEY GREATER THAN OR EQUAL TO VENDOR-NAME
  INVALID KEY
  MOVE "Y" TO FILE-AT-END.

You will usually use START NOT < or the newer START >= (GREATER THAN OR EQUAL TO), which are the most common types of START used in programs. When the START is completed successfully, the key path is changed and any subsequent READ NEXT proceeds to the next record along that path automatically.

The START has two effects: It changes the key path, and it positions the file someplace on that key path. It most commonly is used to position at the beginning of a key path.

A START does not read or acquire a record; it only positions the file ready for a READ or READ NEXT. If you use START NOT LESS THAN ( NOT < ) or START GREATER THAN ( > ), the start does not actually read a record. It positions the file so that a READ NEXT will read the record that matches the START condition. You see an example of this in Listing 17.20, the vendor-by-name report.

TYPE: Listing 17.20. Printing in vendor name order.

000100 IDENTIFICATION DIVISION.
000200 PROGRAM-ID. VNBYNM01.
000300*--------------------------------
000400* Report on the Vendor File in
000500* alphabetical name order.
000600*--------------------------------
000700 ENVIRONMENT DIVISION.
000800 INPUT-OUTPUT SECTION.
000900 FILE-CONTROL.
001000
001100     COPY "SLVND02.CBL".
001200
001300     COPY "SLSTATE.CBL".
001400
001500     SELECT PRINTER-FILE
001600         ASSIGN TO PRINTER
001700         ORGANIZATION IS LINE SEQUENTIAL.
001800
001900 DATA DIVISION.
002000 FILE SECTION.
002100
002200     COPY "FDVND04.CBL".
002300
002400     COPY "FDSTATE.CBL".
002500
002600 FD  PRINTER-FILE
002700     LABEL RECORDS ARE OMITTED.
002800 01  PRINTER-RECORD             PIC X(80).
002900
003000 WORKING-STORAGE SECTION.
003100
003200 01  DETAIL-LINE.
003300     05  PRINT-NUMBER      PIC 9(5).
003400     05  FILLER            PIC X     VALUE SPACE.
003500     05  PRINT-NAME        PIC X(30).
003600     05  FILLER            PIC X(15) VALUE SPACE.
003700     05  PRINT-CONTACT     PIC X(30).
003800
003900 01  CITY-STATE-LINE.
004000     05  FILLER            PIC X(6) VALUE SPACE.
004100     05  PRINT-CITY        PIC X(20).
004200     05  FILLER            PIC X VALUE SPACE.
004300     05  PRINT-STATE       PIC X(2).
004400     05  FILLER            PIC X VALUE SPACE.
004500     05  PRINT-STATE-NAME  PIC X(20).
004600     05  FILLER            PIC X(1) VALUE SPACE.
004700     05  PRINT-ZIP         PIC X(10).
004800
004900 01  COLUMN-LINE.
005000     05  FILLER         PIC X(2)  VALUE "NO".
005100     05  FILLER         PIC X(4) VALUE SPACE.
005200     05  FILLER         PIC X(12) VALUE "NAME-ADDRESS".
005300     05  FILLER         PIC X(33) VALUE SPACE.
005400     05  FILLER         PIC X(17) VALUE "CONTACT-PHONE-ZIP".
005500
005600 01  TITLE-LINE.
005700     05  FILLER              PIC X(20) VALUE SPACE.
005800     05  FILLER              PIC X(24)
005900         VALUE "VENDOR ALPHABETICAL LIST".
006000     05  FILLER              PIC X(11) VALUE SPACE.
006100     05  FILLER              PIC X(5) VALUE "PAGE:".
006200     05  FILLER              PIC X(1) VALUE SPACE.
006300     05  PRINT-PAGE-NUMBER PIC ZZZZ9.
006400
006500 77  FILE-AT-END             PIC X.
006600 77  STATE-FILE-AT-END       PIC X VALUE "N".
006700 77  LINE-COUNT              PIC 999 VALUE ZERO.
006800 77  PAGE-NUMBER             PIC 99999 VALUE ZERO.
006900 77  MAXIMUM-LINES           PIC 999 VALUE 55.
007000
007100 01  TABLE-STATE-RECORD OCCURS 50 TIMES
007200      INDEXED BY STATE-INDEX.
007300     05  TABLE-STATE-CODE          PIC XX.
007400     05  TABLE-STATE-NAME          PIC X(20).
007500 01  NUMBER-OF-STATES              PIC 99 VALUE 50.
007600 PROCEDURE DIVISION.
007700 PROGRAM-BEGIN.
007800
007900     PERFORM OPENING-PROCEDURE.
008000     MOVE ZEROES TO LINE-COUNT
008100                    PAGE-NUMBER.
008200
008300     PERFORM START-NEW-PAGE.
008400
008500     MOVE "N" TO FILE-AT-END.
008600     PERFORM READ-FIRST-RECORD.
008700     IF FILE-AT-END = "Y"
008800         MOVE "NO RECORDS FOUND" TO PRINTER-RECORD
008900         PERFORM WRITE-TO-PRINTER
009000     ELSE
009100         PERFORM PRINT-VENDOR-FIELDS
009200             UNTIL FILE-AT-END = "Y".
009300
009400     PERFORM CLOSING-PROCEDURE.
009500
009600 PROGRAM-DONE.
009700     STOP RUN.
009800
009900 OPENING-PROCEDURE.
010000     OPEN I-O VENDOR-FILE.
010100
010200     OPEN I-O STATE-FILE.
010300     PERFORM LOAD-STATE-TABLE.
010400     CLOSE STATE-FILE.
010500
010600     OPEN OUTPUT PRINTER-FILE.
010700
010800 LOAD-STATE-TABLE.
010900     PERFORM CLEAR-TABLE.
011000     SET STATE-INDEX TO 1.
011100     PERFORM READ-NEXT-STATE-RECORD.
011200     PERFORM LOAD-ONE-STATE-RECORD
011300         UNTIL STATE-FILE-AT-END = "Y" OR
011400               STATE-INDEX > NUMBER-OF-STATES.
011500
011600 CLEAR-TABLE.
011700     PERFORM CLEAR-ONE-TABLE-ROW
011800         VARYING STATE-INDEX FROM 1 BY 1
011900          UNTIL STATE-INDEX > NUMBER-OF-STATES.
012000
012100 CLEAR-ONE-TABLE-ROW.
012200     MOVE SPACE TO TABLE-STATE-RECORD(STATE-INDEX).
012300
012400 LOAD-ONE-STATE-RECORD.
012500     MOVE STATE-CODE TO TABLE-STATE-CODE(STATE-INDEX).
012600     MOVE STATE-NAME TO TABLE-STATE-NAME(STATE-INDEX).
012700
012800     PERFORM READ-NEXT-STATE-RECORD.
012900
013000     IF STATE-FILE-AT-END NOT = "Y"
013100         SET STATE-INDEX UP BY 1
013200         IF STATE-INDEX > NUMBER-OF-STATES
013300             DISPLAY "TABLE FULL".
013400
013500 CLOSING-PROCEDURE.
013600     CLOSE VENDOR-FILE.
013700     PERFORM END-LAST-PAGE.
013800     CLOSE PRINTER-FILE.
013900
014000 PRINT-VENDOR-FIELDS.
014100     IF LINE-COUNT > MAXIMUM-LINES
014200         PERFORM START-NEXT-PAGE.
014300     PERFORM PRINT-THE-RECORD.
014400     PERFORM READ-NEXT-RECORD.
014500
014600 PRINT-THE-RECORD.
014700     PERFORM PRINT-LINE-1.
014800     PERFORM PRINT-LINE-2.
014900     PERFORM PRINT-LINE-3.
015000     PERFORM PRINT-LINE-4.
015100     PERFORM LINE-FEED.
015200
015300 PRINT-LINE-1.
015400     MOVE SPACE TO DETAIL-LINE.
015500     MOVE VENDOR-NUMBER TO PRINT-NUMBER.
015600     MOVE VENDOR-NAME TO PRINT-NAME.
015700     MOVE VENDOR-CONTACT TO PRINT-CONTACT.
015800     MOVE DETAIL-LINE TO PRINTER-RECORD.
015900     PERFORM WRITE-TO-PRINTER.
016000
016100 PRINT-LINE-2.
016200     MOVE SPACE TO DETAIL-LINE.
016300     MOVE VENDOR-ADDRESS-1 TO PRINT-NAME.
016400     MOVE VENDOR-PHONE TO PRINT-CONTACT.
016500     MOVE DETAIL-LINE TO PRINTER-RECORD.
016600     PERFORM WRITE-TO-PRINTER.
016700
016800 PRINT-LINE-3.
016900     MOVE SPACE TO DETAIL-LINE.
017000     MOVE VENDOR-ADDRESS-2 TO PRINT-NAME.
017100     IF VENDOR-ADDRESS-2 NOT = SPACE
017200         MOVE DETAIL-LINE TO PRINTER-RECORD
017300         PERFORM WRITE-TO-PRINTER.
017400
017500 PRINT-LINE-4.
017600     MOVE SPACE TO CITY-STATE-LINE.
017700     MOVE VENDOR-CITY TO PRINT-CITY.
017800     MOVE VENDOR-STATE TO PRINT-STATE.
017900
018000     PERFORM LOOK-UP-STATE-CODE.
018100
018200     MOVE VENDOR-ZIP TO PRINT-ZIP.
018300     MOVE CITY-STATE-LINE TO PRINTER-RECORD.
018400     PERFORM WRITE-TO-PRINTER.
018500
018600 LOOK-UP-STATE-CODE.
018700     SET STATE-INDEX TO 1.
018800     SEARCH TABLE-STATE-RECORD
018900         AT END
019000          MOVE "***Not Found***" TO PRINT-STATE-NAME
019100         WHEN VENDOR-STATE = TABLE-STATE-CODE(STATE-INDEX)
019200          MOVE TABLE-STATE-NAME(STATE-INDEX)
019300            TO PRINT-STATE-NAME.
019400
019500     IF STATE-NAME = SPACE
019600          MOVE "*State is Blank*" TO PRINT-STATE-NAME.
019700
019800 READ-FIRST-RECORD.
019900     MOVE "N" TO FILE-AT-END.
020000     MOVE SPACE TO VENDOR-NAME.
020100     START VENDOR-FILE
020200        KEY NOT < VENDOR-NAME
020300         INVALID KEY MOVE "Y" TO FILE-AT-END.
020400
020500     IF FILE-AT-END NOT = "Y"
020600         PERFORM READ-NEXT-RECORD.
020700
020800 READ-NEXT-RECORD.
020900     READ VENDOR-FILE NEXT RECORD
021000         AT END MOVE "Y" TO FILE-AT-END.
021100
021200 WRITE-TO-PRINTER.
021300     WRITE PRINTER-RECORD BEFORE ADVANCING 1.
021400     ADD 1 TO LINE-COUNT.
021500
021600 LINE-FEED.
021700     MOVE SPACE TO PRINTER-RECORD.
021800     PERFORM WRITE-TO-PRINTER.
021900
022000 START-NEXT-PAGE.
022100     PERFORM END-LAST-PAGE.
022200     PERFORM START-NEW-PAGE.
022300
022400 START-NEW-PAGE.
022500     ADD 1 TO PAGE-NUMBER.
022600     MOVE PAGE-NUMBER TO PRINT-PAGE-NUMBER.
022700     MOVE TITLE-LINE TO PRINTER-RECORD.
022800     PERFORM WRITE-TO-PRINTER.
022900     PERFORM LINE-FEED.
023000     MOVE COLUMN-LINE TO PRINTER-RECORD.
023100     PERFORM WRITE-TO-PRINTER.
023200     PERFORM LINE-FEED.
023300
023400 END-LAST-PAGE.
023500     PERFORM FORM-FEED.
023600     MOVE ZERO TO LINE-COUNT.
023700
023800 FORM-FEED.
023900     MOVE SPACE TO PRINTER-RECORD.
024000     WRITE PRINTER-RECORD BEFORE ADVANCING PAGE.
024100
024200 READ-NEXT-STATE-RECORD.
024300     MOVE "N" TO STATE-FILE-AT-END.
024400     READ STATE-FILE NEXT RECORD
024500         AT END
024600         MOVE "Y" TO STATE-FILE-AT-END.
024700

The output of vnbynm01.cbl prints the same information as vndrpt03.cbl, but the records are in alphabetical order by name:

OUTPUT:

                     VENDOR ALPHABETICAL LIST           PAGE:     1
NO    NAME-ADDRESS                                 CONTACT-PHONE-ZIP
00002 ABC PRINTING                                 CHARLES JOHANSSEN
1624 FOOTHILL BLVD                           (818) 555-4321
SUITE 34
LOS ANGELES          CA CALIFORNIA           91042
01176 ABERCROMBIE AND OTHERS
1234 45TH ST.                                (213) 555-6543
SUITE 17
LOS ANGELES          CA CALIFORNIA           92345
00001 AERIAL SIGNS                                 HENRIETTA MARKSON
BURBANK AIRPORT                              (818) 555-6066
HANGAR 305
BURBANK              CA CALIFORNIA           90016
00005 ALIAS SMITH AND JONES                        ROBIN COUSINS
1216 MAIN STREET                             415 555-9203
PALO ALTO            CA CALIFORNIA           90061
00022 ARTFUL DODGER
123 UNDERWOOD LANE                           202 555-1234
MARKHAM              WA WASHINGTON           40466
00003 CHARLES SMITH AND SONS                       MARTHA HARRISON
1435 SOUTH STREET                            (213) 555-4432
LOS ANGELES          CA CALIFORNIA           90064
00014 RANIER GRAPHICS                              JULIA SIMPSON
4433 WASHINGTO ST                            (213) 555-6789
LOS ANGELES          CA CALIFORNIA           90032
01440 ZINZINDORFF INC.
1604 7TH ST                                  (213) 555-7234
LOS ANGELES          CA CALIFORNIA           90404

ANALYSIS: The program is almost identical to vndrpt03.cbl. There are changes in TITLE-LINE (lines 005600 to 006300), and the program uses the new SELECT and FD for the vendor file (lines 001100 and 002200).

The main change begins at line 008600, which performs READ-FIRST-RECORD. READ-FIRST-RECORD begins at line 019800. It sets up the FILE-AT-END flag and moves spaces to the VENDOR-NAME, which will be used as the key to the file.

The START at lines 020100 through 020300 attempts to position the file so that the next READ NEXT will read a record whose VENDOR-NAME is equal to or greater than spaces.

The record has not actually been read, so at line 020600, a READ-NEXT-RECORD is requested.

One thing to note is the use of the INVALID KEY in START at line 020300. A START NOT LESS THAN positions the file so that the next READ NEXT will read a record whose value matches the start condition. If a START NOT LESS THAN fails with a file error condition (INVALID KEY), there is no next record matching the START condition. The only way there can be no next record in a file is if you are at the end of the file. If the START NOT LESS THAN fails, the INVALID KEY file error condition is used to set the FILE-AT-END flag to "Y".

How is it possible to be at the end of the file if you are starting with the lowest possible value for the VENDOR-NAME--all spaces? If a record cannot be located that contains a VENDOR-NAME equal to or greater than spaces, the data file is empty and contains no records. Using the INVALID KEY and FILE-AT-END logic, even when there seems to be no need for it, prevents file errors even if you attempt to report on a new file that contains no records.

It is important to note that aside from START, which switches the key path, the rest of the program remains basically unchanged. When the START is completed successfully, the key path is changed and READ NEXT proceeds to the next and subsequent records along that path automatically.

Figure 17.6 is a printer spacing chart for a new vendor report program, vnbynm01.cbl (vendor by name), that prints the same vendor information as vndrpt03.cbl, but in alphabetical name order.

Figure 17.6.
The spacing chart for
vnbynm01.cbl.

Looking Up Records by Alternate Key

You also can use alternate keys to help a user look up a record. Listing 17.21, vninnm01.cbl, is a vendor inquiry by name.

TYPE: Listing 17.21. Looking up records by alternate key.

000100 IDENTIFICATION DIVISION.
000200 PROGRAM-ID. VNINNM01.
000300*--------------------------------
000400* Inquire for the Vendor File
000500* using vendor name.
000600*--------------------------------
000700 ENVIRONMENT DIVISION.
000800 INPUT-OUTPUT SECTION.
000900 FILE-CONTROL.
001000
001100     COPY "SLVND02.CBL".
001200
001300     COPY "SLSTATE.CBL".
001400
001500 DATA DIVISION.
001600 FILE SECTION.
001700
001800     COPY "FDVND04.CBL".
001900
002000     COPY "FDSTATE.CBL".
002100
002200 WORKING-STORAGE SECTION.
002300
002400 77  VENDOR-FILE-AT-END           PIC X.
002500 77  STATE-RECORD-FOUND           PIC X.
002600
002700
002800 77  VENDOR-NAME-FIELD            PIC X(30).
002900
003000     COPY "WSCASE01.CBL".
003100
003200 PROCEDURE DIVISION.
003300 PROGRAM-BEGIN.
003400     PERFORM OPENING-PROCEDURE.
003500     PERFORM MAIN-PROCESS.
003600     PERFORM CLOSING-PROCEDURE.
003700
003800 PROGRAM-DONE.
003900     STOP RUN.
004000
004100 OPENING-PROCEDURE.
004200     OPEN I-O VENDOR-FILE.
004300     OPEN I-O STATE-FILE.
004400
004500 CLOSING-PROCEDURE.
004600     CLOSE VENDOR-FILE.
004700     CLOSE STATE-FILE.
004800
004900 MAIN-PROCESS.
005000     PERFORM INQUIRE-BY-NAME.
005100*--------------------------------
005200* INQUIRE
005300*--------------------------------
005400 INQUIRE-BY-NAME.
005500     PERFORM GET-EXISTING-RECORD.
005600     PERFORM INQUIRE-RECORDS
005700        UNTIL VENDOR-NAME = SPACES.
005800
005900 INQUIRE-RECORDS.
006000     PERFORM DISPLAY-ALL-FIELDS.
006100     PERFORM GET-EXISTING-RECORD.
006200
006300*--------------------------------
006400* Locate a record logic
006500*--------------------------------
006600 GET-EXISTING-RECORD.
006700     PERFORM ACCEPT-EXISTING-KEY.
006800     PERFORM RE-ACCEPT-EXISTING-KEY
006900         UNTIL VENDOR-FILE-AT-END NOT = "Y".
007000
007100 ACCEPT-EXISTING-KEY.
007200     PERFORM INIT-FOR-KEY-ENTRY.
007300     PERFORM ENTER-VENDOR-NAME.
007400     IF VENDOR-NAME NOT = SPACES
007500         PERFORM READ-FIRST-VENDOR-RECORD.
007600
007700 RE-ACCEPT-EXISTING-KEY.
007800     DISPLAY "RECORD NOT FOUND"
007900     PERFORM ACCEPT-EXISTING-KEY.
008000
008100*--------------------------------
008200* Field Entry logic
008300*--------------------------------
008400 ENTER-VENDOR-NAME.
008500     PERFORM ACCEPT-VENDOR-NAME.
008600
008700 ACCEPT-VENDOR-NAME.
008800     DISPLAY "ENTER VENDOR NAME".
008900     ACCEPT VENDOR-NAME.
009000     INSPECT VENDOR-NAME
009100         CONVERTING LOWER-ALPHA
009200         TO         UPPER-ALPHA.
009300
009400*--------------------------------
009500* Display logic
009600*--------------------------------
009700 DISPLAY-ALL-FIELDS.
009800     DISPLAY " ".
009900     PERFORM DISPLAY-VENDOR-NUMBER.
010000     PERFORM DISPLAY-VENDOR-NAME.
010100     PERFORM DISPLAY-VENDOR-ADDRESS-1.
010200     PERFORM DISPLAY-VENDOR-ADDRESS-2.
010300     PERFORM DISPLAY-VENDOR-CITY.
010400     PERFORM DISPLAY-VENDOR-STATE.
010500     PERFORM DISPLAY-VENDOR-ZIP.
010600     PERFORM DISPLAY-VENDOR-CONTACT.
010700     PERFORM DISPLAY-VENDOR-PHONE.
010800     DISPLAY " ".
010900
011000 DISPLAY-VENDOR-NUMBER.
011100     DISPLAY "   VENDOR NUMBER: " VENDOR-NUMBER.
011200
011300 DISPLAY-VENDOR-NAME.
011400     DISPLAY "1. VENDOR NAME: " VENDOR-NAME.
011500
011600 DISPLAY-VENDOR-ADDRESS-1.
011700     DISPLAY "2. VENDOR ADDRESS-1: " VENDOR-ADDRESS-1.
011800
011900 DISPLAY-VENDOR-ADDRESS-2.
012000     DISPLAY "3. VENDOR ADDRESS-2: " VENDOR-ADDRESS-2.
012100
012200 DISPLAY-VENDOR-CITY.
012300     DISPLAY "4. VENDOR CITY: " VENDOR-CITY.
012400
012500 DISPLAY-VENDOR-STATE.
012600     MOVE VENDOR-STATE TO STATE-CODE.
012700     PERFORM READ-STATE-RECORD.
012800     IF STATE-RECORD-FOUND = "N"
012900         MOVE "**Not found**" TO STATE-NAME.
013000     DISPLAY "5. VENDOR STATE: "
013100             VENDOR-STATE " "
013200             STATE-NAME.
013300
013400 DISPLAY-VENDOR-ZIP.
013500     DISPLAY "6. VENDOR ZIP: " VENDOR-ZIP.
013600
013700 DISPLAY-VENDOR-CONTACT.
013800     DISPLAY "7. VENDOR CONTACT: " VENDOR-CONTACT.
013900
014000 DISPLAY-VENDOR-PHONE.
014100     DISPLAY "8. VENDOR PHONE: " VENDOR-PHONE.
014200
014300*--------------------------------
014400* File Related Routines
014500*--------------------------------
014600 INIT-FOR-KEY-ENTRY.
014700     MOVE SPACE TO VENDOR-RECORD.
014800     MOVE ZEROES TO VENDOR-NUMBER.
014900     MOVE "N" TO VENDOR-FILE-AT-END.
015000
015100 READ-FIRST-VENDOR-RECORD.
015200     MOVE "N" TO VENDOR-FILE-AT-END.
015300     START VENDOR-FILE
015400        KEY NOT < VENDOR-NAME
015500         INVALID KEY
015600          MOVE "Y" TO VENDOR-FILE-AT-END.
015700
015800     IF VENDOR-FILE-AT-END NOT = "Y"
015900         PERFORM READ-NEXT-VENDOR-RECORD.
016000
016100 READ-NEXT-VENDOR-RECORD.
016200     READ VENDOR-FILE NEXT RECORD
016300       AT END
016400          MOVE "Y" TO VENDOR-FILE-AT-END.
016500
016600 READ-STATE-RECORD.
016700     MOVE "Y" TO STATE-RECORD-FOUND.
016800     READ STATE-FILE RECORD
016900       INVALID KEY
017000          MOVE "N" TO STATE-RECORD-FOUND.
017100

The output of vninnm01.cbl illustrates looking up vendors by name rather than by number. For the third entry (user entries are bold), the user enters "b" and all entries are converted to uppercase. No vendor names start with "B" in the file, so the program selects the first record that has a key equal to or greater than "B", which is CHARLES SMITH AND SONS:

OUTPUT:

ENTER VENDOR NAME
abc
VENDOR NUMBER: 00002
1. VENDOR NAME: ABC PRINTING
2. VENDOR ADDRESS-1: 1624 FOOTHILL BLVD
3. VENDOR ADDRESS-2: SUITE 34
4. VENDOR CITY: LOS ANGELES
5. VENDOR STATE: CA CALIFORNIA
6. VENDOR ZIP: 91042
7. VENDOR CONTACT: CHARLES JOHANSSEN
8. VENDOR PHONE: (818) 555-4321
ENTER VENDOR NAME
aber
VENDOR NUMBER: 01176
1. VENDOR NAME: ABERCROMBIE AND OTHERS
2. VENDOR ADDRESS-1: 1234 45TH ST.
3. VENDOR ADDRESS-2: SUITE 17
4. VENDOR CITY: LOS ANGELES
5. VENDOR STATE: CA CALIFORNIA
6. VENDOR ZIP: 92345
7. VENDOR CONTACT:
8. VENDOR PHONE: (213) 555-6543
ENTER VENDOR NAME
b
VENDOR NUMBER: 00003
1. VENDOR NAME: CHARLES SMITH AND SONS
2. VENDOR ADDRESS-1: 1435 SOUTH STREET
3. VENDOR ADDRESS-2:
4. VENDOR CITY: LOS ANGELES
5. VENDOR STATE: CA CALIFORNIA
6. VENDOR ZIP: 90064
7. VENDOR CONTACT: MARTHA HARRISON
8. VENDOR PHONE: (213) 555-4432
ENTER VENDOR NAME

ANALYSIS: This program follows the pattern of vndinq01.cbl and the inquire logic of vndmnt03.cbl, but instead of asking the user for the vendor number, it asks the user for the vendor name.

The user is allowed to enter a partial value--"ABER" for ABERCROMBIE, for example--and the program attempts to locate a record that contains a name that is close to that value.

For each name entered by the user, the program attempts to START the file using that value, and if the start is successful, the next record in the file is read. It is possible for a user to enter a name that is beyond the end of the file, such as "ZZZZZZ", so a VENDOR-FILE-AT-END flag is used to trap entries that do not allow a record to be found in the file.

In this version of inquire, the user enters a vendor name of spaces to signal end of entry; anything else is converted to uppercase and then used by the program. At lines 008400 and 008700, ENTER-VENDOR-NAME ACCEPT-VENDOR-NAME takes care of this simple field-entry routine.

The loop control and loop start at line 005400 in 005400 INQUIRE-BY-NAME. This routine starts by getting a valid record (GET-EXISTING-RECORD) and then performing the main loop, INQUIRE-RECORDS, until the user enters a vendor name of spaces (VENDOR-NAME = SPACES).

The main loop itself at 005900 displays all fields; then at the bottom of the loop, line 006100 asks the user for another vendor name to look for by once again performing GET-EXISTING-RECORD.

The GET-EXISTING-RECORD routine at line 006600 is structured as a standard field-entry routine that performs an ACCEPT-EXISTING-KEY and then a RE-ACCEPT-EXISTING-KEY until VENDOR-FILE-AT-END NOT = "Y". This logic is used to force the user to enter a vendor name that is not at the end of the file.

At line 007100, ACCEPT-EXISTING-KEY initializes the vendor record and then gets the user entry of the name to look up. If the name is not spaces, an attempt is made to READ-FIRST-VENDOR-RECORD, which will START the file using the entered vendor name and then try to read the next record in the file. If this causes VENDOR-FILE-AT-END = "Y", then back at line 006800, the program will RE-ACCEPT-EXISTING-KEY until VENDOR-FILE-AT-END NOT = "Y".

The RE-ACCEPT-EXISTING-KEY routine at line 007700 displays an error message and then tries again to ACCEPT-EXISTING-KEY.

The READ-FIRST-VENDOR-RECORD routine at line 015100 is identical to that used in Listing 17.20, vnbynbm01.cbl, and it sets the VENDOR-FILE-AT-END flag if a record cannot be found.

The remainder of the program is used to display the fields in the record, and you are familiar with this type of logic. Code, compile, and run vninnm01.cbl, and use it to look up records in your vendor file.

Helping the User with Alternate Keys

A flaw in vninnm01.cbl makes it awkward for the user. In the following output example, the user wants to look up ABERCROMBIE AND OTHERS and enters "ab". In the sample file, two vendors start with "AB", and ABC PRINTING appears before ABERCROMBIE AND OTHERS in the file in alternate key order. The user's entry retrieves ABC PRINTING, and the user must enter "aber" to zero in on the record that is wanted:

OUTPUT:

ENTER VENDOR NAME
ab
VENDOR NUMBER: 00002
1. VENDOR NAME: ABC PRINTING
2. VENDOR ADDRESS-1: 1624 FOOTHILL BLVD
3. VENDOR ADDRESS-2: SUITE 34
4. VENDOR CITY: LOS ANGELES
5. VENDOR STATE: CA CALIFORNIA
6. VENDOR ZIP: 91042
7. VENDOR CONTACT: CHARLES JOHANSSEN
8. VENDOR PHONE: (818) 555-4321
ENTER VENDOR NAME
aber
VENDOR NUMBER: 01176
1. VENDOR NAME: ABERCROMBIE AND OTHERS
2. VENDOR ADDRESS-1: 1234 45TH ST.
3. VENDOR ADDRESS-2: SUITE 17
4. VENDOR CITY: LOS ANGELES
5. VENDOR STATE: CA CALIFORNIA
6. VENDOR ZIP: 92345
7. VENDOR CONTACT:
8. VENDOR PHONE: (213) 555-6543
ENTER VENDOR NAME

This can become a headache in a large file with many names that are close to one another, and it can be impossible to deal with if two vendors have the same name. In the following sample output, the vendor file contains two firms named ABC PRINTING--one in Los Angeles and one in Pomona:

OUTPUT:

VENDOR NUMBER: 00002
1. VENDOR NAME: ABC PRINTING
2. VENDOR ADDRESS-1: 1624 FOOTHILL BLVD
3. VENDOR ADDRESS-2: SUITE 34
4. VENDOR CITY: LOS ANGELES
5. VENDOR STATE: CA CALIFORNIA
6. VENDOR ZIP: 91042
7. VENDOR CONTACT: CHARLES JOHANSSEN
8. VENDOR PHONE: (818) 555-4321
VENDOR NUMBER: 00067
1. VENDOR NAME: ABC PRINTING
2. VENDOR ADDRESS-1: 1606 SOUTH 7TH
3. VENDOR ADDRESS-2:
4. VENDOR CITY: POMONA
5. VENDOR STATE: CA CALIFORNIA
6. VENDOR ZIP: 90404
7. VENDOR CONTACT: HARRIET NELSON
8. VENDOR PHONE: (815) 555-2020

The user needs the phone number for the vendor in Pomona but does not know the vendor number. This is a good time to use inquiry by vendor name. However, the next output sample indicates that, in spite of repeated attempts to enter more and more of the company name, vninnm01.cbl always returns the first vendor.

This problem has to do with the way records are stored by alternate key. If an alternate key allows duplicates, and two or more records actually do have the same key value, which one is first? Usually, it is the one that was added to the file first. Sometimes it is the record with the lowest primary index key value. No matter which record is considered to be "first" in the file, vninnm01.cbl will not let you go to the next record:

OUTPUT:

ENTER VENDOR NAME
abc
VENDOR NUMBER: 00002
1. VENDOR NAME: ABC PRINTING
2. VENDOR ADDRESS-1: 1624 FOOTHILL BLVD
3. VENDOR ADDRESS-2: SUITE 34
4. VENDOR CITY: LOS ANGELES
5. VENDOR STATE: CA CALIFORNIA
6. VENDOR ZIP: 91042
7. VENDOR CONTACT: CHARLES JOHANSSEN
8. VENDOR PHONE: (818) 555-4321
ENTER VENDOR NAME
abc print
VENDOR NUMBER: 00002
1. VENDOR NAME: ABC PRINTING
2. VENDOR ADDRESS-1: 1624 FOOTHILL BLVD
3. VENDOR ADDRESS-2: SUITE 34
4. VENDOR CITY: LOS ANGELES
5. VENDOR STATE: CA CALIFORNIA
6. VENDOR ZIP: 91042
7. VENDOR CONTACT: CHARLES JOHANSSEN
8. VENDOR PHONE: (818) 555-4321
ENTER VENDOR NAME
abc printing
VENDOR NUMBER: 00002
1. VENDOR NAME: ABC PRINTING
2. VENDOR ADDRESS-1: 1624 FOOTHILL BLVD
3. VENDOR ADDRESS-2: SUITE 34
4. VENDOR CITY: LOS ANGELES
5. VENDOR STATE: CA CALIFORNIA
6. VENDOR ZIP: 91042
7. VENDOR CONTACT: CHARLES JOHANSSEN
8. VENDOR PHONE: (818) 555-4321
ENTER VENDOR NAME

Listing 17.22 is vninnm02.cbl, and it solves the problem.

TYPE: Listing 17.22. A better alternate key look up.

000100 IDENTIFICATION DIVISION.
000200 PROGRAM-ID. VNINNM02.
000300*--------------------------------
000400* Inquire for the Vendor File
000500* using vendor name.
000600*--------------------------------
000700 ENVIRONMENT DIVISION.
000800 INPUT-OUTPUT SECTION.
000900 FILE-CONTROL.
001000
001100     COPY "SLVND02.CBL".
001200
001300     COPY "SLSTATE.CBL".
001400
001500 DATA DIVISION.
001600 FILE SECTION.
001700
001800     COPY "FDVND04.CBL".
001900
002000     COPY "FDSTATE.CBL".
002100
002200 WORKING-STORAGE SECTION.
002300
002400 77  VENDOR-FILE-AT-END           PIC X.
002500 77  STATE-RECORD-FOUND           PIC X.
002600
002700 77  SEE-NEXT-RECORD             PIC X.
002800
002900 77  VENDOR-NAME-FIELD            PIC X(30).
003000
003100     COPY "WSCASE01.CBL".
003200
003300 PROCEDURE DIVISION.
003400 PROGRAM-BEGIN.
003500     PERFORM OPENING-PROCEDURE.
003600     PERFORM MAIN-PROCESS.
003700     PERFORM CLOSING-PROCEDURE.
003800
003900 PROGRAM-DONE.
004000     STOP RUN.
004100
004200 OPENING-PROCEDURE.
004300     OPEN I-O VENDOR-FILE.
004400     OPEN I-O STATE-FILE.
004500
004600 CLOSING-PROCEDURE.
004700     CLOSE VENDOR-FILE.
004800     CLOSE STATE-FILE.
004900
005000 MAIN-PROCESS.
005100     PERFORM INQUIRE-BY-NAME.
005200*--------------------------------
005300* INQUIRE
005400*--------------------------------
005500 INQUIRE-BY-NAME.
005600     PERFORM GET-EXISTING-RECORD.
005700     PERFORM INQUIRE-RECORDS
005800        UNTIL VENDOR-NAME = SPACES.
005900
006000 INQUIRE-RECORDS.
006100     PERFORM SHOW-THIS-RECORD.
006200     PERFORM SHOW-NEXT-RECORD
006300        UNTIL SEE-NEXT-RECORD = "N" OR
006400              VENDOR-FILE-AT-END = "Y".
006500
006600     PERFORM GET-EXISTING-RECORD.
006700
006800
006900*--------------------------------
007000* Show records one by one
007100*--------------------------------
007200 SHOW-THIS-RECORD.
007300     PERFORM DISPLAY-ALL-FIELDS.
007400     PERFORM GET-SEE-NEXT-RECORD.
007500
007600 SHOW-NEXT-RECORD.
007700     PERFORM READ-NEXT-VENDOR-RECORD.
007800     IF VENDOR-FILE-AT-END NOT = "Y"
007900         PERFORM SHOW-THIS-RECORD.
008000
008100*--------------------------------
008200* Get valid record logic
008300*--------------------------------
008400 GET-EXISTING-RECORD.
008500     PERFORM ACCEPT-EXISTING-KEY.
008600     PERFORM RE-ACCEPT-EXISTING-KEY
008700         UNTIL VENDOR-FILE-AT-END NOT = "Y".
008800
008900 ACCEPT-EXISTING-KEY.
009000     PERFORM INIT-FOR-KEY-ENTRY.
009100     PERFORM ENTER-VENDOR-NAME.
009200     IF VENDOR-NAME NOT = SPACES
009300         PERFORM READ-FIRST-VENDOR-RECORD.
009400
009500 RE-ACCEPT-EXISTING-KEY.
009600     DISPLAY "RECORD NOT FOUND"
009700     PERFORM ACCEPT-EXISTING-KEY.
009800
009900*--------------------------------
010000* Field Entry logic
010100*--------------------------------
010200 ENTER-VENDOR-NAME.
010300     PERFORM ACCEPT-VENDOR-NAME.
010400
010500 ACCEPT-VENDOR-NAME.
010600     DISPLAY "ENTER VENDOR NAME".
010700     ACCEPT VENDOR-NAME.
010800     INSPECT VENDOR-NAME
010900         CONVERTING LOWER-ALPHA
011000         TO         UPPER-ALPHA.
011100
011200 GET-SEE-NEXT-RECORD.
011300     PERFORM ACCEPT-SEE-NEXT-RECORD.
011400     PERFORM RE-ACCEPT-SEE-NEXT-RECORD
011500         UNTIL SEE-NEXT-RECORD = "Y" OR "N".
011600
011700 ACCEPT-SEE-NEXT-RECORD.
011800     DISPLAY "DISPLAY NEXT RECORD (Y/N)?".
011900     ACCEPT SEE-NEXT-RECORD.
012000
012100     IF SEE-NEXT-RECORD = SPACE
012200         MOVE "Y" TO SEE-NEXT-RECORD.
012300
012400     INSPECT SEE-NEXT-RECORD
012500       CONVERTING LOWER-ALPHA
012600       TO         UPPER-ALPHA.
012700
012800 RE-ACCEPT-SEE-NEXT-RECORD.
012900     DISPLAY "MUST ENTER YES OR NO".
013000     PERFORM ACCEPT-SEE-NEXT-RECORD.
013100
013200*--------------------------------
013300* Display logic
013400*--------------------------------
013500 DISPLAY-ALL-FIELDS.
013600     DISPLAY " ".
013700     PERFORM DISPLAY-VENDOR-NUMBER.
013800     PERFORM DISPLAY-VENDOR-NAME.
013900     PERFORM DISPLAY-VENDOR-ADDRESS-1.
014000     PERFORM DISPLAY-VENDOR-ADDRESS-2.
014100     PERFORM DISPLAY-VENDOR-CITY.
014200     PERFORM DISPLAY-VENDOR-STATE.
014300     PERFORM DISPLAY-VENDOR-ZIP.
014400     PERFORM DISPLAY-VENDOR-CONTACT.
014500     PERFORM DISPLAY-VENDOR-PHONE.
014600     DISPLAY " ".
014700
014800 DISPLAY-VENDOR-NUMBER.
014900     DISPLAY "   VENDOR NUMBER: " VENDOR-NUMBER.
015000
015100 DISPLAY-VENDOR-NAME.
015200     DISPLAY "1. VENDOR NAME: " VENDOR-NAME.
015300
015400 DISPLAY-VENDOR-ADDRESS-1.
015500     DISPLAY "2. VENDOR ADDRESS-1: " VENDOR-ADDRESS-1.
015600
015700 DISPLAY-VENDOR-ADDRESS-2.
015800     DISPLAY "3. VENDOR ADDRESS-2: " VENDOR-ADDRESS-2.
015900
016000 DISPLAY-VENDOR-CITY.
016100     DISPLAY "4. VENDOR CITY: " VENDOR-CITY.
016200
016300 DISPLAY-VENDOR-STATE.
016400     MOVE VENDOR-STATE TO STATE-CODE.
016500     PERFORM READ-STATE-RECORD.
016600     IF STATE-RECORD-FOUND = "N"
016700         MOVE "**Not found**" TO STATE-NAME.
016800     DISPLAY "5. VENDOR STATE: "
016900             VENDOR-STATE " "
017000             STATE-NAME.
017100
017200 DISPLAY-VENDOR-ZIP.
017300     DISPLAY "6. VENDOR ZIP: " VENDOR-ZIP.
017400
017500 DISPLAY-VENDOR-CONTACT.
017600     DISPLAY "7. VENDOR CONTACT: " VENDOR-CONTACT.
017700
017800 DISPLAY-VENDOR-PHONE.
017900     DISPLAY "8. VENDOR PHONE: " VENDOR-PHONE.
018000
018100*--------------------------------
018200* File Related Routines
018300*--------------------------------
018400 INIT-FOR-KEY-ENTRY.
018500     MOVE SPACE TO VENDOR-RECORD.
018600     MOVE ZEROES TO VENDOR-NUMBER.
018700     MOVE "N" TO VENDOR-FILE-AT-END.
018800
018900 READ-FIRST-VENDOR-RECORD.
019000     MOVE "N" TO VENDOR-FILE-AT-END.
019100     START VENDOR-FILE
019200        KEY NOT < VENDOR-NAME
019300         INVALID KEY
019400          MOVE "Y" TO VENDOR-FILE-AT-END.
019500
019600     IF VENDOR-FILE-AT-END NOT = "Y"
019700         PERFORM READ-NEXT-VENDOR-RECORD.
019800
019900 READ-NEXT-VENDOR-RECORD.
020000     READ VENDOR-FILE NEXT RECORD
020100       AT END
020200          MOVE "Y" TO VENDOR-FILE-AT-END.
020300
020400 READ-STATE-RECORD.
020500     MOVE "Y" TO STATE-RECORD-FOUND.
020600     READ STATE-FILE RECORD
020700       INVALID KEY
020800          MOVE "N" TO STATE-RECORD-FOUND.
020900

The output from vninnm02.cbl enables the user to step through the file record by record or to skip around, starting with different names:

OUTPUT:

ENTER VENDOR NAME
abc
VENDOR NUMBER: 00002
1. VENDOR NAME: ABC PRINTING
2. VENDOR ADDRESS-1: 1624 FOOTHILL BLVD
3. VENDOR ADDRESS-2: SUITE 34
4. VENDOR CITY: LOS ANGELES
5. VENDOR STATE: CA CALIFORNIA
6. VENDOR ZIP: 91042
7. VENDOR CONTACT: CHARLES JOHANSSEN
8. VENDOR PHONE: (818) 555-4321
DISPLAY NEXT RECORD (Y/N)?

(User presses Enter here.)

VENDOR NUMBER: 00067
1. VENDOR NAME: ABC PRINTING
2. VENDOR ADDRESS-1: 1606 SOUTH 7TH
3. VENDOR ADDRESS-2:
4. VENDOR CITY: POMONA
5. VENDOR STATE: CA CALIFORNIA
6. VENDOR ZIP: 90404
7. VENDOR CONTACT: HARRIET NELSON
8. VENDOR PHONE: (815) 555-2020
DISPLAY NEXT RECORD (Y/N)?

(User presses Enter here.)

VENDOR NUMBER: 01176
1. VENDOR NAME: ABERCROMBIE AND OTHERS
2. VENDOR ADDRESS-1: 1234 45TH ST.
3. VENDOR ADDRESS-2: SUITE 17
4. VENDOR CITY: LOS ANGELES
5. VENDOR STATE: CA CALIFORNIA
6. VENDOR ZIP: 92345
7. VENDOR CONTACT:
8. VENDOR PHONE: (213) 555-6543
DISPLAY NEXT RECORD (Y/N)?
n
ENTER VENDOR NAME
z
VENDOR NUMBER: 01440
1. VENDOR NAME: ZINZINDORFF INC.
2. VENDOR ADDRESS-1: 1604 7TH ST
3. VENDOR ADDRESS-2:
4. VENDOR CITY: LOS ANGELES
5. VENDOR STATE: CA CALIFORNIA
6. VENDOR ZIP: 90404
7. VENDOR CONTACT:
8. VENDOR PHONE: (213) 555-7234
DISPLAY NEXT RECORD (Y/N)?

ANALYSIS: This program is similar to vninnm01.cbl, but it enables the user to choose the option of looking at the next record after the selected record is displayed.

The differences in the two programs start at line 006000, INQUIRE-RECORDS, in vninnm02.cbl. Rather than displaying all fields and then asking the user for another vendor name, the loop is changed to SHOW-THIS-RECORD and then SHOW-NEXT-RECORD until SEE-NEXT-RECORD = "N" or VENDOR-FILE-AT-END = "Y". The SEE-NEXT-RECORD variable is a yes/no field entered by the user to indicate whether to display the next record. It is defined at line 002700.

At line 007200, the SHOW-THIS-RECORD routine displays all fields and then asks the user whether the next record is wanted. This answer is returned to INQUIRE-RECORDS at line 006200, and if the user has indicated Y by entering the value or simply pressing Enter, the SHOW-NEXT-RECORD routine is performed as a loop.

The SHOW-NEXT-RECORD routine at line 007600 tries to read the next record in the vendor file. If it is successful, it performs SHOW-THIS-RECORD, which displays all fields, and once again asks the user whether the next record is wanted. This loop continues until the user answers N or the read-next takes him or her to the end of the file.

The GET-SEE-NEXT-RECORD routine at line 011200 is a standard field-entry routine. In this case, the user can enter a space, but it is converted to "Y" at lines 012100 and 012200. This gives the user the convenience of pressing Enter to see the next record rather than having to enter a "Y" explicitly. If the user enters something, it must be "Y" or "N" (or the lowercase versions).

The GET-SEE-NEXT-RECORD loop continues until the end of the file is reached, whereupon the user once again is asked for a vendor name.

Code, compile, and run vninnm02.cbl. If you want to, use vndmnt03.cbl to add a vendor with the same name as an existing vendor in the file. You can use vninnm02.cbl to view this record.

Summary

Although an indexed file must have a unique primary key, it is possible to put other keys in the file. These do not need to be unique and can be arranged to return records in a different order than the primary key. Today, you learned these basics:

Q&A

Q How many alternate keys can be in a file?

A This varies based on your version of COBOL. Most versions of COBOL allow at least 16 alternate keys, which is plenty for most problems. I have seen some versions of COBOL that allow 63 and even 119 alternate keys.

Workshop

Quiz

1. If a vendor file contained records with the vendor names

ABC PRINTING

AZAZEL AND SONS

CHARLES RUMFORD INC.

OMEGA MANUFACTURING

ZYDEC DESIGNS

which record would be read after the logic in the following listing?
018900     MOVE "LINCOLN" TO VENDOR-NAME.
019000     START VENDOR-FILE
019100        KEY NOT < VENDOR-NAME
019200         INVALID KEY
019300          MOVE "Y" TO VENDOR-FILE-AT-END.
019400
019500     IF VENDOR-FILE-AT-END NOT = "Y"
019600         PERFORM READ-NEXT-VENDOR-RECORD.
019700
019800 READ-NEXT-VENDOR-RECORD.
019900     READ VENDOR-FILE NEXT RECORD
020000       AT END
020100          MOVE "Y" TO VENDOR-FILE-AT-END.
2. What are the keys in the following listing?

Hint: Don't forget the primary key.
000400     SELECT PART-FILE
000500         ASSIGN TO "PARTS"
000600         ORGANIZATION IS INDEXED
000700         RECORD KEY IS PART-NUMBER
000800         ALTERNATE KEY IS PART-VENDOR WITH DUPLICATES
000900         ALTERNATE KEY IS PART-DEPARTMENT WITH DUPLICATES
001000         ALTERNATE KEY IS PART-VENDOR-NUMBER
001100         ACCESS MODE IS DYNAMIC.
001200
3. In the listing in question 2, which keys allow duplicates? Which do not?

Exercise

Assuming a customer file with a record layout, as shown in the following listing, create a SELECT statement that defines the CUSTOMER-NUMBER as the primary key, the CUSTOMER-NAME as an alternate key with duplicates, and the CUSTOMER-ZIP as an alternate key with duplicates. The physical name of the file on the disk will be "CUST".
000000 FD  CUSTOMER-FILE
000000     LABEL RECORDS ARE STANDARD.
001000 01  CUSTOMER-RECORD.
001100     05  CUSTOMER-NUMBER          PIC 9(5).
001200     05  CUSTOMER-NAME            PIC X(20).
001300     05  CUSTOMER-ADDRESS-1       PIC X(20).
001400     05  CUSTOMER-ADDRESS-2       PIC X(20).
001500     05  CUSTOMER-ZIP             PIC 9(5).
001600     05  CUSTOMER-PHONE.
001700         10  CUSTOMER-AREA-CODE   PIC 9(3).
001800         10  CUSTOMER-PREFIX      PIC 9(3).
001900         10  CUSTOMER-PHONE-NO    PIC 9(4).


Previous chapterNext chapterContents


© Copyright, Macmillan Computer Publishing. All rights reserved.