Comparison
operators and Values
BOOLEAN
Fields, and Fields with CODES or CONDITIONS Properties.
Conditions are widely used in Jazz;
the IF statement, WHERE and UNTIL options of FOR and I/O statements (GET, PROCESS , UPDATE,
DELETE)
all use the same basic rules for conditions. Conditions in Jazz are similar to those in most programming languages but the way
you write the conditions, and the scope of the THEN and ELSE, are more flexible and powerful than you might be used
to.
It is helpful to distinguish several kinds of condition
· Simple Conditions.
· DUPLICATE conditions
· Compound Conditions
· WHERE conditions
Conditions are written
Reference Comparison-operator Value
For example: -
IF R1.Balance > 1000 THEN
….
IF R1.Name = LastRecord.Name THEN
….
“Reference” must refer to a single field if R1 has TYPE SQL, but for other types it may refer to a single field or a GROUP. If it is a GROUP then comparison treats the group as a CHAR field of appropriate length. “Reference” may not be a generic reference.
Value can be another field, a constant, or a numeric expression. If “field” is a group the value is treated as a CHAR field of appropriate length.
When you compare a reference and a constant, the reference must come first. For example you cannot write
IF
1000 <
Record1.
If the field has BOOLEAN type then you may omit the rest of the comparison. Jazz treats
DEFINE
R1 DATA(
Flag BOOLEAN);
IF
R1.Flag THEN;
as equivalent to
IF
R1.Flag =
true THEN;
Comparison operators are: -
Operator |
Meaning |
= |
Equal to |
<> |
Not equal to |
> |
Greater than |
>= |
Greater than or equal to |
< |
Less than |
<= |
Less than or equal to |
IN |
In a list of values |
BETWEEN |
Between two values |
~ or
LIKE |
Pattern match |
DUPLICATE |
Compare with previous value |
IN, BETWEEN, and LIKE follow SQL rules. LIKE, IN and BETWEEN can be written in normal IF and FOR statements, not just in an SQL GET or PROCESS statement’s WHERE condition, but LIKE is not valid in a non-SQL FOR … UNTIL condition. The syntax of these options: -
IF EMPLOYEE.MIDINIT IN ('A', 'C', 'H'); Note the parentheses.
IF EMPLOYEE.SALARY BETWEEN 10000 & 20000;
You probably wrote & as AND. The lower value must be given first: EMPLOYEE.SALARY BETWEEN 20000 & 10000 is always false.
IF EMPLOYEE.NAME LIKE 'Bar%'
Operators and values must be compatible with the field being tested. For example, if the field is numeric then the value must also be numeric. If the field is a group then the value is treated as a CHAR field of appropriate length, and may be a string constant like 'Abcdefg', another group, or a CHAR or VARCHAR field. Arithmetic (numeric) expressions are allowed: for example you can write
IF JZ.IX1
> JZ.IX2
* 2 THEN;
but string expressions are not:
it is not permitted to write IF R.Name = JR.Name1 && R.Name2 THEN;
See DUPLICATE conditions
below for information about the DUPLICATE operator.
Fields defined as type BOOLEAN or with CODES or CONDITIONS properties get special handling, particularly in conditions. You can only test these fields for their special values, and you may only use comparison operators = or <>. Other operators like > imply an order, but coded values don’t have an order: you can’t say that “true is greater than false”. Such a statement is simply meaningless.
A BOOLEAN field is a field that can have only two values, true or false. There are three different formats of BOOLEAN fields, CHAR(1), TINYINT, and SMALLINT, but whichever format you use you will always test the values for the special values true or false, never for the code representation. For example: -
DEFINE
W DATA(
Bool1 BOOLEAN,
Bool2 CHAR(1) BOOLEAN,
Bool3 TINYINT BOOLEAN,
Bool4 SMALLINT BOOLEAN,
X SMALLINT);
IF
W.Bool2 =
true THEN;
W.X = 1;
END
IF;
You may know that CHAR(1) BOOLEAN fields store true as 'Y', but writing a test for 'Y' will result in error
messages
IF
W.Bool2 =
'Y' then;
#065 S Code or
condition value required here
#029 E ''Y'' is invalid here
If the field is BOOLEAN then the condition may simply name the field, implying a test for true. For example: -
IF
W.Bool2 THEN;
is equivalent to the earlier condition
IF
W.Bool2 =
true THEN;
Tests for false are actually tests for “Not true”. Thus a condition
IF W.Bool2 = false THEN;
is true if W.Bool2 has anything other than the true value, which for this format is 'Y'. The value does not have to be 'N' for the condition to be true, and the following condition is identical to the one above: -
IF
W.Bool2 <>
true THEN;
As with BOOLEAN, for a field defined with CODES the condition will refer to the field’s value, not its code. For example, if State is defined: -
State
CHAR(3) CODES(NSW:'
then you’d test
IF
W.State =
not
IF
W.State = Vic THEN;
#065 S Code or
condition value required here
#029 E 'Vic' is
invalid here
As with BOOLEAN, the only valid operators are = and <>.
If a field’s definition includes a CONDITIONS property then there is a predefined condition that can easily be tested for = or <>. For example a record CondTs2 contains
DECLINE-REASON
PIC '9(03)' CONDITIONS(284 TO 295:IBS-VALID-DECL-REASON),
This allows us to write
IF
CondTs2.DECLINE-REASON = IBS-VALID-DECL-REASON
THEN;
to test whether the value of the PIC '9(03)' field is in the range 284 to 295 (inclusive).
CONDITIONS properties can be simple
AFF2
CHAR(4) CONDITIONS('M1 D':MC-STD-CR, …
or give lists of values
AFF2 CHAR(4) CONDITIONS(… , … 'E11M','E12M','E13M','E14M','E15M','E16M','E17M':VALID-AFF, …),
as well as
ranges as above.
COBOL
programmers will recognise CONDITIONS
as “Level 88 definitions”. Note the different
syntax: in Jazz write the reference to a condition as a normal condition
expressed with the = or <> operator: -
IF
CondTs2.AFF2 =
VALID-AFF THEN;
You do not simply name the condition as you would in COBOL: -
IF
VALID-AFF THEN; <= THIS IS NOT VALID
If the value is $Null then the field must have property OPTIONAL, and the comparison can only be = or <>.
Type BOOLEAN. Refer to File.$EndFile, e.g. IN1.$EndFile to test if the file has reached the end.
Type BOOLEAN. A GET statement looks up a record using its WHERE or KEY condition. If there is no such record the record area is initialized so that a record always exists. File.$Found is set True if the GET found a record, False if an initialized record is created.
Type BOOLEAN. Set True if a record is read by GET … UPDATE or PROCESS … UPDATE. If True, the record will be updated at the related END GET/PROCESS. Pending updates can be discarded with File.$UpdatePending = False;
Type ? (depends on the file definition) .Used with VSAM and SQL to calculate the next available key to use when creating a new record.
Type SMALLINT. Special value $CMerge is used to compare merge file keys. With
PROCESS F1 MERGE F2 SKEYS (F1.KEY1, F1.KEY2, F2.KEY1, F2.KEY2) COPY F1OUT;
$CMerge = 0 if the two files match, $CMerge > 0 if F1 > F2, and < 0 if F2 > F1.
More precisely,
IF F1.KEY1 > F2.KEY1 THEN $CMerge = 1
ELSEIF F1.KEY1 < F2.KEY1 THEN $CMerge = -1
ELSEIF F1.KEY2 > F2.KEY2 THEN $CMerge = 2
ELSEIF F1.KEY2 < F2.KEY2 THEN $CMerge = -2
ELSE $CMerge = 0
$CMergeOut behaves like $CMerge, except that instead of comparing F1 against F2, it compares F1OUT. Thus $CMergeOut > 0 if F1OUT > F2, and < 0 if F2 > F1OUT.
These functions are useful when the logic of a PROCESS … MERGE depends on knowing situations such as “Is this a new output record?” For example, this logic will update matching records, but not produce new records from an unmatched transaction: -
PROCESS FILE1 MERGE
FILE2 ON FILE1.KEY1A=FILE2.KEY1B COPY FILEOUT COUNT JZ.IX1;
IF JZ.$CMergeOut = 0;
FILEOUT.BALANCE
+= FIle2.Payment;
IF
JZ.$CMerge > 0;
JZ.COPY-WANTED
= false;
END
IF;
END IF;
PRINT
(JZ.IX1, FILE1.*, FILE2.*, FILEOUT.*) ;
END PROCESS MERGE COPY FILEOUT;
Refer to https://www.jazzsoftware.co.nz/Docs/JazzUGmerge.htm for more information about MERGE logic.
DUPLICATE conditions compare a record or field with the previous value. For example, you might write
PROCESS OLDMSTR MERGE Trans
SKEYS (OLDMSTR.O-KEY,OLDMSTR.O-KEY2,TRANS.T-KEY,TRANS.T-KEY2)
COPY NewMstr SID(20);
IF DUPLICATE OLDMSTR;
#730 W DUPLICATE tests O-KEY,O-KEY2,
JZ2.FLAG1 = True;
ELSE;
JZ2.FLAG1 = False;
END IF;
DISPLAY('Duplicate(OLDMSTR=' JZ2.FLAG1);
END PROCESS MERGE COPY NewMstr;
A DUPLICATE test names a
record, a field, or a group. In the
example above, OLDMSTR is one of the input
files read by PROCESS OLDMSTR … Here’s
another example, this time DUPLICATE tests a field from an array: -
FOR TABL.O-KEY(JZ2.IX);
IF DUPLICATE Tabl.O-KEY(IX);
JZ2.FLAG1 = True;
ELSE;
JZ2.FLAG1 = False;
END IF;
DISPLAY(JZ2.IX, JZ2.FLAG1);
*#Copy Process logic here ==>
END FOR;
If the test names a Record or Group, then DUPLICATE will test the relevant SKEY, TKEY, or KEY fields. Key properties may be derived from the SKEYS or ON option of the PROCESS statement, as in the examples above, or from properties given in the relevant DEFINE statement. If no key properties are found, then the whole record is tested. Message #730 will tell you what is being tested.
If the test names a single field, as in DUPLICATE Tabl.O-KEY(IX) then the field is tested and message #730 is not produced.
Save variables are generated into the COBOL program to hold the values of the previous record. For example, from
PROCESS OLDMSTR MERGE Trans
SKEYS (OLDMSTR.O-KEY,OLDMSTR.O-KEY2,TRANS.T-KEY,TRANS.T-KEY2)
COPY NewMstr SID(20);
IF DUPLICATE OLDMSTR;
#730
W DUPLICATE tests O-KEY,O-KEY2,
the generated COBOL contains
001480 03 JZ-22-DUP PIC X VALUE SPACES.
DupTst
001490 03 JZ-22-DUP-SAVE PIC 99 VALUE ZERO. DupTst
001500 03 JZ-22-DUP-SAVE1 PIC XXXXX VALUE SPACES.
DupTst
The format of these variables is the same as the corresponding key fields, OLDMSTR.O-KEY and OLDMSTR.O-KEY2. If no key fields have been detected, then there will be a single save field with format PIC X(nn) where nn is the record length.
The field JZ-22-DUP is a COBOL BOOLEAN field which would normally hold 'Y' or 'N', but has been initialized to SPACES. This fact is used in the generated COBOL logic, which always returns False from the first test. This guards against the possibility that the very first record might have the initialization values.
DUPLICATE tests for duplication at the point in the program where it is written, not at the beginning of the loop, which may be directly within the PROCESS loop, or within a ROUTINE invoked by PERFORM. Each DUPLICATE test is unique, and will have its own set of save variables and result flag, so if you repeat a DUPLICATE test the results will be recalculated and will reflect changes in the object tested. It is recommended that if you need to know the results of the test in more than one place, you should save the result, either with IF logic as above or else a direct assignment: -
JZ2.FLAG1 = DUPLICATE Tabl.O-KEY(IX);
You can combine several conditions using &
(meaning AND) and | (meaning OR). & and | are called “Boolean Operators”. For example: -
IF R1.X > R1.Y & R1.Flag THEN;
Here we
have combined a simple comparison of two numbers with a test of a BOOLEAN field, using an implied = true.
In the absence of parentheses conditions are evaluated left to
right. Parentheses are particularly
vital when you are using |
(OR). For example the two tests
IF R1.X > R1.Y & R1.Flag | R1.Flag2 THEN;
IF R1.X > R1.Y & (R1.Flag | R1.Flag2) THEN;
give
different results.
You will probably write these conditions using
AND and OR.
Jazz will recognize the use of these words in this context and replace
them with the formal operators & |.
DUPLICATE conditions cannot be part of a Compound
Condition.
WHERE conditions are written just like conditions in
IF statements, but in some circumstances they may
behave slightly differently as the way that a WHERE works
depends on the file type from which records are being read.
Case
#1: filtering a sequential file. In a BATCH program
you can read a physical-sequential file (type F, FB, etc). Every record is read into
your program, but you can use WHERE to discard the records that you don’t want: -
PROCESS File WHERE
(File.
….
END PROCESS File;
Here the WHERE clause is exactly like an IF, and this logic is equivalent to
PROCESS File;
IF File.
….
END IF;
END PROCESS File;
Case
#2. Reading VSAM with PROCESS or GET. As with a sequential file you can write PROCESS file WHERE(condition);, but unlike a sequential file you
can write such statements within a loop (perhaps an outer PROCESS), and within CICS programs. Here the WHERE is not
simply a filter: it specifies that particular records are read using a
“Browse”, and the fields named must have been defined with a key property (KEY, DKEY, or UKEY), or be within a key GROUP. Comparison operators must be =
or >=: the PROCESS will attempt a keyed read and then return all
the records meeting the criteria. With GET, if there are many records that meet the
criteria the first will be returned in a batch program, but several may be
returned with a paging process with classical CICS and web services.
Case
#3. Reading SQL with PROCESS or GET. Within
Jazz PROCESS File WHERE(condition) is the same whether File
is a VSAM record set or a table in an SQL database. In COBOL however the
implementation is very different: in the
case of VSAM records are read and considered by your
program whereas for SQL your program uses a SELECT statement that
instructs the database which records it wants, and the unwanted records are
never seen. You may not know which
fields within the database are keys, and only your program’s performance will
tell you whether you are scanning the whole database to find the record that
you want or going straight to them with keyed retrieval.
Null values (in Jazz, $Null) are handled differently in SQL. To SQL a NULL field does not have a value at all, so its value can never be equal to, or greater than or less than, any other value. Even another NULL value! So that
SQLT2.Charfield
= $Null;
PROCESS SQLTab WHERE (SQLTab.charfield = SQLT2.charfield) …
returns NO RECORDS. It DOES NOT return all the records from SQLTab with NULL values. If you want these records then you write
PROCESS
SQLTab WHERE (SQLTab.charfield = $NULL) …
…