embedded sql kevin forsythe dmc consulting [email protected]
TRANSCRIPT
Why Embed SQL in your RPG?
Compile Timevs
Run Time
Work with Members Using PDM DMC41 File . . . . . . QRPGLESRC Library . . . . DMCSQLLIB Position to . . . . . Type options, press Enter. 2=Edit 3=Copy 4=Delete 5=Display 6=Print 7=Rename 8=Display description 9=Save 13=Change text 14=Compile 15=Create module... Opt Member Type Text SQLPROC SQLRPGLE Test program containing SQL statements SQLTEST SQLRPGLE Test program containing SQL statements STOREDPRC1 SQLRPGLE Test program containing SQL statements STOREDPRC3 SQLRPGLE Test program containing SQL statements STOREDPRC4 SQLRPGLE Test program containing SQL statements STOREDPRC5 SQLRPGLE Test program containing SQL statements TSTRPGPRC SQLRPGLE Test program containing SQL statements TSTRPGPRC2 SQLRPGLE Test program containing SQL statements More... Parameters or command ===> F3=Exit F4=Prompt F5=Refresh F6=Create
F9=Retrieve F10=Command entry F23=More options F24=More keys
Before you can embed SQL into your RPGIV code you have to specify the source type as SQLRPGLE.
New Source Type
Syntax
C/EXEC SQL
C+ your-sql-code-here
C/END-EXEC
C/EXEC SQL INCLUDE member-name
C/END-EXEC
?
Syntax
Avoid creating fields that start with:'SQ''SQL''RDI''DSN’
These may interfere with variable names used by the pre-compiler.
Host Variables
:field-nameRPG IV variables you can’t use as a HOST variable:
Pointers Tables UDATE UDAY UMONTH UYEAR Look-ahead fields Named constants Definitions requiring the resolution of %SIZE or %ELEM
SQL Pre-Compiler
CRTSQLxxx
The pre-compiler will do the following:
Looks for SQL statements and for the definition of host variables. Validates variable names and definitions used in the SQL statements. Any errors found will be included in a compile listing.
Verifies that each SQL statement is free of syntax errors. Validates the syntax of each SQL statement. See the compile listing for errors.
Validates the SQL statements using the description in the database. Validates table, view, and column names. If a specified table or view does not exist, or you are not authorized to the table or view, the validation will be done at run time. If the table or view does not exist at run time, an error occurs.
SQL Pre-Compiler
SQLAIDSQLABCSQLCOD - SQLCODESQLERLSQLERMSQLERPSQLERRSQLSTT - SQLSTATESQLWRN - SQLWARN
The SQL pre-compiler adds a number of fields to your RPG IV programs. These fields are used primarily for error checking. These are covered in more detail later.
When compiling an RPG program that includes embedded SQL statements, some new options are provided.
Commitment Control:
*CHG - Objects and updated records are locked until they are committed. Uncommitted changes from other programs can be seen.
*CS - Objects and updated records are locked until they are committed. Uncommitted changes from other programs can not be seen, and selected records are locked until the next record is selected.
*ALL - As *CS, but records selected are locked until a COMMIT is performed.
*RR - As *ALL, but tables referenced by SELECT, UPDATE, INSERT, and DELETE are locked exclusively.
*NONE- Commitment control is not used.
CRTSQLRPGI
Relational Database:
*LOCAL - The SQL package information is embedded in the program.
named - The SQL package will be created in the named relational database.
Listing Output:
*PRINT - The pre-compile listing will be printed.
*NONE - No pre-compile listing is produced.
CRTSQLRPGI
INCLUDE file:
*SRCFILE - Source members referenced by SQL INCLUDES will be found in the same source file as the program source.
named - Specify the file and library that source members referenced by SQL INCLUDES will be found in.
CRTSQLRPGI
Allow copy of data:
*YES - A copy of selected data is only used when necessary.
*OPTIMIZE- The system will determine whether to use a copy of the selected data, or read it directly from the table.
*NO - A copy of the selected data is not used. If one is required, an error will occur.
CRTSQLRPGI
Close SQL Cursor:
*ENDACTGRP - SQL cursors not explicitly closed in the program will be implicitly closed when the activation group the program is running in ends.
*ENDMOD - SQL cursors not explicitly closed in the program will be implicitly closed when the module is exited.
CRTSQLRPGI
Allow Blocking:
*READ - Read only data retrievals will be blocked. This improves performance for programs reading large numbers of records.
*NONE - No blocking is allowed.
*ALLREAD - As read but if the commitment level is *CHG, then blocked reads may also be performed against updateable tables. However some SQL commands will not function correctly. See the help text for more information.
CRTSQLRPGI
Delay Prepare: *YES - Prepared SQL statements are validated when they are used in an OPEN, EXECUTE, or DESCRIBE statement.*NO - Prepared SQL statements are validated by the PREPARE statement.
RDB Connect Method: *DUW - Your program can be connected to multiple databases simultaneously. *RUW - The CONNECT statement will cause previous connections to be disconnected.
CRTSQLRPGI
Package:
*OBJ - The SQL package will be created with the same name as the program.
named - Specify the SQL package name.
IBM SQL FLAGGING:
*FLAG - SQL statements will be checked to see if they conform to IBM’s SQL syntax.
*NOFLAG - No checking will be done.
CRTSQLRPGI
ANS FLAGGING:
*ANS - Statements are checked to see if they conform to ANSI X3.135-1992, ISO 9075-1992, and FIPS 127.2 standards.
*NONE - No checking will be done.
Debugging View:
*NONE - Source statements will not be available during debug.
*SOURCE - Source statements will be available during debug.
CRTSQLRPGI
User Profile:
*NAMING - If the naming convention is SQL, the *OWNER will be used. If the naming convention is *SYS, *USER will be used.
*USER - Authority is checked against the user profile of the user running the program.
*OWNER - Authority is checked against the user profile of the user who owns the program. (Usually the programmer who compiled it. Also referred to as Adopted authority).
CRTSQLRPGI
Dynamic User Profile:
*USER - Local Dynamic SQL statements are run under the user’s profile.
*OWNER - Local Dynamic SQL statements are run under the profile of the program’s owner.
CRTSQLRPGI
ILE Review
RPG IV ILEActivation Groups
Modules
Serive Programs
Sub Procedures
Built in Functions
Date & Time Support
New Op Codes
“D” Specs
ILE ReviewService PGM X
Module D
Module E
Module F (NOMAIN)
Sub Proc 1
Sub Proc 2
Sub Proc 3Module A
(Procedure Entry Point)
Module B Module C
Program Y
Embedding SELECT
DW_ITEM S 9P 0DW_DESC S 40 * Run the SQL select
C 'ITEM?' DSPLY W_ITEM C/Exec SQL SELECT MDESC C+ INTO :W_DESC C+ FROM ITMMST C+ WHERE MITEM = :W_ITEM C/End-exec
C W_DESC DSPLY C RETURN
Selecting ONE field from ONE record
Embedding SELECT
DW_ITEM S 9P 0DW_PRICE S 7P 2DW_DESC S 40 * Run the SQL select
C 'ITEM?' DSPLY W_ITEM C/Exec SQL SELECT MDESC,MPRICE C+ INTO :W_DESC, :W_PRICE C+ FROM ITMMST C+ WHERE MITEM = :W_ITEM C/End-exec
C W_DESC DSPLY C W_PRICE DSPLY C RETURN
Selecting TWO fields from ONE record
Embedding SELECT
DW_DATA E DS EXTNAME(ITMMST) * Run the SQL select
C 'ITEM?' DSPLY MITEM C/Exec SQL SELECT * C+ INTO :W_DATA C+ FROM ITMMST C+ WHERE MITEM = :MITEM C/End-exec
C MDESC DSPLY C MPRICE DSPLY C RETURN
Selecting MANY fields from ONE record
Embedding INSERT
There are three types of embedded INSERTS:
INSERT VALUES: Adds one row to a table using the values specified.
INSERT SELECT: Adds one or more rows using the results of a sub-select.
INSERT n ROWS: Adds multiple rows at once using the values specified in a multiple occurrence data structure.
Embedding INSERT* Run the SQL INSERT W/VALUES C/Exec SQL C+ INSERT INTO CUST C+ VALUES(7, ’The Fruit Stand’,’’,’’,C+ ’Miami’,‘FL’,’’,’’) C/End-exec
* Run the SQL INSERT W/SUB-SELECT
C/Exec SQL C+ INSERT INTO TESTLIB/CUST C+ SELECT * FROM PRODLIB/CUST C/End-exec
Embedding INSERT
* Run the SQL INSERT W/n ROWS
DW_FIELDS DS OCCURS(10) D W_ITEM 9 0 D W_DESC 40 D W_WGT 4 2 D W_PRICE 9 2
or D W_CUST E DS EXTNAME(CUST)
OCCURS(10) * Run the SQL select C/Exec SQL INSERT INTO ITMMST C+ 10 ROWS VALUES(:W_FIELDS) C/End-exec
Embedding UPDATE * Run the SQL UPDATE
C/Exec SQL UPDATE CUST C+ SET CSTATE = ‘FL’C+ WHERE CCITY = ‘Miami’ C/End-exec
C/Exec SQLC+ UPDATE CUSTWORK SET ROW = (SELECT * FROM CUST WHERE
C+ CUSTWORK.NAME = CUST.NAME) C/End-exec
Embedding DELETE
* Run the SQL DELETE C/Exec SQL DELETE FROM CUST C+ WHERE CNUMBER = :CUSTNBRC/End-exec
Using PREPARE
DW_SQL S 1000DW_SELECT C ‘SELECT MITEM ’DW_FROM C ‘FROM ITMMST ’DW_WHERE C ‘WHERE ’DW_FIELD S 10DW_VALUE S 30
Using PREPARE C ’FIELD?' DSPLY W_FIELD C ’VALUE?' DSPLY W_VALUE
C EVAL W_SQL= W_SELECT + W_FROM +C W_WHERE + W_FIELD +C ‘ = ‘ + W_VALUE
ORC ‘ = ‘’’ + W_VALUE + ‘’’’
Using PREPARE C/Exec SQL C+ PREPARE @S1 from :W_SQL C/End-execC/Exec SQL C+ DECLARE @C1 CURSOR FOR @S1 C/End-exec C/Exec SQL C+ OPEN @C1 C/End-execC DOU SQLCODE <> *zero C/Exec SQL C+ FETCH @C1 INTO :W_VALUE C/End-exec
C ENDDO C/Exec SQL C+ CLOSE @C1C/End-exec
Using PREPARE
PREPARE: An SQL statement can be entered into a character string variable. The prepare statement translates that into an executable statement. Prepare once, execute many times. The prepared statement cannot contain host variables.
DECLARE: If the SQL statement is a select statement, you must declare a cursor.
CURSOR: A cursor defines a result table and the current row within that table. It is read-only if data was selected from more than one table, the data was selected from a read-only table, or if the select statement used the DISTINCT keyword. (A number of other conditions could also cause the table to be read-only.)
OPEN: To perform a prepared SELECT, you must open the cursor. The result table will be created and the cursor will be set to the beginning of the result table.
FETCH: Use FETCH to read the next row from the result table. If you read past the end of the result table, the SQLCOD field will contain an error code of 100.
CLOSE: After you have finished reading the rows from a result table, close the cursor. This will allow you to reopen it later, retrieving new results.
Using PREPARE * DW_SQL S 1000DW_DELETE C ‘DELETE ’DW_FROM C ‘FROM ITMMST ’DW_WHERE C ‘WHERE ’DW_FIELD S 10DW_VALUE S 30 C ’FIELD?' DSPLY W_FIELD C ’VALUE?' DSPLY W_VALUE
C EVAL W_SQL= W_DELETE + W_FROM +C W_WHERE + W_FIELD +C ‘ = ‘ + W_VALUE
C/Exec SQL C+ PREPARE @S1 from :W_SQL C/End-exec
Using EXECUTE
EXECUTE: If the prepared statement is not a SELECT, use EXECUTE to perform the statement.
EXECUTE IMMEDIATE: If the character string contains no host variables or parameter markers, and is only going to be executed once, you can use EXECUTE IMMEDIATE to perform that statement.
Using EXECUTE
C/Exec SQL C+ EXECUTE @S1 C/End-exec
C/Exec SQL C+ EXECUTE IMMEDIATE :W_SQLC/End-exec
EXECUTE IMMEDIATE has to use a string variable. You cannot use an expression in quotes. You must first load the variable with the SQL statement.
Embedding SELECT
Multiple Row Fetch
* Run the SQL SELECT for n ROWS
DW_FIELDS DS OCCURS(10) D W_ITEM 9 0 D W_DESC 40 D W_WGT 4 2 D W_PRICE 9 2 C/EXEC SQL FETCH C1 FOR 10 ROWS C+ INTO :W_FIELDS C/END-EXEC
Embedding SELECT
Multiple Row Fetch
It is possible to read more than one record at a time, loading a MODS with values for several records at once. This is more efficient than the standard one record at a time method.
Three variables will be updated in the SQLCA:
SQLERRD(3) shows the number of rows retrieved.
SQLERRD(4) shows the length of the row retrieved.
SQLERRD(5) shows 100 if the last row was read.
Using Parameter Markers
* DW_SQL S 1000DW_SELECT C ‘SELECT MITEM ’DW_FROM C ‘FROM ITMMST ’DW_WHERE C ‘WHERE MITEM = ?’DW_VALUE S 30 C ’ITEM?' DSPLY W_VALUE
C EVAL W_SQL= W_SELECT + W_FROM +C W_WHERE
Using Parameter Markers
C/Exec SQL C+ PREPARE @S1 from :W_SQL C/End-execC/Exec SQL C+ DECLARE @C1 CURSOR FOR @S1 C/End-exec C/Exec SQL C+ OPEN @C1 USING :W_VALUE C/End-execC DOU SQLCOD <> *zero C/Exec SQL C+ FETCH @C1 INTO :W_VALUE C/End-exec
C ENDDO
C/Exec SQL C+ CLOSE @C1C/End-exec
Using Parameter Markers
DW_SQL S 1000DW_DELETE C ‘DELETE ’DW_FROM C ‘FROM ITMMST ’DW_WHERE C ‘WHERE MITEM IN (?, ?)’DW_VALUE1 S 30DW_VALUE2 S 30
C ’ITEM?' DSPLY W_VALUE1 C ’ITEM?' DSPLY W_VALUE2
C EVAL W_SQL= W_DELETE + W_FROM +C W_WHERE
C/Exec SQL PREPARE @S1 from :W_SQL C/End-exec
C/Exec SQL EXECUTE @S1 USING :W_VALUE1, :W_VALUE2 C/End-exec
Using Parameter Markers
DW_SQL S 1000DW_DELETE C ‘DELETE ’DW_FROM C ‘FROM ITMMST ’DW_WHERE C ‘WHERE MITEM LIKE ?’DW_VALUE1 S 30DW_COMP S 30 VARYING C ’ITEM?' DSPLY W_VALUE1 C EVAL W_COMP = %TRIM(W_VALUE1)
C EVAL W_SQL= W_DELETE + W_FROM +C W_WHERE
Using Parameter Markers
C/Exec SQL C+ PREPARE @S1 from :W_SQL C/End-exec
C/Exec SQL C+ EXECUTE @S1 USING :W_COMP C/End-exec
Be careful when using LIKE with host variables or parameter markers. The embedded blanks at the end of the field will cause the LIKE to fail. Use of a variable length field will eliminate this problem
Using Parameter Markers
Parameter markers cannot be used in all parts of an SQL statement. Here are some of the ways in which parameter markers CANNOT be used:
• In a select list - SELECT ? (except as a sub-query)• In a Concatenation - ? || ‘FRED’• As both parts of a comparison (WHERE ? = ?)
To avoid this problem use the CAST function to specify the data type of the parameter.
CAST(? AS CHAR)CAST(? AS NUMERIC)
SQL Error Handling - SQLCA
SQLCODE
0 - SuccessPositive - WarningNegative - Error
D SQLCA DS
D SQLAID 1 8A
D SQLABC 9 12B 0
D SQLCOD 13 16B 0
D SQLERL 17 18B 0
D SQLERM 19 88A
D SQLERP 89 96A
D SQLERRD 97 120B 0 DIM(6)
D SQLERR 97 120A
D SQLER1 97 100B 0
D SQLER2 101 104B 0
D SQLER3 105 108B 0
D SQLER4 109 112B 0
D SQLER5 113 116B 0
D SQLER6 117 120B 0
SQL Communications Area
D SQLWRN 121 131A
D SQLWN0 121 121A
D SQLWN1 122 122A
D SQLWN2 123 123A
D SQLWN3 124 124A
D SQLWN4 125 125A
D SQLWN5 126 126A
D SQLWN6 127 127A
D SQLWN7 128 128A
D SQLWN8 129 129A
D SQLWN9 130 130A
D SQLWNA 131 131A
D SQLSTT 132 136A
SQL Communications Area
SQL Communications Area
SQLERL - Contains the length of the text loaded into SQLERM.
SQLERM - Contains Message text for the SQLCOD value.
SQLERP - Contains the name of the Product and Module returning an error.
SQLERRD - Array containing 6 diagnostic codes.
(1) - Last four digits of the CPF message if SQLCOD is negative.
(2) - Last four digits of the CPD message if SQLCOD is negative.
(3) - For INSERT, UPDATE, DELETE, and FETCH, the number of rows affected. For PREPARE, the estimated number of rows returned.
(4) - For PREPARE, the estimated cost of execution. For CALL, the message key of the error which caused the call to fail. For trigger errors during a INSERT, UPDATE, or DELETE, the message key of the error. For FETCH, the length of the row.
(5) - For DELETE, the number of rows affected by referential constraints. For PREPARE or EXECUTE IMMEDIATE, the position of a syntax error. For multi-row FETCH, +100 if the last record was read. For CONNECT and SET CONNECTION, -1 if not connected, 0 if local, 1 if remote. For PREPARE, the number of parameter markers.
(6) - SQL completion code when SQLCOD is 0.
SQL Communications Area
SQLWRN - String of 11, 1 byte flags.
(0) - Master flag, will contain a ‘W’ if any flags contain a ‘W’ or ‘N’. (1) - Contains a ‘W’ if a string column was truncated while being loaded into host variable.(2) - Contains ‘W’ if NULL values were eliminated from an argument of a function. (3) - Contains a ‘W’ if the number of columns exceeds the number of host variables.(4) - Contains a ‘W’ if UPDATE or DELETE do not contain a WHERE clause. (5) - Reserved. (6) - Contains a ‘W’ if date arithmetic results in an end of month adjustment. (7) - Reserved. (8) - Contains a 'W' if the result of a character conversion contains a substitution character. (9) - Reserved.(10) - Reserved.
SQLSTT - A return code for the most recent SQL statement.
(For detail on the SQLSTATE (SQLSTT) values, see Appendix B in manual “DB2 UDB for AS/400 SQL Programming”.)
You can find more information on-line at: publib.boulder.ibm.com
in the manual: DB2 for AS/400 SQL programming.
SQL Error Handling
WHENEVER…
In addition to the SQLCA, other error handling features are available.
Use the SQL WHENEVER statement to set up error trapping before issuing other SQL statements.
WHENEVER NOT FOUND … = IF SQLCODE = 100
WHENEVER SQLERROR … = IF SQLCOD < *ZERO
WHENEVER SQLWARNING … = IF SQCOD > *ZERO and SQLCOD <> 100
The WHENEVER statement will go into effect until a new WHENEVER statement of the same type is issued. This eliminates needing to test each SQL statement for an error. In general, this function is very similar to the global MONMSG function in CL. However the statements are processed by compile in the sequence they are written, not at run time in the order they are executed. Therefor their use is discouraged.
* Run the SQL INSERT W/VALUES C/Exec SQL C+ WHENEVER SQLWARNING CONTINUE C/End-execC/Exec SQL C+ WHENEVER NOT FOUND GOTO DONE C/End-execC/Exec SQL C+ WHENEVER SQLERROR GOTO ERROR C/End-exec C DONE TAG
C ERROR TAG
SQL Error Handling
ADVANCED CURSORS
SCROLL - Allows forward and back movement of the cursor.
DYNAMIC SCROLL - As Scroll but also allows update.
WITH HOLD - Allows COMMIT to be performed without closing the cursor.
In the DECLARE CURSOR statement you can add additional clauses to provide more capability within the program.
By default a FETCH will perform sequential reads against the data in the specified order.
Specify SCROLL to allow more flexibility in moving the cursor. Specify DYNAMIC SCROLL to allow update. Specify WITH HOLD if you want to use commitment control with the updates.
ADVANCED CURSORS
ADVANCED CURSORS
FETCH NEXT - positions cursor ON next record.
PRIOR - positions cursor ON previous record.
FIRST - positions cursor ON the first record in the result table.
LAST - positions cursor ON the last record in the result table.
BEFORE - positions cursor BEFORE the first record in the result table.
AFTER - positions cursor AFTER the last record in the result table.
CURRENT - no change.
RELATIVE - A negative number moves back x rows. A positive value moves forward X rows.
C+ DECLARE @QC1 DYNAMIC SCROLL CURSOR WITH HOLD C+ FOR SQLSTMT @S1
C+ FETCH PRIOR FROM @QC1 INTO :W_DATA1 :W_IND1, C+ :W_DATA2 :W_IND2, :W_DATA3 :W_IND3
C+ FETCH RELATIVE -1 FROM @QC1 INTO :W_DATA1 :W_IND1, C+ :W_DATA2 :W_IND2, :W_DATA3:W_IND3
C+ FETCH BEFORE FROM @QC1
C+ DECLARE @C2 DYNAMIC SCROLL CURSOR WITH HOLD FOR C+ SELECT * FROM DATA FOR UPDATE OF LONGNAM2
C+ FETCH FIRST FROM @QC2 INTO :W_FIELDS
C+ UPDATE DATA SET LONGNAM2 = ‘FRED’ C+ WHERE CURRENT OF @C2
ADVANCED CURSORS
ADVANCED CURSORS
The FOR UPDATE OF clause in the SELECT statement indicates which columns in the table are valid for update.
Specifying just FOR UPDATE will make all columns updateable.
Updateable columns cannot be referenced in the ORDER BY clause.
SQL Commitment Control
UPDATE MASTER RECORD
UPDATE MASTER RECORD
INSERT HISTORY RECORD
INSERT HISTORY RECORD
DELETE TRANSACTION
DELETE TRANSACTION
COMMIT
COMMIT
Commit
SQL Commitment Control
Commitment control is used to group multiple file updates together, and treat them as a single transaction. Either they all succeed, or they all fail. This ensures data integrity even in the case of a program crash. All updates are considered tentative, until the commit is performed.
The Commit command requires commitment control to be active. To determine if it is active, check each user’s Query Manager profile to determine their commitment control level. There are five different levels. Each level provides increasing levels of control.
1) NONE - Commitment control is not active. It is the default setting, and COMMIT and ROLLBACK cannot be used with this setting.
2) CHG - Rows that have been inserted, updated, or deleted will be locked until a COMMIT or ROLLBACK is performed.
3) CS - (Cursor Stability) As CHG, but in addition all rows selected in a WHERE clause are locked until you have scrolled past them.
4) ALL - All rows that were read, inserted, updated, or deleted are locked until a COMMIT or ROLLBACK is performed.
5) RR - (Repeatable Read) As ALL, but in addition all tables are locked.
SQL Commitment Control
The rollback command also requires commitment control to be active. Unlike COMMIT, ROLLBACK is rarely used in SQL. The system will automatically issue a roll back when a SQL job ends abnormally, and on exit from an interactive SQL session. Be sure to COMMIT all changes, before pressing F3 to exit.
Also be aware that when using commitment control in an interactive SQL session you may be keeping records locked for extended periods of time. This may interfere with other users on the system.
Rollback
SQL Commitment Control
COMMITMENT CONTROL
After reading a transaction record:
1) The master file will be updated2) A record will be added to the history file3) The transaction record will be deleted
All of these transactions will be done as a group. Either they all
succeed or they all fail. This will simplify restarting the program after
an error.
D W_FIELDS E DS EXTNAME(TRAN)
D W_SQL S 1000 C EVAL W_SQL = 'SELECT * + C FROM TRAN FOR UPDATE' C/EXEC SQL WHENEVER SQLERROR GOTO ERROR
C/END-EXEC C/EXEC SQL PREPARE SQLSTMT FROM :W_SQL C/END-EXEC
COMMITMENT CONTROL
c* C/EXEC SQL C+ DECLARE @QC1 DYNAMIC SCROLL CURSOR WITH HOLD FOR SQLSTMT C/END-EXEC C/EXEC SQL C+ OPEN @QC1 C/END-EXEC C/EXEC SQL C+ FETCH NEXT FROM @QC1 INTO :W_FIELDS C/END-EXEC
COMMITMENT CONTROL
C DOW SQLCOD = *ZERO C/EXEC SQL C+ UPDATE MAST SET OHQTY = OHQTY + :TRQTY C+ WHERE MAST.PART = :PART C/END-EXEC C/EXEC SQL C+ INSERT INTO HIST VALUES(:W_FIELDS) C/END-EXEC
C/EXEC SQL C+ DELETE FROM TRAN WHERE CURRENT OF @QC1 C/END-EXEC C COMMIT C/EXEC SQL C+ FETCH NEXT FROM @QC1 INTO :W_FIELDS C/END-EXEC C ENDDO
COMMITMENT CONTROL
C ERROR TAG C ROLBK C END TAG C/EXEC SQL C+ WHENEVER SQLERROR CONTINUE C/END-EXEC * Close SQL cursor C/EXEC SQL C+ CLOSE @QC1 C/END-EXEC C RETURN
COMMITMENT CONTROL
C/EXEC SQL C+ UPDATE DATA SET LONGNAM2 = NULL WHERE NAME IS NOT NULLC/END-EXEC C/EXEC SQL C+ SELECT * FROM DATA WHERE LONGNAM2 IS NULLC/END-EXEC
Null Values
Null Values
NULL values are “unknown” values, not blank, not zero, not anything, simply unknown. You cannot reference the data in a field if it has a NULL value.
Not all fields can contain NULL values. An even value in SQLTYPE denotes that the field can not contain NULL. An odd value denotes that NULL is allowed.
SQL provides a mechanism for testing fields for NULL values. An associated indicator will be loaded with negative 1 if the field contains a NULL value, as seen on the next page.
* Single Row FETCH testing for NULL values
DW_NAME S 30 DW_LONGNAM1 S 120 DW_LONGNAM2 S 120 DW_IND1 S 5I 0 DW_IND2 S 5I 0 C/EXEC SQL C+ FETCH NEXT FROM @QC1 INTO :W_NAME :W_IND1, C+ :W_LONGNAM1, :W_LONGNAM2 :W_IND2 C/END-EXEC
Null Values
* Multi Row FETCH testing for NULL values
DW_FIELDS DS OCCURS(10) D W_NAME 30 D W_LONGNAM1 120 D W_LONGNAM2 120
DW_IND DS OCCURS(10) D W_IND1 5I 0 D W_IND2 5I 0D W_IND3 5I 0
C/EXEC SQL FETCH @QC1 FOR 10 ROWS C+ INTO :W_FIELDS :W_INDC/END-EXEC
Null Values
CREATE PROCEDURE RPGPROC(IN PARM1 CHAR (30)) LANGUAGE RPGLE NOT DETERMINISTIC CONTAINS SQL EXTERNAL NAME RPGPROC PARAMETER STYLE GENERAL
Stored Procedures
CALL RPGPROC (‘DATA’)
Stored Procedures
How to Embed RPG in your SQL?
The Service Program is an extremely powerful tool for deploying applications. Essentially it allows you to extend both SQL and RPG to include new features that you need.
Build Service Programs!
User Defined Functions (UDFs) allow you to extend the capabilities of the SQL engine, and more tightly integrate it with your database. RPG ILE is often used to develop these functions.
Build User Defined Functions!
1) Have one binding directory for all tools – i5Tools
2) Have one copy book for all prototypes – i5Tools
3) Document Procedures in the Copy Book
Keep It Simple System
Service Program Code
ADDBNDDIRE BNDDIR(I5TOOLS) OBJ((MsgBox))ADDBNDDIRE BNDDIR(I5TOOLS) OBJ((Dates))ADDBNDDIRE BNDDIR(I5TOOLS) OBJ((Fetch))
CRTBNDDIR i5Tools
Create Binding Directory
Add Service Programs to directory
1)Message Box Utility
2) Get Text Descriptions of Dates
3) Perform SQL Fetch
Service Program Examples
H NOMAIN EXPROPTS(*RESDECPOS) H OPTION(*SRCSTMT:*NODEBUGIO) FMSGBOXDF CF E WORKSTNF USROPN
MsgBox Code Part 1 of 4
DMsgBox PR D Msg1 50A VALUED Msg2 50A OPTIONS(*NOPASS)D VALUED Msg3 50A OPTIONS(*NOPASS)D VALUED Msg4 50A OPTIONS(*NOPASS)D VALUE
PMsgBox B EXPORT DMsgBox PI D Msg1 50A VALUED Msg2 50A OPTIONS(*NOPASS)D VALUED Msg3 50A OPTIONS(*NOPASS)D VALUED Msg4 50A OPTIONS(*NOPASS)D VALUE
MsgBox Code Part 2 of 4
C OPEN MSGBOXDF C Clear Text1C Clear Text2C Clear Text3C Clear Text4
MsgBox Code Part 3 of 4
C EVAL Text1 = Msg1 C SELECT C WHEN %PARMS = 2 C EVAL Text2 = Msg2 C WHEN %PARMS = 3 C EVAL Text2 = Msg2 C EVAL Text3 = Msg3 C WHEN %PARMS = 4 C EVAL Text2 = Msg2 C EVAL Text3 = Msg3 C EVAL Text4 = Msg4 C ENDSL C EXFMT Win1 C CLOSE MSGBOXDF C Return PMsgBox E
MsgBox Code Part 4 of 4
A R WIN1 A WINDOW(10 10 8 60- A *NOMSGLIN) A PUTOVR A WDWBORDER( -A (*COLOR BLU)-A (*DSPATR RI)-A (*CHAR ' ')) A 1 26'Message Box' A DSPATR(HI)
MsgBox DDS Part 1 of 2
A TEXT1 50 O 3 6 A TEXT2 50 O 4 6 A TEXT3 50 O 5 6 A TEXT4 50 O 6 6 A 8 2'F12 = Cancel' A COLOR(BLU) A R DUMMY A ASSUME
A 1 3' '
MsgBox DDS Part 2 of 2
Service Program Code
CRTSRVPGM SRVPGM(MYFLIB/MsgBox) MODULE(MsgBox) EXPORT(*ALL)
CRTRPGMOD MODULE(MYLIB/MsgBox) SRCFILE(MYLIB/i5Tools) SRCMBR(MsgBox)
Create Module
Create Service Program
Putting it to the test
H BNDDIR(‘I5TOOLS’)/COPY i5Tools,i5Tools
/FREE MsgBox(‘What a Cool Tool!’); MsgBox(‘First one line…’: ‘Then another!’); *INLR = *ON; /END-FREE
Putting it to the test
Getting Date Names
Getting Date Names
02/14/2006
Order 123 scheduled to ship on Tuesday
Month End Close for February is Complete
Shipment due for Monday, February 14 2006
H NOMAIN EXPROPT(*RESDECPOS) H OPTION(*SRCSTMT:*NODEBUGIO) H COPYRIGHT('DMC Consulting 2005')
/COPY i5Tools,i5Tools
Service Program Code Part 1 of 4
D GetDayNam PR 10 D InpDat D
D GetMonNam PR 10 D InpDat D
D GetDatNam PR 40
D InpDat D
PGetDayNam B EXPORT DGetDayNam PI 10 D Date d D Daynam S 10 C/EXEC SQL C+ SET :DayNam = DAYNAME(:Date) C/END-EXEC C return DayNam P E
Service Program Code Part 2 of 4
Service Program Code Part 3 of 4
PGetMonNam B EXPORT DGetMonNam PI 10 D Date d D MonNam S 10 C/EXEC SQL C+ SET :MonNam = MONTHNAME(:Date) C/END-EXEC C return MonNam P E
Service Program Code Part 4 of 4
PGetDatNam B EXPORT DGetDatNam PI 40 D Date d D Datnam S 40 C/EXEC SQL C+ SET :DatNam = DAYNAME(:Date) || ' ' || C+ MONTHNAME(:Date) || ' ' || C+ CHAR(DAYOFMONTH(:Date)) || ', ' C+ CHAR(YEAR(:Date)) C/END-EXEC C return DatNam P E
Service Program Code
CRTSRVPGM SRVPGM(KPFLIB/Dates) MODULE(Dates) EXPORT(*ALL)
CRTRPGMOD MODULE(KPFLIB/Dates) SRCFILE(KPFLIB/i5Tools) SRCMBR(Dates)
Create Module
Create Service Program
Putting it to the test
H BNDDIR(‘I5TOOLS’)/COPY i5Tools,i5Tools
/FREE Date = %DATE(); DayNam = GetDayNam(DATE); MsgBox(‘Order ’ + Ordno + ‘ scheduled ’ + ‘to ship ’ + DayNam); *INLR = *ON; /END-FREE
Getting Date Names
02/14/2006
Order 123 scheduled to ship on Tuesday
Putting it to Use
H BNDDIR(‘I5TOOLS’)/COPY i5Tools,i5Tools
/FREE Date = %DATE(); DayNam = GetDayNam(DATE); MsgBox(‘Order ’ + Ordno + ‘ is due to ’ + ‘ship on ’ + DayNam); *INLR = *ON; /END-FREE
Put these two lines in every program
User Defined Functions
1)Unpack Data
2)Running Totals
Getting Packed Data
ID Customer ? General Motors? Honda? Toyota? Ford? Chrysler
SELECT SUBSTR(CUSTFLAT,4,30), DECIMAL(SUBSTR(HEX(SUBSTR( CUSTFLAT,1,3)),1,5),5,0) * CASE WHEN SUBSTR(HEX(SUBSTR( CUSTFLAT,3,1)),2,1) ='F' THEN 1 ELSE -1 END FROM CUSTFLAT
SQL Syntax to Unpack
RPG Syntax to Unpack Part 1 of 2
PUnPack B DUnPack PI 15P 2 D Text 3000A Varying D Start 5I 0D Len 5I 0 D RawDS DS D Packed 15P 0
RPG Syntax to Unpack part 2 of 2
*----------------------------------------------- * Load Text Into Packed Field *-----------------------------------------------C Clear Packed C EVALR %SUBST(RAWDS:C 9-Len:Len) = C %SUBST(Text:Start:Len) C Return Packed PUnPack E
RPG Syntax to Unpack
00000000RawDS
0000000F
001Text
00F
00000001RawDS
0000000F
RPG Syntax to Unpack
CRTSRVPGM SRVPGM(KPFLIB/UNPACK) MODULE(UNPACK) EXPORT(*ALL)
CRTRPGMOD MODULE(KPFLIB/UnPack) SRCFILE(KPFLIB/i5Tools) SRCMBR(UnPack)
Create Module
Create Service Program
RPG Syntax to Unpack
CREATE FUNCTION MYLIB/UnPack (IN VARCHAR(2000), IN INTEGER, IN INTEGER) RETURNS DECIMAL(15,0) EXTERNAL NAME ‘MYLIB/UNPACK(UNPACK)' LANGUAGE RPGLE PARAMETER STYLE GENERAL)
Register Function With SQL (SQL CMD)
RPG Syntax to Unpack
Select Unpack(CUSTFLAT,1,3) from CUSTFLAT
Use the Function!
UNPACK 00001 00002 00003 00005
SELECT SUBSTR(CUSTFLAT,4,30), DECIMAL(SUBSTR(HEX(SUBSTR( CUSTFLAT,1,3)),1,5),5,0) * CASE WHEN SUBSTR(HEX(SUBSTR( CUSTFLAT,3,1)),2,1) ='F' THEN 1 ELSE -1 END
FROM CUSTFLAT
RPG Syntax to Unpack
Instead of this!
Running Totals
1001 10 10
1002 12 22
1003 7 29
1004 15 44
1005 11 55
Order Qty Total
Running Totals
* Prototype for RunSum(QTY) D RunSum PR D Parm1 8F D QtyOut 8F D InNull1 5I 0 D OutNull 5I 0 D SqlState 5 D FuncName 517A VARYING D SpecName 128A VARYING D MesgText 70A VARYING D Scratch 8F D Flag 10I 0
DB2SQL Parameter style
Running Totals
P RunSum B EXPORT D RunSum PI D QtyIn 8F D QtyOut 8F D InNull1 5I 0 D OutNull 5I 0 D SqlState 5 D FuncName 517A VARYING D SpecName 128A VARYING D MesgText 70A VARYING D Scratch 8F D Flag 10I 0
Running Totals
* Trap for any error that occurs C MONITOR * If this is the first time it’s called * for this select, set the balance to 0 C IF FLAG = -1 C Clear Scratch C ENDIF * Add the input quantity to the scratch quantity C ADD QtyIn Scratch C Z-ADD Scratch QtyOut * Return the quantity C RETURN
Running Totals
* If any error occurs return a zero C ON-ERROR *PROGRAM C C EVAL QtyOut = 0 C RETURN C ENDMON P E
Running Totals
CRTSRVPGM SRVPGM(KPFLIB/RunSum) MODULE(RunSum) EXPORT(*ALL)
CRTRPGMOD MODULE(KPFLIB/RunSum) SRCFILE(KPFLIB/i5Tools) SRCMBR(RunSum)
Create Module
Create Service Program
Running Totals
CREATE FUNCTION your-schema/RunSum (INOUT DOUBLE) RETURNS DOUBLE EXTERNAL NAME 'your-schema/SQLFUNCS(RunSum)' LANGUAGE RPGLE PARAMETER STYLE DB2SQL NOT DETERMINISTIC SCRATCHPAD 8 FINAL CALL DISALLOW PARALLEL)
Register Function With SQL (SQL CMD)
Running Totals
Select Order, Qty, Runsum(Qty) from ORDMAST
Use the Function!
1 10 10
1001 12 22
1002 7 29
1003 15 44
1004 11 55
Order Qty Total
Canned SQL
1)Fetch This!
1)Easy Load all Subfile
Canned SQL
RPG Syntax to Fetch
H EXPROPTS(*RESDECPOS) BNDDIR('I5TOOLS') H DFTACTGRP(*NO) ACTGRP(*NEW) H OPTION(*SRCSTMT:*NODEBUGIO)FSAMPSUBDF CF E WORKSTN PREFIX(S_) F SFILE(SUBFILE:SflRRN) *--------------------------------------------------------
RPG Syntax to Fetch/COPY i5Tools,i5Tools D RefDS E DS EXTNAME(Reffile)D Prefix(Ref)D Based(Ptr_Not_Used) D PTR S * INZ(%ADDR(*IN)) D INDARRAY DS BASED(PTR) D Exit 03 03N D SflDsp 10 10N D SflDspCtl 11 11N D SflEnd 14 14N
RPG Syntax to Fetch D Stmt S 512 D SflRRN S 4S 0 D NumRows S 4S 0 * D Cust DS Qualified D CNumber LIKE(RefCnumber) D CName LIKE(RefCname)
RPG Syntax to Fetch
*-------------------------------------------------------- * Load SQL Results into subfile *--------------------------------------------------------C CALLP Fetch('Select Cnumber,Cname +C from Cust': NumRows)
C DoW SflRRN < NumRows C CALLP Load(Cust) C EVAL SflRRN = SflRRN + 1 C Eval S_CName = Cust.CName C Eval S_CNumber = Cust.CNumber C Eval S_Opt = *BLANK C Write SUBFILE C Enddo
RPG Syntax to FetchC EVAL SflDspCtl = *ON C EVAL SflDsp = *ON C EVAL SflEnd = *ON C EVAL S_S@RRN = 1 C DoU Exit C Write FKeys C Exfmt Control C Enddo C Eval *INLR = *ON C Return
Dynamic SQL SyntaxC/Exec SQL C+ PREPARE stmt1 from :W_SQL C/End-exec
C/EXEC SQL C+ DESCRIBE Stmnt1 INTO :SQLDA C/END-EXEC
Select Cnumber,Cname from Cust
SQLVAR Array Field Type Field Length Pointer to Data
Dynamic SQL SyntaxDSQLDA DS D SQLDAID 8A D SQLDABC 10I 0 D SQLN 5I 0 D SQLD 5I 0 D SQLVAR 80A DIM(120) D SQLTYPE 5I 0 OVERLAY(SQLVAR:1) D SQLLEN 5I 0 OVERLAY(SQLVAR:3) D SQLRES 12A OVERLAY(SQLVAR:5) D SQLDATA * OVERLAY(SQLVAR:17) D SQLIND * OVERLAY(SQLVAR:33) D SQLNAMELEN 5I 0 OVERLAY(SQLVAR:49) D SQLNAME 30A OVERLAY(SQLVAR:51)
Dynamic SQL SyntaxC EVAL RowLen = 0 C DO SQLD X C SELECT * Small Integer C WHEN SQLTYPE(X) = 500 C EVAL W_Len = 2 * Large Integer C WHEN SQLTYPE(X) = 496 C EVAL W_Len = 4
Dynamic SQL Syntax
* Zoned decimal format C WHEN SQLTYPE(X) = 488 C SQLLEN(X) DIV 256 W_LEN C EVAL W_Len = %INT(%DEC( C W_Len / 2:7:1) + 1) * Packed decimal format C WHEN SQLTYPE(X) = 484
C SQLLEN(X) DIV 256 W_LEN
Dynamic SQL Syntax
C/EXEC SQL C+ FETCH @C1 USING DESCRIPTOR :SQLDA
C/END-EXEC
Col 1
Col 2
Col 3
SQLVAR(1) SQLVAR(2) SQLVAR(3)
Fetch 1 row of data into SQLVAR
Dynamic SQL Syntax
Col 1
Col 2
Col 3
Concatenate all columns
Row Data
Dynamic SQL Syntax
Pointer to DataPointer to Next Elem.
Pointer to DataPointer to Next Elem.
Pointer to DataPointer to Next Elem.
Pointer to DataPointer to Next Elem.
Pointer to DataPointer to Next Elem.
Dynamic SQL Syntax
RsltData (1st address)
CUST DSCNUMBER CNAME
Why?
Simpler Applications…C CALLP Fetch('Select Cnumber, +C Cname from Cust': NumRows)C DoW SflRRN < NumRows C CALLP Load(Cust) C EVAL SflRRN = SflRRN + 1 C Eval S_CName = Cust.CName C Eval S_CNumber = Cust.CNumber C Eval S_Opt = *BLANK C Write SUBFILE C Enddo
Embedded SQL w/o Precompiler
Avoid Record format Level checks
Dynamic sort and selection options
Just a starting point for your own tools…
1)Fetch and Throw!
To the Web!
1)Easy web display
Canned SQL
RPG Syntax to WebH EXPROPTS(*RESDECPOS) BNDDIR('I5TOOLS')H ACTGRP(*NEW) DFTACTGRP(*NO) /copy CGIDEV2/qrpglesrc,hspecs /copy CGIDEV2/qrpglesrc,hspecsbnd *================================================== * Includes to be used in CGIs *================================================== /copy CGIDEV2/qrpglesrc,prototypeb /copy CGIDEV2/qrpglesrc,usec /copy CGIDEV2/qrpglesrc,variables3 *================================================== /COPY i5Tools,i5Tools
RPG Syntax to Web
D RefDS E DS EXTNAME(Reffile)D Prefix(Ref) D Stmt S 512 D X S 4S 0 D NumRows S 4S 0 * D Customer DS Qualified D CNumber LIKE(RefCnumber) D CName LIKE(RefCname)
RPG Syntax to Web
* Main line *==================================================C CALLP DoCmd('ADDLIBLE KPFLIB') C CALLP DoCmd('ADDLIBLE + C CGIDEV2') /copy CGIDEV2/qrpglesrc,prolog3 * Load HTML C callp gethtml('HTMLSRC': C 'KPFLIB':'SAMPWEB2') * C callp wrtsection('top') * C CALLP Fetch('Select Cnumber, +C Cname from Cust’: C NumRows)
RPG Syntax to Web
C DoW X < NumRows C Add 1 X C CALLP Load(Customer) C callp updhtmlvar('cnumber': C %Char(Customer.Cnumber))C callp updhtmlvar('cname': C Customer.CName) C callp wrtsection('row') C Enddo C callp wrtsection('bottom') C callp wrtsection('*fini') C Eval *INLR = *ON C Return
HTML Syntax<HTML> <HEAD><title>Sample Web Page One</title> </HEAD> <BODY> <FORM NAME="SELECT" method="POST" action="/kpflib/sampweb.pgm"><CENTER><H2>Customer Data</H2> Display Customer Data <BR><BR> <INPUT type="submit" value=" List Customers "> </CENTER> </FORM> </body></html>
First Page
HTML Syntax
/$top <html> <head><title>Customer List</title> </head> <body> <CENTER> <h2>Customer List</h2> <table> <tr> <th>Cus. Nbr</th> <th>Customer Name</th> </TR>
Second Page (1 of 2)
HTML Syntax
/$row <tr> <td>/%cnumber%/</TD> <td>/%cname%/</td></tr> /$bottom </TR></TABLE></CENTER> </body></html>
Second Page (2 of 2)
Canned SQL
Performance?
Indexes –Creation OrderKey SequenceIndex TypeUse EVIOptimizer Time
Out
Visual DebugCoding Techniques
Performance
There are often times many different ways to achieve the results you desire. Some will be more efficient than others.
This example illustrates using a sub-select to generate the value of a single column. This is not a very efficient technique.
This more complex example shows how to join a file to a temporary sub-total file generated by a sub-select over the same file.
Performance
This additional example illustrates the WITH clause and a prelude to the SELECT statement. It predefines the sub-select as a temporary table object, and in many cases can greatly simplify the code in the rest of the select statement.
The second and third example are similar from a performance standpoint. But this last example is the better choice in general.
PerformanceAnother way to improve performance is by limiting the number of records selected. Add the FETCH FIRST x ROWS ONLY clause.
In Embedded SQL statements, if you will be processing the result set in groups of a certain number of records. You may wish to add the OPTIMIZE clasue.
SQE vs CQE
CQE - is an older less efficient version of the SQL engine – it does however support some features which the newer SQE engine does not.
SQE – is the latest and most advanced SQL engine available for the iSeries. There are some features which it does not yet support though, and SQL commands utilizing such features will be rerouted to the CQE engine.
By default the SQE engine will not process files with logical files which include Select/Omit statements. To instruct the SQE engine to ignore these logical files ensure that your copy of the QAQQINI file has the parameter: IGNORE_DERIVED_INDEX set to *YES.
See more info athttp://www-03.ibm.com/servers/eserver/iseries/db2/sqe.html
Additional Resources
IBM Database Homepage: http://www-03.ibm.com/servers/eserver/iseries/db2/
IBM SQE Documentation: http://www-03.ibm.com/servers/eserver/iseries/db2/sqe.html
IBM Info Center: http://publib.boulder.ibm.com/infocenter/iseries/v5r4/index.jsp
SQL Book (Shameless self promotion): http://www.mc-store.com/5063.html
Contact me for additional help or information on additional training options. You can reach me at:[email protected], or call me at (419) 535-2900.
Why Embed SQL in your RPG?
COMPILE TIME Vs. RUN TIMEF
Specs