Multiplication division using DFSORT utility in Mainframe - mainframe

There are two files FILE1.DATA and FILE2.DATA
To calculate percentage (Number of records in FILE1/Number of records in FILE2)*100 using DFSORT in Mainframe. And setting Return Code if it crossing a threshold (90%).
//********Extracting Unique records data*****************
//SORTT000 EXEC PGM=SORT
//SYSOUT DD SYSOUT=*
//SORTIN DD DSN=SAMPLE.DATA1,DISP=SHR
//SORTOUT DD DSN=FILE1.DATA,
// SPACE=(2790,(5376,1075),RLSE),
// UNIT=TSTSF,
// DCB=(RECFM=FB,LRECL=05,BLKSIZE=0),
// DISP=(NEW,CATLG,DELETE)
//SYSIN DD *
SORT FIELDS=(10,5,CH,A)
OUTREC FIELDS=(1:10,5)
SUM FIELDS=NONE
/*
//************Getting count of records*****************
//STEP001 EXEC PGM=ICETOOL
//TOOLMSG DD SYSOUT=*
//DFSMSG DD SYSOUT=*
//SYSOUT DD SYSOUT=*
//SYSPRINT DD SYSOUT=*
//IN1 DD DISP=SHR,DSN=FILE1.DATA
//IN2 DD DISP=SHR,DSN=FILE2.DATA
//OUT1 DD DSN=FILE1.DATA.COUNT,
// SPACE=(2790,(5376,1075),RLSE),
// UNIT=TSTSF,
// DCB=(RECFM=FB,LRECL=06,BLKSIZE=0),
// DISP=(NEW,CATLG,DELETE)
//OUT2 DD DSN=FILE2.DATA.COUNT,
// SPACE=(2790,(5376,1075),RLSE),
// UNIT=TSTSF,
// DCB=(RECFM=FB,LRECL=06,BLKSIZE=0),
// DISP=(NEW,CATLG,DELETE)
//TOOLIN DD *
COUNT FROM(IN1) WRITE(OUT1) DIGITS(6)
COUNT FROM(IN2) WRITE(OUT2) DIGITS(6)
/*
//*******Calculating percentage and if above 90% setting RC 04*****
//STEP002 EXEC PGM=SORT
//SYSOUT DD SYSOUT=*
//SORTIN DD DSN=FILE2.DATA.COUNT,DISP=SHR
// DD DSN=FILE1.DATA.COUNT,DISP=SHR
//SORTOUT DD DSN=FILE.DATA.COUNT.OUT,
// SPACE=(2790,(5376,1075),RLSE),
// UNIT=TSTSF,
// DCB=(RECFM=FB,LRECL=80,BLKSIZE=0),
// DISP=(NEW,CATLG,DELETE)
//SETRC DD SYSOUT=*
//SYSIN DD *
INREC IFTHEN=(WHEN=INIT,BUILD=(1,6,X,6X'00',SEQNUM,1,ZD,80:X)),
IFTHEN=(WHEN=(14,1,ZD,EQ,2),OVERLAY=(8:1,6))
SORT FIELDS=(7,1,CH,A),EQUALS
SUM FIELDS=(8,4,BI,12,2,BI)
OUTREC OVERLAY=(15:X,1,6,ZD,DIV,+2,M11,LENGTH=6,X,
(8,6,ZD,MUL,+100),DIV,1,6,ZD,MUL,+100,EDIT=(TTT.TT))
OUTFIL FNAMES=SETRC,NULLOFL=RC4,INCLUDE=(23,6,CH,GT,C'090.00')
OUTFIL BUILD=(05:C'TOTAL NUMBER RECRODS IN FILE2 : ',1,6,/,
05:C'TOTAL NUMBER RECRODS IN FILE1 : ',8,6,/,
05:C'PERCENTAGE : ',23,6,/,
80:X)
//*
The problem I am facing is datasets FILE1.DATA.COUNT and FILE1.DATA.COUNT are getting created of 15 record length despite mentioning LRECL 6. (note, this was the question that existed when the first answer was written and does not relate now to the above code).
Can we merge both steps into one?
What does this, (15:X,1,6,ZD,DIV,+2,M11,LENGTH=6,X, (8,6,ZD,MUL,+100),DIV,1,6,ZD,MUL,+100,EDIT=(TTT.TT)), mean specifically?

The answer to your first question is simply that you did not tell
ICETOOL's COUNT operator how long you wanted the output data to be, so
it came up with its own figure.
This is from the DFSORT Application Programming Guide:
WRITE(countdd) Specifies the ddname of the count data set to be
produced by ICETOOL for this operation. A countdd DD statement must be
present. ICETOOL sets the attributes of the count data set as follows:
v RECFM is set to FB.
v LRECL is set to one of the following:
– If WIDTH(n) is specified, LRECL is set to n. Use WIDTH(n) if your count
record length and LRECL must be set to a particular value (for
example, 80), or if you want to ensure that the count record length
does not exceed a specific maximum (for example, 20 bytes).
– If WIDTH(n) is not specified, LRECL is set to the calculated required
record length. If your LRECL does not need to be set to a particular
value, you can let ICETOOL determine and set the appropriate LRECL
value by not specifying WIDTH(n).
And:
DIGITS(d)
Specifies d digits for the count in the output record, overriding the
default of 15 digits. d can be 1 to 15. The count is written as d
decimal digits with leading zeros. DIGITS can only be specified if
WRITE(countdd) is specified.
If you know that your count requires less than 15 digits, you can use
a lower number of digits (d) instead by specifying DIGITS(d). For
example, if DIGITS(10) is specified, 10 digits are used instead of 15.
If you use DIGITS(d) and the count overflows the number of digits
used, ICETOOL terminates the operation. You can prevent the overflow
by specifying an appropriately higher d value for DIGITS(d). For
example, if DIGITS(5) results in overflow, you can use DIGITS(6)
instead.
And:
WIDTH(n)
Specifies the record length and LRECL you want ICETOOL to use for the
count data set. n can be from 1 to 32760. WIDTH can only be specified
if WRITE(countdd) is specified. ICETOOL always calculates the record
length required to write the count record and uses it as follows:
v If WIDTH(n) is specified and the calculated record length is less
than or equal to n, ICETOOL sets the record length and LRECL to n.
ICETOOL pads the count record on the right with blanks to the record
length.
v If WIDTH(n) is specified and the calculated record length is greater
than n, ICETOOL issues an error message and terminates the operation.
v If WIDTH(n) is not specified, ICETOOL sets the record length and
LRECL to the calculated record length.
Use WIDTH(n) if your count record length and LRECL must be set to a
particular value (for example, 80), or if you want to ensure that the
count record length does not exceed a specific maximum (for example,
20 bytes). Otherwise, you can let ICETOOL calculate and set the
appropriate record length and LRECL by not specifying WIDTH(n).
For your second question, yes it can be done in one step, and greatly simplified.
The thing is, it can be further simplified by doing something else. Exactly what else depends on your actual task, which we don't know, we only know of the solution you have chosen for your task.
For instance, you want to know when one file is within 10% of the size of the other. One way, if on-the-dot accuracy is not required, is to talk to the technical staff who manage your storage. Tell them what you want to do, and they probably already have something you can use to do it with (when discussing this, bear in mind that these are technically data sets, not files).
Alternatively, something has already previously read or written those files. If the last program to do so does not already produce counts of what it has read/written (to my mind, standard good practice, with the program reconciling as well) then amend the programs to do so now. There. Magic. You have your counts.
Arrange for those counts to be in a data set of their own (preferably with record-types, headers/trailers, more standard good practice).
One step to take the larger (expectation) of the two counts, "work out" what 00% would be (doesn't need anything but a simple subtraction, with the right data) and generate a SYMNAMES format file (fixed-length 80-byte records) with a SORT-symbol for a constant with that value.
Second step which uses INCLUDE/OMIT with the symbol in comparison to the second record-count, using NULLOUT or NULLOFL.
The advantage of the above types of solution is that they basically use very few resources. On the Mainframe, the client pays for resources. Your client may not be so happy at the end of the year to find that they've paid for reading and "counting" 7.3m records just so that you can set an RC.
OK, perhaps 7.3m is not so large, but, when you have your "solution", the next person along is going to do it with 100,000 records, the next with 1,000,000 records. All to set an RC. Any one run of which (even with the 10,000-record example) will outweigh the costs of a "Mainframe" solution running every day for the next 15+ years.
For your third question:
OUTREC OVERLAY=(15:X,1,6,ZD,DIV,+2,M11,LENGTH=6,X,
(8,6,ZD,MUL,+100),DIV,1,6,ZD,MUL,+100,EDIT=(TTT.TT))
OUTREC is processed after SORT/MERGE and SUM (if present) otherwise after INREC. Note, the physical order in which these are specified in the JCL does not affect the order they are processed in.
OVERLAY says "update the information in the current record with these data-manipulations (BUILD always creates a new copy of the current record).
15: is "column 15" (position 15) on the record.
X inserts a blank.
1,6,ZD means "the information, at this moment, at start-position one for a length of six, which is a zoned-decimal format".
DIV is divde.
+2 is a numeric constant.
1,6,ZD,DIV,+2 means "take the six-digit number starting at position one, and divide it by two, giving a 'result', which will be placed at the next available position (16 in your case).
M11 is a built-in edit-mask. For details of what that mask is, look it up in the manual, as you will discover other useful pre-defined masks at the time. Use that to format the result.
LENGTH=6 limits the result to six digits.
So far, the number in the first six positions will be divided by two, treated (by the mask) as an unsigned zoned-decimal of six digits, starting from position 16.
The remaining elements of the statement are similar. Brackets affect the "precedence" of numeric operators in a normal way (consult the manual to be familiar with the precedence rules).
EDIT=(TTT.TT) is a used-defined edit mask, in this case inserting a decimal point, truncating the otherwise existing left-most digit, and having significant leading zeros when necessary.

Related

Understand the following control cards

I have the following control cards that I can't understand how to read. Could someone help me traduce what this part of a JOB is performing?
OUTFIL FNAMES=(XSCB),BLKCCT1,INCLUDE=(67,7,CH,EQ,
C'XSCB ',OR,69,7,CH,EQ,
C'XSCB '),
HEADER2=(22:C'XSCB MVS USERID SYSTEM USAGE REPORT',/,
01:C'GENERATED ON ',&DATE=(MD4/),70:C'PAGE',&PAGE,/,
01:C' AT ',&TIME,/,X,/,
01:C'JULIAN',/,
01:C'DATE TIME SYSTEM JOB MESSAGE',/,
01:C'-------- -------- ------ -------- ---------------->'),
TRAILER1=(X,/,01:C'RECORDS FOUND =',COUNT,/,34:C'END OF REPORT'),
OUTREC=(20,07,ZD,EDIT=(TTTT.TTT),X, * JULIAN DATE
28,08,X, * TIME
11,06,X, * SYSTEM
40,08,X, * JOB OR REF
59,07,CHANGE=(50,C'IEF125I',C'LOGGED ON ', * MESSAGE
C'IEF126I',C'LOGGED OFF'),
NOMATCH=(79,50),
132:X)
I understand that it searches the ID 'XSCB' in the position 67 or 69. But once it finds it, I cannot interpret what it does next.
Those are SORT control cards. If you look at the SYSOUT for the step, and pay attention to the messages, you will be able to tell if it is DFSORT (messages prefixed by ICE) or SyncSORT (messages prefixed by WER).
Your step may be EXEC PGM=SORT or ICEMAN or something else, depends on your site.
The control cards are producing a report. You have at least one line missing from your control cards (OPTION COPY, or SORT FIELDS=COPY or a different SORT or MERGE statement). There could be any number of missing cards, and you possibly have another output from the step. Otherwise the OUTFIL INCLUDE= could perhaps be a plain INCLUDE COND=.
What does what you have shown actually do?
OUTFIL defines final processing for a particular output data set. With no name, it would be for the SORTOUT DD in your JCL.
With FNAMES=(XSCB) it is for a DD named XSCB in your JCL. For a single name specified in FNAMES, the brackets are redundant.
BLKCTT1 says "put a blank in column one to not get a page-eject from TRAILER1 output".
The INCLUDE= is as you suspect. Testing two different starting positions for the same value. If either test is true, the current record will be included in the OUTFIL group.
HEADER2 defines what appears at the top of each page.
The 01: is a column-number, and is redundant, as each line by default starts are column one.
HEADER2 can create multiple lines (as can any HEADERn or TRAILERn and BUILD (or OUTREC, but don't use it for new) on OUTFIL), each separated by "/". &DATE, &TIME and &PAGE are special, containing the obvious. &DATE can be formatted in various ways, MD4/ is MM, DD, YYYY separated by slashes.
The X is a blank, on a line of its own. You could equally see .../,/... or n/ to create n multiple blank lines.
The constants should be obvious.
TRAILER1 defines what is printed at the end of the report.
COUNT is the number of records in the OUTFIL group, here used with no formatting, but it can be formatted.
The 34: column-number means the items following will start from column 34.
The OUTREC is better spelled as BUILD. OUTREC exists elsewhere. BUILD has been around for more than 10 years, so no need to use OUTREC on OUTFIL in new code (maybe this is old anyway).
What the BUILD would do is format the current input record into what is desired for an output line on the report.
The numbers in pairs are start-position and length of fields. Where no field-type is defined, they are (treated as) character fields.
You have one field-type, ZD, which is zoned-decimal. Its length is seven, and an EDIT mask is used, four digits, full-stop (decimal-point) and then three digits.
The Xs as previously are blanks, used as separators on the report. The content of each field is described in a comment. A comment is any text after the end of a control card. A control card ends at the blank after the statement is complete, or where a there is a blank after a possible continuation (a comma or a colon are possible continuations).
132:X puts a blank in column 132, and pads any intervening columns from the last field or constant with blanks.
That leaves the CHANGE=.
CHANGE= is a very useful test-and-replace.
79,50,CHANGE=(50,C'IEF125I',C'LOGGED ON ', * MESSAGE
C'IEF126I',C'LOGGED OFF'),
NOMATCH=(79,50)
This says "at the current column of the record being created, consider the content of the input from position 79 for a length of 50. The output length will be 50. If IEF125I, then use the constant LOGGED ON, if IEF126I use LOGGED OFF, and else (NOMATCH) use whatever is at position 79 for a length of 50 from the input.
Basically, the report is using the system log, or an extract from it, to report activity related to the Userid/Logon XSCB.

Sort on length of field

I want to write a sort JCL with requirement where I want to sort on variable record length file
Input file:
Mark aaaaaaa
Amy bbbbbb
Paula ccccccccccc
Sort on the length of field before spaces on ascending order. That is sorting on length of first col/word Mark,Amy etc.. On basis of their length.
And second one is like performing sort on field after spaces on descending order but if any vowels in field should always be first and then rest of data.
Coming on second part ,here it's like the fields after spaces or aaaaa, bbbbb and ccccc we need to sort it in descending order (alphabetically) ,but then we also need to check if the field is vovel ,if any vovel then that field will be always as top, so the expected output will be like:
Considering above input file output file will be:
Mark aaaaaaaa
Paula cccccc
Amy bbbbbb
Now here vovel as in first record which contains aaaa in it is at top and rest data is sorted in descending order. I want to achieve this.
What you are asking is not at all a simple thing :-)
Whilst DFSORT has much intrinsic functionality, finding the length of a sequence of non-space characters is not available.
So you have to roll-your-own.
Although the task is also possible with fixed-length records (different technique) it is easier with variable-length records.
Because the fields are variable-length as well, you'll need PARSE to separate the fields. For variable-length or variably-located fields, PARSE is usually the answer.
PARSE creates fixed-length parsed fields, so you have to know the maximum lengths of your text. In this example 30 is chosen for each.
The solution will develop piece by piece, because you will need to be secure in your understanding of it. The pieces are presented as "stand alone" code which you can run and see what happens:
OPTION COPY
INREC IFTHEN=(WHEN=INIT,
PARSE=(%01=(ENDBEFR=C' ',
FIXLEN=30),
%02=(FIXLEN=30))),
IFTHEN=(WHEN=INIT,
BUILD=(1,4,%01,%02))
If you run that, you will get this output:
MARK AAAAAAA
AMY BBBBBB
PAULA CCCCCCCCCCC
INREC runs before a SORT, so to make any changes to the data before a SORT, you use INREC. OUTREC runs after SORT, and OUTFIL after OUTREC.
For now, the BUILD is just to show that the PARSEd fields contain the output you want (don't worry about the case, if you used mixed-case it will be like that).
WHEN=INIT means "do this for each record, before the following IFTHEN statements (if any)". You can use multiple WHEN=INIT, and you have to use multiple IFTHEN of some type to transform data in multiple stages.
The 1,4 in the BUILD is for the Record Descriptor Word (RDW) which each variable-length record hase, and is always necessary when creating a variable-length current record in SORT, but we'll use it for another purpose here as well.
The next stage is to "extend" the records, because we need two fields to SORT on. For a variable-length record, you extend "at the front". In general:
BUILD=(1,4,extensionstuff,5)
This makes a new version of the current record, with first the RDW from the old current record, then "does some stuff" to create the extension, then copies from position 5 (the first data-byte on a variable-length record) to the end of the record.
Although the RDW is "copied", the value of the RDW at the time is irrelevant, as it will be calculated for the BUILD. It just must be an RDW to start with, you can't just put anything there except an actual RDW.
Another component that will be needed is to extend the records for the SORT key. We need the length of the first field, and we need a "flag" for whether or not to "sort early" for the second field containing a vowel. For the length it will be convenient to have a two-byte binary value. For now, we are just reserving bytes for the things:
OPTION COPY
INREC BUILD=(1,4,2X,2X,X,5)
The 2X is two blanks, the X is one blank, so a total of five blanks. It could have been written as 5X, and in the final code is best that way, but for now it is clearer. Run that and you will see your records prefixed by five blanks.
There are two tasks. The length of the first field, and whether the second field contains a vowel.
The key to the first task is to replace blanks from the PARSEd field with "nothing". This will cause the record to be shortened by one for each blank replaced. Saving the length of the original current record, and calculating with the length of the current record and the fixed-length (30) reveals the length of the data.
The key to the second task applies a similar technique. This time, change the second PARSEd field such that a, e, i, o, u are replaced by "nothing". Then if the length is the same as the original, there were no vowels.
The FINDREP will look something like this:
IFTHEN=(WHEN=INIT,
FINDREP=(IN=C' ',
OUT=C'',
STARTPOS=n1,
ENDPOS=n2)),
You'll need a variant for the vowels:
IFTHEN=(WHEN=INIT,
FINDREP=(IN=(C'A',C'E',C'I',C'O',C'U'),
OUT=C'',
STARTPOS=n1,
ENDPOS=n2)),
To run:
OPTION COPY
INREC IFTHEN=(WHEN=INIT,
PARSE=(%01=(ENDBEFR=C' ',
FIXLEN=30),
%02=(FIXLEN=30))),
IFTHEN=(WHEN=INIT,
BUILD=(1,4,2X,X,%02)),
IFTHEN=(WHEN=INIT,
OVERLAY=(5:1,2)),
IFTHEN=(WHEN=INIT,
FINDREP=(IN=(C'A',
C'E',
C'I',
C'O',
C'U'),
OUT=C'',
STARTPOS=8,
ENDPOS=38)),
IFTHEN=(WHEN=(1,4,BI,EQ,5,2,BI),
OVERLAY=(7:C'N'))
If you run that, you will see the flag (third data-position) is now space (for a vowel present) or "N". Don't worry that all the "A"s have disappeared, they are still tucked away in %02.
OVERLAY can make changes to the current record without creating a new, replacement record (which is what BUILD does). You'll see OVERLAY used below to get the new record-length after the a new current record-length has been created (the BUILD would get the original record-length from the RDW).
A similar process for the other task.
I've included some additional test-data and made further assumptions about your SORT order. Here's full, annotated (the comments can remain, they do not affect the processing), code:
* PARSE CURRENT INPUT TO GET TWO FIELDS, HELD SEPARATELY FROM THE RECORD.
*
INREC IFTHEN=(WHEN=INIT,
PARSE=(%01=(ENDBEFR=C' ',
FIXLEN=30),
%02=(FIXLEN=30))),
* MAKE A NEW CURRENT RECORD, RDW FROM EXISTING RECORD, THREE EXTENSIONS, AND
* A COPY OF THE FIRST PARSED FIELD.
*
IFTHEN=(WHEN=INIT,
BUILD=(1,4,
2X,
2X,
X,
%01)),
* STORE THE LENGTH OF THE NEW CURRENT RECORD ON THE CURRENT RECORD.
*
IFTHEN=(WHEN=INIT,
OVERLAY=(5:
1,2)),
* REPLACE BLANKS WITH "NOTHING" WITHIN THE COPY OF THE PARSED FIELD. THIS WILL
* AUTOMATICALLY ADJUST THE RDW ON THE CURRENT RECORD.
*
IFTHEN=(WHEN=INIT,
FINDREP=(IN=C' ',
OUT=C'',
STARTPOS=10,
ENDPOS=40)),
* CALCULATE THE LENGTH OF THE NON-BLANKS IN THE FIELD, BY SUBTRACTING PREVIOUS
* STORED RECORD-LENGTH FROM CURRENT RECORD-LENGTH (FIRST TWO BYTES, BINARY, OF
* RDW) AND ADDING 30 (LENGTH OF PARSED FIELD).
*
IFTHEN=(WHEN=INIT,
OVERLAY=(5:
1,2,BI,
SUB,
5,2,BI,
ADD,
+30,
TO=BI,
LENGTH=2)),
* MAKE A NEW CURRENT RECORD, COPYING RDW AND THE VALUE CALCULATED ABOVE, BLANKS
* (COULD BE COPIED) AND THEN THE SECOND PARSED FIELD.
*
IFTHEN=(WHEN=INIT,
BUILD=(1,4,
5,2,
2X,
X,
%02)),
* AGAIN SAVE THE LENGTH OF THE NEW CURRENT RECORD.
*
IFTHEN=(WHEN=INIT,
OVERLAY=(7:
1,2)),
* CHANGE ALL VOWELS TO "NOTHING". THIS WILL AUTOMATICALLY ADJUST THE RDW. FOR
* MIXED-CASE JUST EXTEND THE IN TO INCLUDE LOWER-CASE VOWELS AS WELL.
*
IFTHEN=(WHEN=INIT,
FINDREP=(IN=(C'A',
C'E',
C'I',
C'O',
C'U'),
OUT=C'',
STARTPOS=10,
ENDPOS=40)),
* CALCULATE NUMBER OF VOWELS.
*
IFTHEN=(WHEN=INIT,
OVERLAY=(7:
7,2,BI,
SUB,
1,2,BI,
TO=BI,
LENGTH=2)),
* MAKE A NEW CURRENT RECORD TO BE SORTED, WITH BOTH PARSED FIELDS.
*
IFTHEN=(WHEN=INIT,
BUILD=(1,4,
5,2,
7,2,
9,1,
%01,
%02)),
* SET THE FLAG TO "OUTSORT" THOSE RECORDS WITH A VOWEL IN THE SECOND FIELD.
*
IFTHEN=(WHEN=(7,2,BI,EQ,0),
OVERLAY=(9:
C'N'))
* SORT ON "OUTSORT FLAG", LENGTH OF NAME (DESCENDING), NAME, 2ND FIELD.
SORT FIELDS=(9,1,CH,A,
5,2,CH,D,
10,30,CH,A,
40,30,CH,A)
* FIELDS NEEDED TO BE IN FIXED POSITION FOR SORT, AND EXTENSION FIELDS NO
* LONGER NEEDED. ALSO REMOVE BLANKS FROM THE TWO FIELDS, KEEPING A SEPARATOR
* BETWEEN THEM. THIS COULD INSTEAD BE DONE ON THE OUTFIL.
*
OUTREC BUILD=(1,4,
10,60,
SQZ=(SHIFT=LEFT,
MID=C' '))
* CURRENTLY THE VARIABLE-LENGTH RECORDS ARE ALL THE SAME LENGTH (69 BYTES) SO
* REMOVE TRAILING BLANKS.
*
OUTFIL VLTRIM=C' '
Extensive test-data:
MARK AAAAAAA
AMY BBBBBB
PAULA CCCCCCCCCCC
PAULA BDDDDDDDDDD
IK JJJJJJJJJJO
You can also see how the code works by "removing a line at a time" from the end of the code, so you can see how the transformation reaches that point, or by running the code increasing a line at a time from the start of the code.
It is important that you, and your colleagues, understand the code.
There are some opportunities for some rationalisation. If you can work those out, it means you understand the code. Probably.

How to compare two totals from different datasets

We have three different file one is like that
File A
000001000
000002000
000003000
000004000
File B (After Summing of all Records in file A)
000010000
File C
Total : - 10000
I have to compare the value in the File C and File B and if the value matches successfully I have to set the desired Return Code RC.
The starting position of the word "Total" is five.
I also find the solution of the problem using the JOINKEY.
The code is given below.
//STEP1 EXEC PGM=SORT,PARM=’NULLOUT=RC4′
//SORTJNF1 DD DSN=FILEB
//SORTJNF2 DD DSN=FILEC
//SORTOUT DD SYSOUT=*
//SYSOUT DD SYSOUT=*
//SYSIN DD *
JOINKEYS FILE=F1,FIELDS=(1,9,A)
JOINKEYS FILE=F2,FIELDS=(5,9,a)
REFORMAT FIELDS=(F1:1,80)
OPTION COPY
/*
When the Match is success full it will return the RC=0
And
When Match is not success full it will return the RC=4 as it is supplied using the PARM.
Basically it search for the record in the output file (sortout) if Match successful the jcl sort utility will copy the record from fileB into the Output file(in spool) and if the unsuccessful match then the output file is empty and the with the help of PARM=’NULLOUT=RC4′ it return the RC=4.
Why you wouldn't use JOINKEYS?
A professional would know not to use JOINKEYS for this task or would recognise specific advice against using it.
For everyone else, here's why not.
JOINKEYS consists of three "Tasks", the Main Task and two Sub-Tasks (one for each input dataset). This means JOINKEYS can use up to three times the memory of a plain SORT/MERGE/COPY. This means a JOINKEYS step can be more difficult to "select" and can keep other JOBs from being selected. It also means a JOINKEYS step is going to be slower than an if an equivalent solution is possible not using JOINKEYS (this can depend on the exact solution, but is true for anything trivial).
More memory, more CPU, more elapsed time, more impact on other JOBs.
Only use JOINKEYS where necessary. Necessary does not mean "so I can type/copy-paste less code", it means when a good solution requires the use of JOINKEYS.
If a solution does not require JOINKEYS, then don't use JOINKEYS. Someone pays for the extra resources and impacts. A professional avoids such costs.
Of course, such advice is free, and you get what you pay for at times. However, I am the DFSORT Moderator at www.ibmmainframes.com, taking over that task from Frank Yaeger of IBM, the inventor of the modern DFSORT.
Plus in the sample code given by https://stackoverflow.com/users/5433120/sharad-singhal, the JOINKEYS won't even work with the data that they themselves showed in the question, as the field-types are different (one left-zero filled, the other leading-zero truncated, right-space-padded).
You can of course normalise the second key, using a JNF2CNTL DD and some code (the code needed is included here).
But. Even. Though. It. Can. Be. Made. To. Work. JOINKEYS. Is. A. Bad. Solution. For. This. Task.
I hope this is clear for any future searchers.
I have to set the desired Return Code RC
This is impossible, except by coincidence. The only Condition Code/Return Code (CC/RC) available to you from DFSORT is zero, four and 16.
Perhaps that provides one of the CC/RC which you desired. If not, change your desire. Or do it the odd way around by using an IDCAMS step to "convert" the CC/RC you get from DFSORT to the CC/RC you so desperately want.
The only way you can get, with correct control cards, other than a zero RC is by having an empty output file (either SORTOUT or an OUTFIL dataset).
You want to SUM. SUM requires SORT or MERGE. You can also use OUTFIL reporting functions, REMOVECC and TOTAL to get sums, but by that time you don't have an opportunity to test that two totals are equal, or not.
Matching datasets is a good task for JOINKEYS.
You can also code the summation yourself.
Resource-wise and since you are probably a beginner, the best solution will be the MERGE.
You need two DD's in your JCL, SORTIN01 and SORTIN02, in place of SORTIN which you would usually have for a SORT step. You will also need at least one output DD, which can be SORTOUT, but best as another, and this should be set to DUMMY or DSN=NULLFILE.
You need to arrange that all the value records get a "key" which is equal, so that they can be SUMmed, and your Total record should get a different value.
//MATCHTOT EXEC PGM=SORT
//SYMNAMES DD *
* IN- is the input records with values (and the summed value later)
* EXT- are temporary extensions made to the record for processing
* TOT- is the total record
* IND- is an indicator determining wheter value or total record
IN-RECORD,*,80,CH
EXT-IN-IND,*,1,CH
EXT-TOT-TOTAL-VALUE,*,9,zd
POSITION,IN-RECORD
IN-VALUE,=,9,ZD
IN-SUM-VALUE,=,=,=
POSITION,IN-RECORD
SKIP,5
TOT-TOTAL-NAME,*,5,CH
SKIP,5
TOT-TOTAL-VALUE,*,9,CH
* Constants
TOTAL-TEXT,C'Total'
IND-TOT-TOTAL,C'0'
IND-IN-VALUE,C'5'
CLOBBER-FIRST-PART,C'000000000'
//SYMNOUT DD SYSOUT=*
//CHECK DD DUMMY
//SYSOUT DD SYSOUT=*
//SORTOUT DD SYSOUT=*
//FILEB DD SYSOUT=*
//SYSIN DD *
INREC IFTHEN=(WHEN=(TOT-TOTAL-NAME,
EQ,
TOTAL-TEXT),
OVERLAY=(EXT-IN-IND:
IND-TOT-TOTAL,
IN-VALUE:
CLOBBER-FIRST-PART)),
IFTHEN=(WHEN=NONE,
OVERLAY=(EXT-IN-IND:
IND-IN-VALUE))
MERGE FIELDS=(EXT-IN-IND,A)
SUM FIELDS=(IN-VALUE)
OUTREC IFTHEN=(WHEN=GROUP,
BEGIN=(EXT-IN-IND,
EQ,
IND-TOT-TOTAL),
PUSH=(EXT-TOT-TOTAL-VALUE:
TOT-TOTAL-VALUE)),
IFTHEN=(WHEN=INIT,
OVERLAY=(EXT-TOT-TOTAL-VALUE:
EXT-TOT-TOTAL-VALUE,UFF,
TO=ZD,
LENGTH=9))
OUTFIL FNAMES=CHECK,
INCLUDE=(EXT-IN-IND,
EQ,
IND-IN-VALUE,
AND,
IN-SUM-VALUE,
EQ,
EXT-TOT-TOTAL-VALUE),
NULLOFL=RC4
OUTFIL FNAMES=FILEB,
INCLUDE=(EXT-IN-IND,CH,EQ,IND-IN-VALUE),
BUILD=(IN-RECORD)
//SORTIN01 DD *
000001000
000002000
000003000
000004000
//SORTIN02 DD *
Total : - 20000
This uses DFSORT symbols/SYMNAMES. These are defined on the SYMNAMES DD (best as a PDSE (or PDS) member, fixed-length 80-byte records.
The SYMNOUT DD statement lists the source symbols, and the normalised symbols which will by used by DFSORT to translate the control cards.
In INREC, the key for the merge is established and in the case of the Total record, the input positions (no longer required) that match the value on the value records are set to zero. Any data from an input file which is not needed for output can be happily destroyed (if it is not written, it is not written, so its content is irrelevant and can be amended if convenient to do so).
The MERGE then uses the key just established. This is just a trick to allow SUM to be used. All the value records (key five) will be SUMmed to an actual value, the total record will be left as it is (since its key is unique).
In OUTREC, the total from the Total record will be PUSHed to the next record (the SUMmed values) using WHEN=GROUP. The Unsigned Free Format data of the Total record will be converted to a Zoned Decimal value.
There are then two OUTFIL statements.
The first looks at the SUMmed record and compares the value to the value PUSHed from the Total record. If they are equal, a record is written to the OUTFIL (which is DUMMY in the JCL). The RC4 will not be set. If the values are different, no record will be written and the RC4 will be set.
The second OUTFIL is to create your FILEB, which is not itself needed as a file. If you don't need FILEB for anything else, you can remove this OUTFIL.
I have left the SORTOUT DD in the JCL so you can see what happens to the records. You can remove this once you are happy you know what is going on.
The only other RC you can get is 16. Avoid this as 16 is also produced for Control Card or run-time errors.

Adding a new column in SORT

My input data is like this:
trainnumber name station price coach seats
16001 CHN-CENTRAL PALANI 400.00 AC 02
16002 PALANI CHN-CENTRAL 410.00 ORD 76
16003 CHN-CENTRAL NAGARKOIL 425.00 AC 30
16004 NAGARKOIL CHN-CENTRAL 439.00 SLP 37
16005 THANJAVUR CHN-EGMORE 395.00 ORD 60
16006 CHN-EGMORE THANJAVUR 375.00 SLP 10
I want to add a new column before train number containing a four-digit sequence number followed by a blank and add 1 to my train number.
How to do this?
You have:
SORT FIELDS=COPY
OUTREC FIELDS=(1:SEQNUM,4,ZD,X,6:1,5,ZD,ADD,+1,EDIT=(TTTTT),
X,12:7,69)
Simplified:
OPTION COPY
INREC BUILD=(SEQNUM,4,ZD,
X,
1,5,ZD,
ADD,+1,
EDIT=(TTTTT),
X,
7,69)
OUTREC runs after a SORT/MERGE. INREC runs before a SORT/MERGE. Since you're not doing a SORT or MERGE (you're doing a COPY) it doesn't matter, but INREC is the more logical choice.
FIELDS is overloaded (consult the documentation to confirm) and since the presence of BUILD, FIELDS is not needed on INREC or OUTREC (and OUTREC is not needed on OUTFIL) because BUILD does the same job but with no possible confusion (BUILD is a synonym for FIELDS on INREC and OUTREC and OUTREC on OUTFIL - already complicated, without considering FIELDS on SUM, REFORMAT...).
Don't specify column positions (like 1:) if the positions are simply the natural arrangement. You are just building in maintenance.
The default start-point for a BUILD (or even the ugly FIELDS) is 1:. The default for the next field is immediately after the current field. You've used X for the spacing of your columns, so all data abuts the previous data. Using columns just complicates it.
Note: you have X,7,69. You could consider changing that to just 6,69, because position six is blank on your input.
Note: you are "losing" six bytes of your 80-byte record. If your input has, guaranteed, twelve trailing blanks (or other data that you do not require, ie any program using the file doesn't care about that loss) then that's OK, but we can't tell from your description.
Try to make your SORT Control Cards easier to read (try to make everything easier to read). It will save time and reduce errors. Which means cheaper. Time is money.
Assuming that you do mean with SORT, and your "column" isn't for DB2....
For fixed-length records:
OPTION COPY
INREC BUILD=(5X,1,your-lrecel)
The BUILD will cause a new current record, replacing the original, to be created. It will start with five blanks (the 5X) but you can put there whatever you like of whatever size (within the limits of the product, which are large). Change your-lrecl to the actual LRECL value.
For variable-length records:
OPTION COPY
INREC BUILD=(1,4,5X,5)
The 1,4 is the Record Descriptor Word, and it is always necessary to copy an RDW when creating a new current record. Once it is copied, SORT will ensure that the value contained in the first two bytes (the record-length) is correct. Then the new column, again five blanks in the example, then the rest of the variable-length record, which is specified simply by using a start-position (five here, to get the first byte of data) and implicitly this continues to the end of the record.
In your actual JCL (none of the above is JCL, it is SORT Control Cards), ensure that you do not specify any DCB info for SORTOUT. This means you can't use LIKE for that DD, remember that adding data makes the new LRECL different. Don't code the new LRECL in the JCL either. With it not specified, SORT will insert the correct value, and there is only one place to maintain it.
I tried like this and I did it.
SORT FIELDS=COPY
OUTREC FIELDS=(1:SEQNUM,4,ZD,X,6:1,5,ZD,ADD,+1,EDIT=(TTTTT),
X,12:7,69)
My file is 80 record length.

Compare two files and write it to "match" and "nomatch" files

I have two input files, each with length of 5200 bytes. A seven byte key is used to compare both files, if there is a match then it needs to be written to "match" file but while writing to match file I need a few fields from infile1 and all other fields from infile2.
If there is no match then write to no match file.
Is it possible to do it in sort? I know it can be easily done using COBOL program but just want to know in SORT/ICETOOL/Easytrieve Plus (EZTPA00).
Since 12,200 people have looked at this question and not got an answer:
DFSORT and SyncSort are the predominant Mainframe sorting products. Their control cards have many similarities, and some differences.
JOINKEYS FILE=F1,FIELDS=(key1startpos,7,A)
JOINKEYS FILE=F2,FIELDS=(key2startpos,7,A)
JOIN UNPAIRED,F1,F2
REFORMAT FIELDS=(F1:1,5200,F2:1,5200)
SORT FIELDS=COPY
A "JOINKEYS" is made of three Tasks. Sub-Task 1 is the first JOINKEYS. Sub-Task 2 is the second JOINKEYS. The Main Task follows and is where the joined data is processed. In the example above it is a simple COPY operation. The joined data will simply be written to SORTOUT.
The JOIN statement defines that as well as matched records, UNPAIRED F1 and F2 records are to be presented to the Main Task.
The REFORMAT statement defines the record which will be presented to the Main Task. A more efficient example, imagining that three fields are required from F2, is:
REFORMAT FIELDS=(F1:1,5200,F2:1,10,30,1,5100,100)
Each of the fields on F2 is defined with a start position and a length.
The record which is then processed by the Main task is 5311 bytes long, and the fields from F2 can be referenced by 5201,10,5211,1,5212,100 with the F1 record being 1,5200.
A better way achieve the same thing is to reduce the size of F2 with JNF2CNTL.
//JNF2CNTL DD *
INREC BUILD=(207,1,10,30,1,5100,100)
Some installations of SyncSort do not support JNF2CNTL, and even where supported (from Syncsort MFX for z/OS release 1.4.1.0 onwards), it is not documented by SyncSort. For users of 1.3.2 or 1.4.0 an update is available from SyncSort to provide JNFnCNTL support.
It should be noted that JOINKEYS by default SORTs the data, with option EQUALS. If the data for a JOINKEYS file is already in sequence, SORTED should be specified. For DFSORT NOSEQCHK can also be specified if sequence-checking is not required.
JOINKEYS FILE=F1,FIELDS=(key1startpos,7,A),SORTED,NOSEQCHK
Although the request is strange, as the source file won't be able to be determined, all unmatched records are to go to a separate output file.
With DFSORT, there is a matching-marker, specified with ? in the REFORMAT:
REFORMAT FIELDS=(F1:1,5200,F2:1,10,30,1,5100,100,?)
This increases the length of the REFORMAT record by one byte. The ? can be specified anywhere on the REFORMAT record, and need not be specified. The ? is resolved by DFSORT to: B, data sourced from Both files; 1, unmatched record from F1; 2, unmatched record from F2.
SyncSort does not have the match marker. The absence or presence of data on the REFORMAT record has to be determined by values. Pick a byte on both input records which cannot contain a particular value (for instance, within a number, decide on a non-numeric value). Then specify that value as the FILL character on the REFORMAT.
REFORMAT FIELDS=(F1:1,5200,F2:1,10,30,1,5100,100),FILL=C'$'
If position 1 on F1 cannot naturally have "$" and position 20 on F2 cannot either, then those two positions can be used to establish the result of the match. The entire record can be tested if necessary, but sucks up more CPU time.
The apparent requirement is for all unmatched records, from either F1 or F2, to be written to one file. This will require a REFORMAT statement which includes both records in their entirety:
DFSORT, output unmatched records:
REFORMAT FIELDS=(F1:1,5200,F2:1,5200,?)
OUTFIL FNAMES=NOMATCH,INCLUDE=(10401,1,SS,EQ,C'1,2'),
IFTHEN=(WHEN=(10401,1,CH,EQ,C'1'),
BUILD=(1,5200)),
IFTHEN=(WHEN=NONE,
BUILD=(5201,5200))
SyncSort, output unmatched records:
REFORMAT FIELDS=(F1:1,5200,F2:1,5200),FILL=C'$'
OUTFIL FNAMES=NOMATCH,INCLUDE=(1,1,CH,EQ,C'$',
OR,5220,1,CH,EQ,C'$'),
IFTHEN=(WHEN=(1,1,CH,EQ,C'$'),
BUILD=(1,5200)),
IFTHEN=(WHEN=NONE,
BUILD=(5201,5200))
The coding for SyncSort will also work with DFSORT.
To get the matched records written is easy.
OUTFIL FNAMES=MATCH,SAVE
SAVE ensures that all records not written by another OUTFIL will be written here.
There is some reformatting required, to mainly output data from F1, but to select some fields from F2. This will work for either DFSORT or SyncSort:
OUTFIL FNAMES=MATCH,SAVE,
BUILD=(1,50,10300,100,51,212,5201,10,263,8,5230,1,271,4929)
The whole thing, with arbitrary starts and lengths is:
DFSORT
JOINKEYS FILE=F1,FIELDS=(1,7,A)
JOINKEYS FILE=F2,FIELDS=(20,7,A)
JOIN UNPAIRED,F1,F2
REFORMAT FIELDS=(F1:1,5200,F2:1,5200,?)
SORT FIELDS=COPY
OUTFIL FNAMES=NOMATCH,INCLUDE=(10401,1,SS,EQ,C'1,2'),
IFTHEN=(WHEN=(10401,1,CH,EQ,C'1'),
BUILD=(1,5200)),
IFTHEN=(WHEN=NONE,
BUILD=(5201,5200))
OUTFIL FNAMES=MATCH,SAVE,
BUILD=(1,50,10300,100,51,212,5201,10,263,8,5230,1,271,4929)
SyncSort
JOINKEYS FILE=F1,FIELDS=(1,7,A)
JOINKEYS FILE=F2,FIELDS=(20,7,A)
JOIN UNPAIRED,F1,F2
REFORMAT FIELDS=(F1:1,5200,F2:1,5200),FILL=C'$'
SORT FIELDS=COPY
OUTFIL FNAMES=NOMATCH,INCLUDE=(1,1,CH,EQ,C'$',
OR,5220,1,CH,EQ,C'$'),
IFTHEN=(WHEN=(1,1,CH,EQ,C'$'),
BUILD=(1,5200)),
IFTHEN=(WHEN=NONE,
BUILD=(5201,5200))
OUTFIL FNAMES=MATCH,SAVE,
BUILD=(1,50,10300,100,51,212,5201,10,263,8,5230,1,271,4929)
I had used JCL about 2 years back so cannot write a code for you but here is the idea;
Have 2 steps
First step will have ICETOOl where you can write the matching records to matched file.
Second you can write a file for mismatched by using SORT/ICETOOl or by just file operations.
again i apologize for solution without code, but i am out of touch by 2 yrs+
Though its really long back this question was posted, I wish to answer as it might help others. This can be done easily by means of JOINKEYS in a SINGLE step. Here goes the pseudo code:
Code JOINKEYS PAIRED(implicit) and get both the records via reformatting filed. If there is NO match from either of files then append/prefix some special character say '$'
Compare via IFTHEN for '$', if exists then it doesnt have a paired record, it'll be written into unpaired file and rest to paired file.
Please do get back incase of any questions.
In Eztrieve it's really easy, below is an example how you could code it:
//STEP01 EXEC PGM=EZTPA00
//FILEA DD DSN=FILEA,DISP=SHR
//FILEB DD DSN=FILEB,DISP=SHR
//FILEC DD DSN=FILEC.DIF,
// DISP=(NEW,CATLG,DELETE),
// SPACE=(CYL,(100,50),RLSE),
// UNIT=PRMDA,
// DCB=(RECFM=FB,LRECL=5200,BLKSIZE=0)
//SYSOUT DD SYSOUT=*
//SRTMSG DD SYSOUT=*
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
FILE FILEA
FA-KEY 1 7 A
FA-REC1 8 10 A
FA-REC2 18 5 A
FILE FILEB
FB-KEY 1 7 A
FB-REC1 8 10 A
FB-REC2 18 5 A
FILE FILEC
FILE FILED
FD-KEY 1 7 A
FD-REC1 8 10 A
FD-REC2 18 5 A
JOB INPUT (FILEA KEY FA-KEY FILEB KEY FB-KEY)
IF MATCHED
FD-KEY = FB-KEY
FD-REC1 = FA-REC1
FD-REC2 = FB-REC2
PUT FILED
ELSE
IF FILEA
PUT FILEC FROM FILEA
ELSE
PUT FILEC FROM FILEB
END-IF
END-IF
/*
//STEP01 EXEC SORT90MB
//SORTJNF1 DD DSN=INPUTFILE1,
// DISP=SHR
//SORTJNF2 DD DSN=INPUTFILE2,
// DISP=SHR
//SORTOUT DD DSN=MISMATCH_OUTPUT_FILE,
// DISP=(,CATLG,DELETE),
// UNIT=TAPE,
// DCB=(RECFM=FB,BLKSIZE=0),
// DSORG=PS
//SYSOUT DD SYSOUT=*
//SYSIN DD *
JOINKEYS FILE=F1,FIELDS=(1,79,A)
JOINKEYS FILE=F2,FIELDS=(1,79,A)
JOIN UNPAIRED,F1,ONLY
SORT FIELDS=COPY
/*

Resources