| Using RPG in SQL |
|
|
|
| Programming - RPG | ||||||||||
| Written by Paul Tuohy | ||||||||||
| Tuesday, 18 September 2007 18:00 | ||||||||||
|
In this article, I will look at using a simple RPG subprocedure that converts a numeric field containing a date to an actual date field. The RPG SideFigure 1 shows the DDS for a simple table containing Employee ID, Name, Date Joined, and Date of Birth. The Date Joined and Date of Birth fields are defined as packed numeric data types and contain dates in ISO (YYYYMMDD) format. If there is a need to perform any sort of "date math" (e.g., calculating a person's current age or the age they were when they joined the company), there is no problem converting the dates to proper date fields in an RPG program.
Figure 1: This is the DDS of a simple table. To make the date conversion process even easier in RPG programs, I have written a ConvertToDate subprocedure, shown in Figure 2, which converts numeric fields to date fields. The ConvertToDate subprocedure accepts a single parameter (which should be a numeric field containing a date in ISO format) and returns either the ISO date equivalent of the passed parameter (if it contains a valid date) or today's date (if the passed parameter is not a valid date). The module containing the ConvertToDate subprocedure is in a service program named DATES.
Figure 2: The ConvertToDate subprocedure converts numeric fields to date fields. You can use the ConvertToDate subprocedure anywhere you need to treat a numeric date field (with a date in ISO format) as a proper date field. For example, using the database described in Figure 1, you would use the following code to calculate the age of an employee when he or she joined the company: Age = %Diff( ConvertToDate(Joined) : ConvertToDate(Birth) : *Y);
The use of ConvertToDate in programs ensures that a proper date is always used and takes care of the unlikely instance that an invalid date might exist in the database. Of course, you could add extra processing to log any errors, halt processing, or return a different default date instead of today's date. But how can you use the ConvertToDate subprocedure in SQL? Define an SQL Function in iSeries NavigatorJust as RPG has BIFs, SQL has scalar functions. And just as RPG allows you to define your own BIFs by writing subprocedures, SQL allows you to define your own scalar functions, referred to as user-defined functions (UDFs). If you have an urge to jump into the deep end, you can attempt to define an SQL function using a CREATE FUNCTION command in SQL, but, for the first attempt at least, it is a lot easier to use the wizard provided in iSeries Navigator. I will return to the CREATE FUNCTION command later in this article. To define a function using iSeries Navigator, you must add the schema (library) that will contain the function to the list of schemas. Expand Databases -> DatabaseName -> Schemas. If the required schema is not in the list, right-click on Schemas and select the option to Select Schemas to Display and add the schema to the list. To define a function, expand the required schema, right-click on Functions, and select New -> External from the pop-up menu, as shown in Figure 3. An external SQL function indicates that the SQL function is a "wrapper" to call an external program or subprocedure. Figure 3: An "external" SQL function calls an external program or subprocedure. (Click images to enlarge.)
The wizard to define an external SQL function consists of three tabs: General, Parameters, and External Program. Let's look at the contents of the three tabs in detail, starting with the General tab, shown in Figure 4, which identifies the function and the value (or values) it returns and provides details relating to the SQL environment for the function. Figure 4: The General tab defines a function.
The Parameters tab, shown in Figure 5, identifies the parameters that are passed to the function. Figure 5: The Parameters tab identifies the parameters passed to the function.
The External Program tab, shown in Figure 6, identifies the program or subprocedure that is called by the function. The function may be a program or a Java method. For the purposes of this article, I'll discuss only the program. Figure 6: The External Program tab identifies the program or subprocedure called by the function.
Now click on the OK button to create the ConvertToDate Function. Create FunctionEarlier in the article, I mentioned that you could define a function using the CREATE FUNCTION command in SQL. To get an idea of the format of the SQL CREATE FUNCTION command, right-click on an existing SQL function in iSeries Navigator and select Generate SQL from the pop-up menu. Figure 7 shows the SQL CREATE FUNCTION command that would be used to create the ConvertToDate function defined earlier.
Figure 7: Define a function with CREATE FUNCTION. When defining multiple functions that are somewhat similar, it is often easier to define the first function using iSeries Navigator and then define the remaining functions using the first function as a template; simply generate the SQL, change the relevnt definitions, and run the commands. Now let's look at a couple of examples of using the newly defined function. Using the Function with SELECTYou can use your own user-defined functions in almost any place you can use an SQL scalar function. For example, Figure 8 shows an SQL SELECT statement that uses the ConvertToDate function and the corresponding result of running the statement. The ConvertToDate function is used to represent the Birth and Joined columns as proper dates and used again in the calculation of the Age When Joined column.
Figure 8: Use the function in a SELECT statement. (Note to self: Stop using your date of birth in these examples). Using the Function in a ViewYou can make life even easier by defining a view that already does all the hard work. Figure 9 shows the definition of the view EMPLOYEEV1, which contains the definition of columns corresponding to those defined in the SELECT statement shown in Figure 8.
Figure 9: Use the function in the definition of a view. Now simply selecting all columns from the view EMPLOYEEV1 generates the same result set shown back in Figure 8. Do you think your users might find this useful when they are using QUERY? A Last Word...The ability to use existing RPG programs and subprocedures as functions within SQL can offer some intriguing possibilities in that they can provide a means of using existing application logic to provide column values. Yet another tool to add to your ever-expanding utility belt! | ||||||||||
|
| MC Press Web Site Staff |
Using RPG in SQL
Nov 26 2007 14:33:00 This is a discussion about Using RPG in SQL.
|
#143801 |
| frfancha |
Using RPG in SQL
Sep 19 2007 10:15:00 What about: date(substr(digits(input), 1, 4) || '-' || substr(digits(input), 5, 2) || '-' || substr(digits(input), 6, 7) ) ?
|
#143802 |
| P.Tuohy |
Using RPG in SQL
Sep 19 2007 10:53:00 Of course standard SQL will work :-) The point was to demonstrate how to use an existing RPG subprocedure in SQL and I thought the date conversion one was one everyone could relate to. But I do think that converttodate(birth) is a bit easier to read as opposed to date(substr(digits(birth), 1, 4) || '-' || substr(digits(birth), 5, 2) || '-' || substr(digits(birth), 6, 7) ). And then, of course, you could just as easily create an SQL function Paul
|
#143803 |
|
Using RPG in SQL
Sep 19 2007 16:57:00 yup, post the code! code-thief, and proud of it :-) HAGD
|
#143805 |
| scatterload |
Using RPG in SQL
Sep 19 2007 17:34:00 ye be not a code thief, matey! ye be a code PIRATE! courtesy of International Talk Like A Pirate Day! http://www.talklikeapirate.com/ -Sarrrrge
|
#143806 |
|
Using RPG in SQL
Sep 20 2007 09:09:00 I do bear a slight resemblance to that moniker... thx for the smile :-)
|
#143807 |
| AlexKr |
Using RPG in SQL
Sep 20 2007 20:14:00 I am trying to use a UDF to get the number of business days between two dates entered in "JD Edwards Julian" format (i.e., 107090 for April 1, 2007, etc.). It is supposed to invoke an RPGLE procedure in the background using a work calendar file to get the result (no SQL involved). It takes two numeric parms (6,0 both) and returns an integer (number of days). I created an export definition using binder language and added my own signature to it. Then I created a UDF via Navigator defining the two parms as decimal 6,0. I have my SQL stored as a Query Manager Query. When I run the query, it stops advising me that '
|
#143809 |
| AlexKr |
Using RPG in SQL
Sep 21 2007 20:04:00 Not sure what it is. We are running World 7.3. There is no readily available function to calculate the number of BUSINESS days in JD Edwards and moreover, my goal is to produce a program free solution so that a function would be available from a query. In fact, I was able to get somewhat further in my quest. After I changed the name of the procedure in parenthesis for external program definition to all caps, the query would start recognizing the service program/procedure (BTW, does anybody know why everything is greyed out in the 'wizard' window since the function has been defined, so I have no choice but scratch it all and re-start from scratch?) But now I am apparently experiencing misunderstanding between the SQL and the service program. I tried 'zoned' scalar function in SQL matched by 'numeric' data type in Parameters tab and zoned parms in procedural interface in RPG as well as 'decimal'/decimal/packed combination but either one would end up in decimal data error at the first RPG statement referring to the parms when I run the query. To add to the trouble, I could not get the srvpgm to stop at a breakpoint when invoking the strqmqry in debug mode. Neither could I produce a meaningful RPG dump being advised that 'Variable data is not available in a secondary thread'. Again, any thoughts? Mr. Tuohy?
|
#143811 |
| P.Tuohy |
Using RPG in SQL
Sep 24 2007 16:08:00 Hi Alex, Apologies for the delay in replying - 5 hour time difference and a weekend to myself :-) I am not familiar with the JDE Dates but I wrote a little procedure to test the concept - code is below along with the instruction to create the Function. At a guess it is the SIMPLE CALL on the create function you were missing. Also, to debug a function you have to do it from a different job - much as you debug a batch job i.e. strsrvjob, strdbg srvpgm(dates)... HTH Paul Tuohy BTW, I prefer Paul to Mr. Tuohy.
Code |
#143812 |
| AlexKr |
Using RPG in SQL
Sep 24 2007 20:19:00 No problem Paul. I am running out of time right now (the case of Monday :-)) but will try your suggestion later and definitely let you and the forum know of the result. Bill, unfortunately I am hardly able to read your code in this fashion. You may want to try and convert your source into PDF and post it as an attachment. I'll post mine as soon as I make it work as a UDF. (I know it does per se since I've tested it as a bound service program.)
|
#143814 |
| BillR |
Using RPG in SQL
Sep 28 2007 11:18:00 Okay. I like this idea. But when I tried to compile it (ILE) the "UDF_FIRST_CALL" is not defined(7030 ERROR). What have I missed?
|
#143815 |
|
Using RPG in SQL
Sep 30 2007 01:19:00 I looked at the SQLUDF /COPY and forgot a little modification I had to make to the sqludf...My bad.
Code |
#143816 |









