Searching Tables – General Notes
SEARCH is used to look up a table and return a corresponding value. You can
search coded fields, simple tables, and GROUP tables.
SEARCH
Field WITH
Search-Value [GIVING
Result] [INDEX
index];
You may
give either or both of GIVING or INDEX, you may not omit them both.
Field is either a coded variable, or a table
(array) defined to Jazz.
Prior to
Build #309 an Easytrieve Search Statement like
SEARCH BRANCHID WITH BRANCH
GIVING BRANCH-TEXT
was
converted into something like
SEARCH(BRANCHID)WITH INPUT1.BRANCH
GIVING EZT-Test1-Data.BRANCH-TEXT;
#709 S Invalid Search statement: BRANCHID Is Not a table
This is
because the EZT definition of BRANCHID was converted into
DEFINE BRANCHID DATA(
DESC CHAR(4) CAPS CODES('CHEM':'CHEMICAL','MCA ':'MASTERCOMP','MECH':'MECHANICAL','MTEC':'MTECHNOLGY'));
BRANCHID is a record name, not a field name. Correct error #709 by changing the statement to
SEARCH(BRANCHID.DESC)WITH INPUT1.BRANCH GIVING
EZT-Test1-Data.BRANCH-TEXT;
With Build
#309 and later this correction should be done automatically.
If the
table is a group, it must contain one or more search fields, and one or more
other fields. Search fields are
identified with property SKEY or TKEY, for example: -
DEFINE GROUP1(150) GROUP,
ARG CHAR(4) SKEY,
DESC CHAR(40),
END GROUP);
Use SKEY if the
search field is in ascending sequence.
This will allow the SEARCH to use more efficient logic, but will give incorrect
results if you specify this and the search fields is NOT in ascending
sequence. Use TKEY if you
can’t guarantee that the search field is in ascending sequence.
If the
table is not a group, then you must use the INDEX option and
any GIVING option will be ignored.
For tables
created by conversion from Easytrieve the search field will usually be named ARG, and the value field will be named DESC. For other tables the search and value fields
can have any valid names. Instream
tables are converted to Coded Fields, External Tables are converted into Tables
(arrays).
DEFINE TEST1 DATA(
DESC CHAR(4) CAPS CODES('CHEM':'CHEMICAL','MCA ':'MASTERCOMP','MECH':'MECHANICAL','MTEC':'MTECHNOLGY'));
You could
search this with
DEFINE W DATA(
WITH CHAR(4) VALUE 'MECH',
GIVE1 CHAR(10));
SEARCH(TEST1.DESC) WITH W.WITH
GIVING W.GIVE1;
This will set W.GIVE1 to 'MECHANICAL'. If the search value was not found, the value
returned will be '**********'. The WITH field (W.WITH) should have the same format as the
field searched, i.e. CHAR(4) in this case, and the GIVING field (W.GIVE1) must be at least long enough for the longest
value returned (CHAR(10)).
With coded
fields you may not use the INDEX option.
You will
probably write a search statement like this
SEARCH ST WITH W2 INDEX IX1;
and click [Check] to have MANASYS qualify the field names and check the statement. If you do, you’ll get something like this: -
SEARCH(W.ST(*))WITH W.W2 INDEX JZ2.IX1;
#049 S Subscript(s) expected for W.ST
The table reference in a SEARCH statement needs to have subscript (*), and MANASYS has actually put this in for you, but unfortunately too late to prevent message #49. Just click [Check] again to get rid of it.
SEARCH(W.ST(*))WITH W.W2 INDEX JZ2.IX1;
There is a special field SEARCH-FOUND that you can test after the SEARCH statement if you need to know whether the search has been successful. Write
IF SEARCH-FOUND = false (or true)
which [Check] will qualify to
IF JZ2.SEARCH-FOUND = false; (or true;)
For unsuccessful searches an INDEX value will be returned as zero, and GIVING values will be set to asterisks if alpha, or zero if numeric.
If you know that the table is in ascending order and the
table is large (say over 50 occurrences) then you should use binary search
logic as this is more efficient. Do this
by using the property SKEY for the only key field of the group, or
the only field of a stand-alone table.
Sequential searches will be used if keys have property TKEY, and you must use this option if there is more than one key
field in the group, or you can’t guarantee that the table is in ascending
order.
For example
SEARCH(W.ST(*))WITH W.W2 INDEX JZ2.IX1;
With a
table that is not a group you don’t use GIVING, but
instead use INDEX to return the subscript of the found
occurrence. If not found the index value
will be returned as zero, so
IF JZ2.IX1 = 0;
is
logically equivalent to
IF JZ2.SEARCH-FOUND = false;
If the group
is in ascending order (ST is not) and it has property SKEY, then the SEARCH will use efficient binary-search logic.
When SKEY is invalidly used, as in the definition of W.ST above, the search results are unpredictable.
A repeating group can be searched, provided that the group contains one or a few fields identified as search keys, and at least one other value field.
· Search keys are identified by having the property SKEY or TKEY. There can only be one key field if you use SKEY, but you can have more than one TKEY fields.
· Value fields are other fields in the group not named FILLER.
SKEY indicates that the table will be in ascending sequence (so a binary search can be used), while TKEY indicates the key field for sequential searching.
This is particularly useful for external tables, where the data is not hard-coded into the program but instead is held on a file that is read into the table when the program starts up. This has advantages: the table can be changed without recompiling the program, it can be easy to ensure that the data is read in the correct order to use binary searching, and there is a natural association between the key value (like 'AK' and its value “Alaska”), and whatever other information the table holds.
Here are some examples
DEFINE T DATA(
TA (10) GROUP,
FIND CHAR(4) TKEY,
V1 CHAR(10),
End GROUP);
DEFINE T2 DATA(
TB (500) GROUP,
F2 CHAR(3) SKEY,
V2 CHAR(10),
V3 CHAR(5),
V4 SMALLINT,
V5 REAL,
End GROUP);
DEFINE CLASSES DATA(
GROUP1(150) GROUP,
ARG CHAR(4) SKEY,
DESC CHAR(40),
END GROUP);
Each of these tables need to be loaded before the first SEARCH: it is usual to do this in the program’s Start routine. T.TA is very small and so it’s not worth sorting the data into order and using a binary search, but T2.TB and CLASSES.GROUP1 are larger.
When loading the tables, the load procedure should check that the table size has not been exceeded (10, 500, and 150 respectively). Unused table positions should have their key fields set to HIGH-VALUES if CHAR, or the maximum numeric value if a number.
With single-value tables like T.TA and CLASSES.GROUP1 you’d normally use GIVING, although you can use INDEX as well. With multi-value tables you can only use INDEX, as GIVING returns a single value: -
SEARCH(T.TA(*)) WITH W.W1 GIVING W.G1;
SEARCH(T2.TB(*) )WITH W.W3 INDEX JZ2.IX;
SEARCH(CLASSES.GROUP1(*) )WITH W.W1 GIVING W.G1;
If the value is found it is returned in the GIVING field. If not found the GIVING field is set to “**********” (to the length of the GIVING field). In addition, a flag field SEARCH-FOUND is set TRUE or FALSE.
If there are several value fields, then use INDEX to return the subscript of the found value, as in the search for EX.TABLE:-
DEFINE EX DATA(
TABLE (5000) GROUP,
T-XYZ CHAR(4) SKEY,
T-V1 CHAR(1),
T-V2 CHAR(1),
T-V3 CHAR(1),
T-V4 CHAR(1),
END GROUP,
W.W1 = ???;
SEARCH(EX.TABLE(*)) WITH W.W1 INDEX JZ2.IX3;
IF JZ2.SEARCH-FOUND = TRUE;
W.T-TV1 = EX.TABLE.T-V1(JZ2.IX3);
…
The INDEX field, IX3, will be returned as 0 if the search value is not found, so testing
IF JZ2.IX3 = 0;
is equivalent to testing SEARCH-FOUND = TRUE.
Tables may have fixed values, like W.ST, or they may be empty and require loading before the SEARCH is executed. This should be done with a routine that is executed as part of the program’s START routine. Here’s an example: -
ROUTINE LOAD-TA;
PROCESS LTA COUNT JZ2.IX1 SID(20);
IF JZ2.IX1 > 10;
DISPLAY('Table Limit (10)
Exceeded. Load Terminated');
COBOL.RETURN-CODE =
8;
EXIT LOAD-TA;
END IF;
T.TA.FIND(JZ2.IX1) = LTA.FIND;
T.TA.V1(JZ2.IX1) = LTA.V1;
END PROCESS LTA;
JZ2.IX1 += 1;
FOR JZ2.IX3 = JZ2.IX1 TO 10;
T.FIND(JZ2.IX3) = HIGH-VALUES;
END FOR;
END ROUTINE LOAD-TA;
All such routines should check that the table size is not exceeded,
and also initialize unused positions to HIGH-VALUES if
the key is alphabetic, or the maximum numeric value otherwise. If the table uses SKEY, then the PROCESS statement should include ORDER to ensure that the data is read in
ascending order of the key field.
Data is read from a file containing the key and value data for the table. This example uses LTA which is defined: -
DEFINE LTA FB DATA(
FIND CHAR(4),
FILLER CHAR(1),
V1 CHAR(10),
FILLER CHAR(65))
DSNAME '*';
The use of DSNAME '*' causes JCL //LTA DD * to be generated, so here the data will be added into the job.
With Build #293 and later MANASYS Jazz will create load routines for you. If a PROGRAM statement has property EZT, then an [LProc] button appears in place of the [Services] button. Click this and a dialog follows where you select a table defined into your program, a file from which you will load it, and then select file fields for each table field. [Finish] then will create a routine like that above. See https://www.jazzsoftware.co.nz/Docs/JazzWKLProc.htm for more information.