There's no telling how many programmers have simply given up using ISPF tables because 'the @#$% table services never work the way you expect them to work!' They actually work just fine. It's the @#$% IBM manuals that don't make their operation clear — don't make the operation of some very complex services as clear as they ought to be. Whether this is simply a matter of writing style or whether IBM does so deliberately, the delicate interrelationship between TBVCLEAR, TBSARG, TBSCAN, and TBDISPL rarely gets to a programmer's conscious level. The lack of examples showing TBVCLEAR, TBSARG, and TBDISPL working in concert also doesn't help.
The most important ISPF table service related to table searching is neither TBSARG nor TBSCAN; it is TBVCLEAR. The IBM manuals (almost) completely gloss over the fact that when searching an ISPF table, every element that has a value, whether it is part of the explicitly-named search argument or not, participates in setting the search argument. Every element that has a value. The service that makes sure elements do not have values is TBVCLEAR.
If you carefully read the manual text for TBSARG, there are subtle clues to this behavior as it relates to setting search/scan arguments:
A value of null for one of the dialog variables means that the corresponding table variable is not to be examined during the search.and
To set up a search argument, set table variables in the function pool to nulls by using TBVCLEAR.and
The search arguments are specified in dialog variables that correspond to columns in the table, and this determines the columns that take place (sic) in the search.
You could be forgiven for thinking that specifying this variable or that one in a TBSARG might have some bearing on which variables get used for setting the search argument. Alas, it has less 'bearing' than might seem obvious.
Let us assume that we have a table, TBL, and that this table is defined as
"TBCREATE TBL KEYS(KEY1 KEY2) NAMES(NAME1 NAME2 NAME3) WRITE REPLACE"
We have read a particular row, either by TBGET or TBSKIP, and now we wish to find all the rows in TBL with the same NAME2 value. These will be displayed via TBDISPL for further processing.
"TBSARG TBL NAMECOND(NAME2,EQ)"
is insufficient. It is insufficient because KEY1, KEY2, NAME1, and NAME3 all have values and are not null. In fact, a subsequent TBSCAN or TBDISPL will return a single row, the one that was just read. That's the only row on the table with a matching KEY1 and KEY2. In order to expand the search to the remainder of the table, we must zap all those variables:
nval = name2 /* preserve the value */ "TBVCLEAR TBL" name2 = nval /* load NAME2 */ "TBSARG TBL NAMECOND(NAME2,EQ)"
In fact, since "EQ" is the default and all table variables that are not null participate in the search (and NAME2 is the only non-null variable at this point), it should be sufficient in this case to cast the TBSARG as
Since we're working in REXX, some may be tempted to DROP the elements that are not to be searched-for. A REXX 'drop' is not the same thing as setting a table variable to null via TBVCLEAR and the results may be disappointing. Likewise, setting the variables to null via 'closed quotes'
parse value "" with key1 key2 name1 name3 .
also won't get you what you need. This will cause the search to be limited to those rows for which KEY1 and KEY2 are empty. There can only be one of those and there may not be even that.
Before TBSARG, before TBSCAN (if that's how you're setting the search argument), TBVCLEAR. Voila!