How to Get Matching records from both F1 & F2 in output file1 and Non-Matching Records from F2 in output File 2 using Sort - mainframe

Hi I am trying to Find Matching and Un Matching records from 2 file, one file is a Error file with 181 LRECL, second file F2 is valid records file with same 181 LRECL.
Example ERROR File
12345678901
11111111111
11111111111
22222222222
VALID File has
33333333333
11111111111
11111111111
44444444444
I implemented the Left Outer join
//F1 -> ERROR FILE
//F2 -> VALID FILE
//SYSOUT DD SYSOUT=*
//SYSIN DD *
JOINKEYS F1=MAIN,FIELDS=(43,11,A)
JOINKEYS F2=LOOKUP,FIELDS=(10,11,A)
JOIN UNPAIRED,F2 ONLY
SORT FIEDLS=COPY
/*
so using this I was able to get the valid records that were not present in Error file in sysout
SYSOUT:
33333333333
44444444444
but I don't want lose the Match Records from both File and want to Write the Matching Records from Both file in Second Output file.
I tried Implementing
the FULL OUTER Join, but was unable to get the result
//F1 -> Error File
//F2 -> Valid File
//MATCH DD DSN=MYDATA.URMI.SAMPLE.MATCH,DISP=OLD
//NOMATCH1 DD DSN=MYDATA.URMI.SAMPLE.NOMATCH1,DISP=OLD
//NOMATCH2 DD DSN=MYDATA.URMI.SAMPLE.NOMATCH2,DISP=OLD
//SYSOUT DD SYSOUT=*
//SYSIN DD *
JOINKEYS F1=MAIN,FIELDS=(43,11,A)
JOINKEYS F2=LOOKUP,FIELDS=(10,11,A)
JOIN UNPAIRED,F1,F2
REFORMAT FIELDS=(?,F1:1,181,F2:1,181)
OPTION COPY
OUTFIL FNAMES=MATCH,INCLUDE=(1,1,CH,EQ,C'B'),BUILD=(1:2,181)
OUTFIL FNAMES=NOMATCH1,INCLUDE=(1,1,CH,EQ,C'1'),BUILD=(1:2,181)
OUTFIL FNAMES=NOMATCH2,INCLUDE=(1,1,CH,EQ,C'2'),BUILD=(1:2,181)
/*

According to the above snapshot, you were reformatting 181 bytes of Error file first followed by 181 bytes of VALID file. So, while writing the NOMATCH2 file, you should build as BUILD=(1:183,181) instead of BUILD=(1:2,181). Hope this will solve your problem and please update here if you still not able to overcome the issue.
Regards,
Anbu.

For completeness, I am posting the updated script and the resulting output files:
JOINKEYS F1=MAIN,FIELDS=(43,11,A)
JOINKEYS F2=LOOKUP,FIELDS=(10,11,A)
JOIN UNPAIRED,F1,F2
REFORMAT FIELDS=(?,F1:1,181,F2:1,181)
OPTION COPY
OUTFIL FNAMES=MATCH,INCLUDE=(1,1,CH,EQ,C'B'),BUILD=(1:2,181)
OUTFIL FNAMES=NOMATCH1,INCLUDE=(1,1,CH,EQ,C'1'),BUILD=(1:2,181)
OUTFIL FNAMES=NOMATCH2,INCLUDE=(1,1,CH,EQ,C'2'),BUILD=(1:183,181)
The output file MATCH should contain:
11111111111
11111111111
11111111111
11111111111
The output file NOMATCH1 should contain:
12345678901
22222222222
The output file NOMATCH2 should contain:
33333333333
44444444444
This was verified using AHLSORT for Windows v14r3-117-ge2d0a249 but the results should be the same with DFSORT for z/OS.

Related

Extracting data from an input file using SORT to different output files not providing expected data

I am using SORT to process an input file and extract records that match three different criteria. The criteria is laid out in the control statements. I am looking for fields that match the text GROUND OPERATIONS, TECHNICAL OPERATI and AIRPORT TRANSFERS. I want records from the input that match each of the conditions to be written to a corresponding output file. The DDs for the output file are SORTOF01, SORTOF02 and SORTOF03 respectively.
I can see there are records that match my criteria in the input file but when executed the SORT completes but no records are selected. I’m missing something but I don’t know what it is. The JCL and control statements for the SORT are supplied below.
//PROB3 EXEC PGM=SORT
//SORTIN DD DSN=XXX.T.KR0Z1N99.RU02.FTPGEN.THOTLNON,DISP=SHR
//SORTOF01 DD DSN=XXX.T.KR0Z1N99.RU02.FTPGEN.THOTLN01,
// DISP=(NEW,CATLG,DELETE),
// LIKE=XXX.T.KR0Z1N99.RU02.FTPGEN.THOTLNON
//SORTOF02 DD DSN=XXX.T.KR0Z1N99.RU02.FTPGEN.THOTLN02,
// DISP=(NEW,CATLG,DELETE),
// LIKE=XXX.T.KR0Z1N99.RU02.FTPGEN.THOTLNON
//SORTOF03 DD DSN=XXX.T.KR0Z1N99.RU02.FTPGEN.THOTLN03,
// DISP=(NEW,CATLG,DELETE),
// LIKE=XXX.T.KR0Z1N99.RU02.FTPGEN.THOTLNON
//SYSPRINT DD SYSOUT=*
//SYSOUT DD SYSOUT=*
//SYSIN DD *
SORT FIELDS=COPY
INCLUDE COND=(93,3,CH,EQ,C'YES')
OUTFIL FILES=01,INCLUDE=(73,20,CH,EQ,C'GROUND OPERATIONS')
OUTFIL FILES=02,INCLUDE=(73,20,CH,EQ,C'TECHNICAL OPERATI')
OUTFIL FILES=03,INCLUDE=(73,20,CH,EQ,C'AIRPORT TRANSFERS')
Try changing the length from 20 to 17 as below
//SYSIN DD *
SORT FIELDS=COPY
INCLUDE COND=(93,3,CH,EQ,C'YES')
OUTFIL FILES=01,INCLUDE=(73,17,CH,EQ,C'GROUND OPERATIONS')
OUTFIL FILES=02,INCLUDE=(73,17,CH,EQ,C'TECHNICAL OPERATI')
OUTFIL FILES=03,INCLUDE=(73,17,CH,EQ,C'AIRPORT TRANSFERS')
Comparing 20 bytes with 17 bytes is probably going to be false
When having problems with sort, Always check the Record-Format, if the RECFM is VB you need to add 4 to all positions

SELECT operator

//ICETSIM1 EXEC PGM=ICETOOL
//TOOLMSG DD SYSOUT=*
//DFSMSG DD SYSOUT=*
//SYMNOUT DD SYSOUT=*
//NAMESIN DD *
LINK_REC;1,45
LINK_REFDATE;=,8,CH
LINK;*,16,CH
LINK_COLL;*,16,CH
LINK_TYPE;*,3,CH
LINK_LABEL;*,02,CH
LINK_P_LABEL;=,1,CH
LINK_S_LABEL;*,1,CH
//NAMESOUT DD DSN=&NAMES,DISP=(,PASS,DELETE),SPACE=(TRK,1)
//TOOLIN DD *
COPY FROM(NAMESIN) TO(NAMESOUT)
//S01 EXEC PGM=ICETOOL
//TOOLMSG DD SYSOUT=*
//DFSMSG DD SYSOUT=*
//SYMNAMES DD DSN=&NAMES,DISP=(OLD,PASS)
//SYMNOUT DD SYSOUT=*
//IN2 DD DISP=SHR,DSN=LINKS.001
SELECT FROM(IN2) TO(OU2) ON(LINK) FIRST USING(CTL2)
//CTL2CNTL DD *
OUTFIL FNAMES=OU2,
OUTREC=(LINK_REFDATE,16X,LINK,500X,LINK_TYPE,C'22')
This ICETOOL selects the first record for each LINK value in LINKS.001. The question is: does the SELECT operator expects the input to be sorted? If yes, it has to be sorted on all the fields of LINKS.001 (REF_DATE, LINK, LINK_COLL...)?
I'm a big fan of SORT symbols/SYMNAMES, so nice to see you using that, and in a fairly advanced way. You only specify one start position for your record, and relate everything else to the previous field, which is the most flexible way to do it.
I didn't realise a semi-colon could be used, so I changed it to a comma. You can't have a blank line (you can have a comment, * in column one, and leave the rest of the line blank.
I don't know why you have the first step, so I've dropped it out until you explain. The best way to use the SYMNAMES DD is with a DSN= and then a PDS/PDSE with a member-name, then have your cards inside the member. For explanation, I use DD *.
In your USING file, you have SORT control cards, which is correct, but they must have a blank in column one.
OUTREC on OUTFIL is dated, available for backwards-compatibility, so I've changed it to BUILD (which is a synonym for OUTREC on OUTFIL, and FIELDS on INREC and OUTREC - see how much less confusing it is to stick to just BUILD?).
By default, ICETOOL's SELECT operator sorts the data on each ON field you specify, in the order specified. If your data is already in the correct sequence, you use a USING (which you already have anyway) and specify SORT FIELDS=COPY or OPTION COPY there. Your data will then nor be sorted again.
//S01 EXEC PGM=ICETOOL
//TOOLMSG DD SYSOUT=*
//DFSMSG DD SYSOUT=*
//SYMNAMES DD *
LINK_REC,1,45
LINK_REFDATE,=,8,CH
LINK,*,16,CH
LINK_COLL,*,16,CH
LINK_TYPE,*,3,CH
LINK_LABEL,*,02,CH
LINK_P_LABEL,=,1,CH
LINK_S_LABEL,*,1,CH
//SYMNOUT DD SYSOUT=*
//IN2 DD *
1111111111111111111
1111111111111111111
2222222222222222222
//OU2 DD SYSOUT=*
//TOOLIN DD *
SELECT FROM(IN2) TO(OU2) ON(LINK) FIRST USING(CTL2)
//CTL2CNTL DD *
SORT FIELDS=COPY
OUTFIL FNAMES=OU2,
BUILD=(LINK_REFDATE,
16X,
LINK,
500X,
LINK_TYPE,
C'22')
The above, with my simple test-data, produces what you want. There were a couple of other typos (no OU2 DD, for instance) that I fixed to get it running.

How to get the Matching and Non-matching records from two input files in one step using SYNCSORT?

I have a requirement like below.
-> I have 2 input files FILE1 and FILE2.
-> Write the matching records into a FILE3.
-> Write the Non matching records from FILE1 into FILE4.
-> Write the Non matching records from FILE2 into FILE5.
The key position in both the Input Files is (1,10).
Can anybody please let me know the SORTCARD, how Can I get this in single step in SyncSort??
Thanks in Advance,
Rajasekhar Jannu.
JOINKEYS FILE=F1,FIELDS=(01,10,A)
JOINKEYS FILE=F2,FIELDS=(01,10,A)
UNPAIRED F1,F2 <== This results in cartesian product
REFORM FIELDS=(F1:01,10,F2:01,10),FILL=C'$'
SORT FIELDS=(01,10,CH,A)
OUTFIL FNAMES=03,
INCLUDE=(01,01,CH,NE,C'$',AND,11,01,CH,NE,C'$') <== Matched Records
OUTFIL FNAMES=04,
INCLUDE=(01,01,CH,NE,C'$',AND,11,01,CH,EQ,C'$') <== Non-Matched Records from File1
OUTFIL FNAMES=03,
INCLUDE=(01,01,CH,EQ,C'$',AND,11,01,CH,NE,C'$') <== Non-Matched Records from File2
Also, note that only 10bytes have been considered from both the files as you didnt mention the lengths of each file.
Also suggest you to search known mainframe forums and post it here if you dont get the working solution. Hope this helps
Refer this:
Joinkeys Guide
enter code here
//JOIN EXEC PGM=SORT
//SORTJNF1 DD *
1234567890 FILEB
1234678901 FILE1
/*
//SORTJNF2 DD *
1234567890 FILEB
1234789012 FILE2
/*
//JNF1CNTL DD DUMMY <=== not necessary(informatory)
//JNF2CNTL DD DUMMY <=== not necessary(informatory)
//BOTH DD DSN=<FILENAME>
//FILE1 DD DSN=<file name>
//FILE2 DD DSN=<file name>
//SYSOUT DD SYSOUT=*
//SYSPRINT DD SYSOUT=*
//SORTOUT DD SYSOUT=* <=== we can see the JOIN output here(explanatory)
//SYSIN DD *
JOINKEYS FILE=F1,FIELDS=(1,10,A)
JOINKEYS FILE=F2,FIELDS=(1,10,A)
JOIN UNPAIRED,F1,F2
REFORMAT FIELDS=(F1:1,16,F2:1,16),FILL=C'#'
OPTION COPY
OUTFIL FNAMES=BOTH,
INCLUDE=(01,01,CH,NE,C'#',AND,17,01,CH,NE,C'#'),BUILD=(1,16)
OUTFIL FNAMES=FILE1,
INCLUDE=(01,01,CH,NE,C'#',AND,17,01,CH,EQ,C'#'),BUILD=(1,16)
OUTFIL FNAMES=FILE2,
INCLUDE=(01,01,CH,EQ,C'#',AND,17,01,CH,NE,C'#'),BUILD=(17,16)
/*
//***** this REFORMAT output would be something like
1234567890 FILEB1234567890 FILEB
1234678901 FILE1################
################1234789012 FILE2
//*********************************
FILL=C'#' pads the charecter '#' to the record produced by JOIN operation, so that this padded charecter can be used later at the time of Filter using INCLUDE

How to use SORT to move blank lines to the end of the file?

I have 9787 records of which the first 17 lines are blank. I want to move those 17 lines to the end of the file.
How can I do that?
The below will sort the input dataset with the blank lines at the end of the SORTOUT DD
//SORT EXEC PGM=SORT
//SYSOUT DD SYSOUT=*
//SORTWK01 DD SPACE=(CYL,(10,5),RLSE)
//SORTWK02 DD SPACE=(CYL,(10,5),RLSE)
//SORTWK03 DD SPACE=(CYL,(10,5),RLSE)
//SORTIN DD DSN=INPUT.DATASET,DISP=SHR
//SORTOUT DD SYSOUT=*
//SYSIN DD *
SORT FIELDS=(1,80,CH,D)
//*
FIELDS=(1,80,CH,D) means it is sorting in descending order from Position 1 for 80 characters using character data.
If you dataset is wider than 80 characters you might need to put the actual width here or the blank lines might not be put at the end.
There is no need to Sort the data. Lucky that the data lines were 1) in order as per entire record, 2) in descending order :-).
This temporarily extends each record by adding a sequence number at the "end" of each record (five digits should allow for expansion). With OUTFIL OMIT, blank lines that are from the first 17 records of the data are dropped. With TRAILER1 and the "slash operator" "/", 17 blank lines are added to the "end" of the file. The REMOVECC is due to the need to not have the printer control-character that TRAILER1 (a reporting function) would otherwise add. The BUILD on OUTFIL is to return the record to its original size, dropping the 5-digit sequence number.
OPTION COPY
INREC IFTHEN=(WHEN=INIT,OVERLAY=(81:SEQNUM,5,ZD))
OUTFIL OMIT=((1,80,CH,EQ,C' ',
AND,81,5,ZD,LE,17)),
BUILD=(1,80),
REMOVECC,
TRAILER1=(/,/,/,/,/,/,/,/,/,/,/,/,/,/,/,/)

Mainframe Dataset

I have a sequential dataset which has some data formatted in columns.
suppose below is the format of my dataset.
Emp_ID Emp_name Emp_addr
---------------------------------
I want to remove the column Emp_Name from the dataset. Can I do it without writing the COBOL program? Please let me know if we have any command to do the same.
Thanks and Regards,
Manasi Kulkarni.
You can use SORT to eliminate bytes from your sequential file.
Let's assume the following format:
Employee ID Bytes 1 - 10
Employee Name Bytes 11 - 40
Employee Address Bytes 41 - 70
We want to eliminate the Employee Name. We want to copy the first 10 bytes, skip the next 30 bytes, and copy the last 30 bytes.
The input sequential file is 70 bytes and the output sequential file will be 40 bytes.
Here's the SORT JCL to perform this task. You'll need to modify the JCL to conform to the standards of your mainframe shop.
//EXAMP JOB A400,PROGRAMMER
//COPY EXEC PGM=SORT
//SYSOUT DD SYSOUT=*
//SORTIN DD DSN=SMF.DATA,DISP=SHR
//SORTOUT DD DSN=SMF.COPY,DISP=(,KEEP),SPACE=(CYL,(2,5))
// UNIT=SYSDA
//SYSIN DD *
OPTION COPY
OUTREC FIELDS=(1,10,41,30)
/*
//
The OUTREC statement says to copy starting from byte 1 for 10 bytes, and from byte 41 for 30 bytes, for a total of 40 bytes.
Here's IBM's DFSORT manual for more information.
You could also use IEBGENER to reformat the data as follows:
//FORMAT EXEC PGM=IEBGENER
//SYSPRINT DD SYSOUT=*
//SYSUT1 DD *
EMP_ID EMP_NAME EMP_ADDR
---------------------------------
120 FIRST NEW ROAD
130 SECOND OLD ROAD
/*
//SYSUT2 DD SYSOUT=*
//SYSIN DD *
GENERATE MAXFLDS=99,MAXLITS=99
RECORD FIELD=(10,1,,1),
FIELD=(30,24,,11)
/*
//
Further details to IEBGENER command syntax can be found here.
Also, if you want more control you can always write a quick REXX script with two EXEXCIO command to read the file and write only the portions you want in the second command.
For a one-off change to a smaller dataset - this probably wouldn't be appropriate here - the ISPF editor can do this. Simply use the BNDS line command to set the left bound and then the 'left shift '(' line command to shift the data left by the width of that column to effectively eliminate it.

Resources