Build the RUNSQL SQL statement on the fly.
Last month, in "More on the RUNSQL CL Command," we saw how to select specific records from the SAMPLE database using the RUNSQL command. In that article, we hardcoded the selection criteria of Effective date (EffDate) being earlier than May 1, 2012. This month, we'll look at how to allow a user to interactively specify the Class, Status, and EffDate year values that the user is interested in. This will include the ability for the user to also ignore (that is, not select on) any one or more of these three fields/columns.
To prompt the user for the field values they are interested in, we'll create a display file named MyDSPF. The source for this display file is provided below. To create the display file, you can use the command CRTDSPF FILE(MYDSPF) SRCFILE(QDDSSRC).
A R RPT_PROMPT
A CA03(03 'Exit')
A 2 2DATE(*SYS *YY) EDTCDE(Y)
A 2 31'RUN MY SAMPLE REPORT'
A 2 70TIME
A 4 2'Class:'
A RQS_CLASS 5 B 4 20
A 5 2'Status:'
A RQS_STATUS 1Y 0B 5 20EDTCDE(Z)
A 6 2'Effective year:'
A RQS_YEAR 4Y 0B 6 20EDTCDE(Z)
A 23 2'F3=Exit'
The display file MyDSPF contains one record format, RPT_PROMPT, which prompts the user for the class, status, and effective year values of interest (variables Rqs_Class, Rqs_Status, and Rqs_Year, respectively). Command key 3 is enabled and can be used to exit the program when all of the desired reports have been spooled.
The program generating the report(s) is based on the RPTSAMPLE program introduced last month. As the actual creation of the report has not changed—only the records to be included or excluded in the report—we will not be changing the subroutine ReadFile. If you need a refresher on the processing of the ReadFile subroutine, refer to last month's column.
The new version of the RPTSAMPLE program is shown below, with the changes in bold.
Pgm
DclF File(MyDSPF)
DclF File(Sample) OpnID(MyResults)
Dcl Var(&EOF) Type(*Lgl)
Dcl Var(&RptLine) Type(*Char) Len(16)
Dcl Var(&Class) Type(*Char) Len(5) +
Stg(*Defined) DefVar(&RptLine 1)
Dcl Var(&Status) Type(*Char) Len(1) +
Stg(*Defined) DefVar(&RptLine 6)
Dcl Var(&EffDate) Type(*Char) Len(10) +
Stg(*Defined) DefVar(&RptLine 7)
Dcl Var(&NoDtaToWrt) Type(*Char) Len(1)
Dcl Var(&OvrFlwLin) Type(*Dec) Len(3 0)
Dcl Var(&CurPrtLin) Type(*Dec) Len(3 0)
Dcl Var(&SQLText) Type(*Char) Len(1024)
Dcl Var(&Where) Type(*Lgl)
Dcl Var(&Status_Chr) Type(*Char) Len(1)
Dcl Var(&Year_Chr) Type(*Char) Len(4)
DoWhile Cond(*not &IN03)
SndRcvF RcdFmt(Rpt_Prompt)
If Cond(&IN03) Then(Leave)
ChkObj Obj(QTemp/MyResults) ObjType(*File)
MonMsg MsgID(CPF9801) Exec( +
CrtDupObj Obj(Sample) FromLib(*Libl) +
ObjType(*File) ToLib(QTemp) +
NewObj(MyResults))
ChgVar Var(&SQLText) Value( +
'Insert into QTemp/MyResults +
(Select * +
from Sample')
ChgVar Var(&Where) Value('0')
If Cond(&Rqs_Class *NE ' ') Then(Do)
CallSubr Subr(ChkWhere)
ChgVar Var(&SQLText) +
Value(&SQLText *BCat +
'Class like(''' *TCat +
&Rqs_Class *TCat +
'%'')')
EndDo
If Cond(&Rqs_Status *NE 0) Then(Do)
CallSubr Subr(ChkWhere)
ChgVar Var(&Status_Chr) Value(&Rqs_Status)
ChgVar Var(&SQLText) +
Value(&SQLText *BCat +
'Status = ' *BCat +
&Status_Chr)
EndDo
If Cond(&Rqs_Year *NE 0) Then(Do)
CallSubr Subr(ChkWhere)
ChgVar Var(&Year_Chr) Value(&Rqs_Year)
ChgVar Var(&SQLText) +
Value(&SQLText *BCat +
'Year(EffDate) = ' *BCat +
&Year_Chr)
EndDo
ChgVar Var(&SQLText) +
Value(&SQLText *BCat +
'order by Class, EffDate)')
RunSQL SQL(&SQLText) Commit(*None)
CallSubr Subr(ReadFile)
ClrPFM File(QTemp/MyResults)
EndDo
Return
Subr Subr(ChkWhere)
If Cond(*not &Where) Then(Do)
ChgVar Var(&SQLText) +
Value(&SQLText *BCat 'Where')
ChgVar Var(&Where) Value('1')
EndDo
Else Cmd(ChgVar Var(&SQLText) +
Value(&SQLText *BCat 'and'))
EndSubr
Subr Subr(ReadFile)
OvrDBF File(Sample) ToFile(QTemp/MyResults)
ChgVar Var(&EOF) Value('0')
OpnFCLF FileID(MyReport) Usage(*Output) +
LvlChk(*No)
WrtRcdCLF FileID(MyReport) RcdFmt(Heading) +
RcdBuf(&NoDtaToWrt)
DoUntil Cond(&EOF = '1')
RcvF OpnID(MyResults)
MonMsg MsgID(CPF0864) Exec( +
ChgVar Var(&EOF) Value('1'))
If Cond(&EOF *EQ '0') Then(Do)
ChgVar Var(&Class) +
Value(&MyResults_Class)
ChgVar Var(&Status) +
Value(&MyResults_Status)
ChgVar Var(&EffDate) +
Value(&MyResults_EffDate)
WrtRcdCLF FileID(MyReport) +
RcdFmt(Detail) +
RcdBuf(&RptLine)
RtvFInfCLF FileID(MyReport) +
CurPrtLine(&CurPrtLin) +
PrtFOvrFlw(&OvrFlwLin)
If Cond(&CurPrtLin *GE &OvrFlwLin) +
Then(WrtRcdCLF FileID(MyReport) +
RcdFmt(Heading) +
RcdBuf(&NoDtaToWrt))
EndDo
Else Cmd( +
WrtRcdCLF FileID(MyReport) +
RcdFmt(End_Of_Rpt) +
RcdBuf(&NoDtaToWrt))
EndDo
CloFCLF FileID(MyReport)
Close OpnID(MyResults)
DltOvr File(Sample)
EndSubr
EndPgm
Assuming that the preceding CL source is stored in member RPTSAMPLE of source file QCLSRC, you can create the program using the command CRTBNDCL PGM(RPTSAMPLE). To run the program, use the command CALL PGM(RPTSAMPLE). Now let's look at what the RPTSAMPLE program is doing.
The first change to the program is related to declaring, and then using, the display file MyDSPF, which was created at the start of this column. RPTSAMPLE declares MyDSPF using the DCLF command and, in terms of processing, enters into a DOWHILE loop, which is conditioned by indicator 03 being off. Indicator 03 is "on" when the user presses command key 3. Within the DOWHILE loop, RPTSAMPLE writes and then reads record format RPT_PROMPT. If command key 3 is pressed, the program leaves the DOWHILE; otherwise, a report is generated.
Report Creation
When a report is being created, the following processing is done:
Using the command CHKOBJ, a check is made to determine whether the file MyResults in library QTemp already exists. If it doesn't exist, which will be the case for the first report created within the current job, the CHKOBJ command will send message CPF9801 – Object in library not found. Monitoring for this message, RPTSAMPLE will create MyResults in QTemp using the command CRTDUPOBJ. This is the same CRTDUPOBJ command that was used in last month's column. In running subsequent reports, within the same job, MyResults will exist in QTemp and RPTSAMPLE will re-use the current file.
Variable &SQLText is set to the value 'Insert into QTemp/MyResults (Select * from Sample'. This text, when later passed to the RUNSQL command, causes all fields of the Sample database to be written to MyResults in QTemp. This variable was previously declared as Type(*Char) with a (decidedly arbitrary) length of 1024 bytes.
Variable &Where is set to the value '0' where a value of '0' indicates that no selection criteria have (yet) been processed by RPTSAMPLE. This variable was previously declared as Type(*Lgl), where a value of '0' represents "off" and the value '1' "on."
Variable &Rqs_Class is examined to determine whether only a specific Class value is to be included in the report. If &Rqs_Class is blank, all classes are to be included. If &Rqs_Class is non-blank, a call to subroutine ChkWhere is made.
Subroutine ChkWhere has one purpose in life: to determine whether any previous selections have been processed for the current report. This is done by checking the value of logical variable &Where. If &Where is "off," indicating that the caller of the subroutine represents the first selection criteria encounter for the current report, then ChkWhere concatenates the string 'Where' (with one leading blank due to the use of the *BCat operator) to the current value of &SQLText and sets the variable &Where to "on."
If variable &Where is "on" when entering ChkWhere, indicating that previous selection criteria have been encountered for the current report, then ChkWhere concatenates the string 'and' (with again one leading blank using *BCat) to the current value of &SQLText
Returning from the ChkWhere subroutine,
- 1.the string 'Class like( ''' is concatenated (with one leading blank) to &SQLText,
- 2.the value of the &Rqs_Class variable is concatenated (with no leading blank using *TCat) to &SQLText,
- 3.and the value '%)''' is concatenated (with no leading blank using *TCat) to &SQLText.
Assuming that the user-specified value of variable &Rqs_Class is 'A', then the value of &SQLText, after the previous processing has completed, would be 'Insert into QTemp/MyResults (Select * from Sample Where Class like('A%')'.
This SQL Select statement would cause all records in the SAMPLE database that have a Class value starting with the capital letter 'A' to be included in the report. Using the SAMPLE database from last month, and making no additional selection criteria, would then result in the following report:
CLASS STATUS EFF. DATE
A 1 2012-05-01
ABC 2 2012-04-15
If you preferred that the report only include exact matches (that is, only class 'A' and not class 'ABC' in the previous report), you can change the concatenation of string 'Class like (''' to 'Class = ''' and the concatenation of string '%)''' to ''''.
Having completed the processing of &Rqs_Class, variable &Rqs_Status is examined to determine whether only a specific Status value is to be included in the report. As with the previous handling of a non-blank &Rqs_Class value, the processing of a non-0 &Rqs_Status value is done by calling the subroutine ChkWhere and then concatenating the desired value to &SQLText. The only new consideration here is the need to convert the numeric value of &Rqs_Status to character form (using variable &Status_Chr) due to CL's requirement that concatenation be done with like data types.
After processing &Rqs_Status, variable &Rqs_Year is then processed in the same manner as &Rqs_Status, determining whether a non-0 value was specified and, if so, calling subroutine ChkWhere, converting &Rqs_Year to character format using &Year_Chr, and then concatenating the appropriate string (in this case, the string is using SQL's built-in function YEAR) to &SQLText so that the year component of &EffDate is compared for equality to the user-requested year.
Having finished checking for all request variable values that might impact the records of SAMPLE that are to be included in the report, the variable &SQLText is now completed by concatenating the string 'order by Class, EffDate)'.
Using the command RUNSQL, the SQL statement found in variable &SQLText is run.
The report is generated by calling the subroutine ReadFile.
The current contents of file QTemp/MyResults are cleared in anticipation of the user wanting to run another report with new selection criteria.
The command ENDDO for the controlling DOWHILE is run, causing the user to be prompted for the next report's selection criteria.
Just One Example
This month's RPTSAMPLE program provides one example of what can be accomplished when you combine CL and SQL. The program, though simple, provides a framework that can be very easily maintained in terms of adding or removing user selection criteria; just add or remove request variable checks such as was done with &Rqs_Class, &Rqs_Status, and &Rqs_Year.
Next month, we'll look at some additional changes that can be made to the RPTSAMPLE program.
More CL Questions?
Wondering how to accomplish a function in CL? Send your CL-related questions to me at
LATEST COMMENTS
MC Press Online