Está en la página 1de 93

SQL Tips &

Techniques
Fred Gamache
Agenda
 Query Manager
 SQL Syntax

 Embedded SQL

 Stored Procedures

 Performance
30 Second Bio
 Started on S/38 in 1986 – in consulting since 1989
 Worked on AS/400 doing primarily RPG application
development and support
 Written a few articles for AS/400 magazines
 Started Independent Consulting in 2003
 Currently using
 DB2 – RPG – SQL
 Oracle – PL/SQL
 SQL Server – VB.Net and ASP.Net
Goals & Assumptions
 Assuming a basic knowledge of SQL syntax.
 My goal is to give you some tips and
techniques that you can use tomorrow.

 Stop me if you’re not clear on something


 Presentation can be downloaded from
www.gamacheconsulting.com
Query Manager
Query Manager vs. Query Manager
and SQL Developers Kit
Query Manager Developers Kit
 Comes with every iSeries  Separate chargeable
 Can create a QM object product
based on a source
member containing a SQL
 QM has a front end similar
statement to Query/400
 No front end – just a  STRSQL – gives you the
command interface ability to create an SQL
 No green screen statement via a prompted
Interactive SQL interface
 Cannot compile a program  Ability to compile programs
with embedded with Embedded SQL
 Can Execute a program
that has been compiled
with Embedded SQL
Query Manager
 No front end – just a command interface
 CRTQMQRY – create
 STRQMQRY - execute
 Create an object from a source file member
 Source can contain any single valid SQL
statement
 Compilation and Execution are separate
steps – just like a program
Query Manager – Create QM Query
Create Query Management Query (CRTQMQRY)

Type choices, press Enter.

Query management query . . . . . Name


Library . . . . . . . . . . . *CURLIB Name, *CURLIB
Source file . . . . . . . . . . QQMQRYSRC Name
Library . . . . . . . . . . . *LIBL Name, *LIBL, *CURLIB
Source member . . . . . . . . . *QMQRY Name, *QMQRY
Text 'description' . . . . . . . *SRCMBRTXT

Sort sequence . . . . . . . . . *SRC Name, *SRC, *JOBRUN, *JOB...


Library . . . . . . . . . . . *LIBL Name, *LIBL, *CURLIB
Language ID . . . . . . . . . . *SRC *JOB, *JOBRUN...

Additional Parameters

Authority . . . . . . . . . . . *LIBCRTAUT Name, *USE, *CHANGE, *ALL...


Replace object . . . . . . . . . *YES *YES, *NO
Query Manager – Start QM
Query
Start Query Management Query (STRQMQRY)

Type choices, press Enter.

Query management query . . . . . Name


Library . . . . . . . . . . . *LIBL Name, *LIBL, *CURLIB
Output . . . . . . . . . . . . . * *, *PRINT, *OUTFILE
Query management report form . . *SYSDFT Name, *SYSDFT, *QMQRY
Library . . . . . . . . . . . Name, *LIBL, *CURLIB

Additional Parameters

Relational database . . . . . . *NONE


Connection Method . . . . . . . *DUW *DUW, *RUW
User . . . . . . . . . . . . . . *CURRENT Name, *CURRENT
Password . . . . . . . . . . . . Character value, *NONE
Naming convention . . . . . . . *SYS *SYS, *SQL, *SAA
Allow information from QRYDFN . *NO *NO, *YES, *ONLY

Set variables:
Variable name . . . . . . . .
Variable value . . . . . . . .
Using Query Manager in PDM
 Create a source file QQMQRYSRC
 (use length of 91)
 WRKMBRPDM on your QM Source
 Use F16 in PDM to go to User Defined Option
 Create these options
QC CRTQMQRY QMQRY(&L/&N) SRCFILE(&L/&F)
QR STRQMQRY QMQRY(&L/&N)

 Enter an SQL statement in a source member, then


use the QC PDM option to compile it, then QR to
execute it
Prompted QM Queries
 Gives you the ability to create an object that will
prompt you at run time to fill in the parameter
 Interactively you get prompted
 In a CL program you can set a variable in your
program
 Variables should -
 Be prefixed with a “&”
 Should be UPPER case
 Can replace any part of your SQL statement
SELECT DISTINCT &FIELD FROM &LIB/&FILE ORDER BY &ORDERBY
“Type a value for variable “FIELD" and press Enter. “
Prompted QM Queries -
Examples
Select * from ordhead where ordstat = ‘O’
and custid = &CUSTOMER
Select * from custmast
where cusnam like &CUSNAME
You need to include the quotes into the value
passed to Query Manager –

When prompted, you’d enter: ‘CPCU%’


Passing Parms from a CL
Program
 CL variables should be character variables
 You need to pass in any quotes around
character fields
 Sample CL statement
STRQMQRY QMQRY(TESTQRY) OUTPUT(*PRINT) +
SETVAR((FROM &FDATEA) (TO &TDATEA))
Passing Parms from a CL
Program
DCL VAR(&CDATE) TYPE(*CHAR) LEN(8)
RTVJOBA CYMDDATE(&JDATE)
CVTDAT DATE(&JDATE) TOVAR(&CDATE) FROMFMT(*CYMD) +
TOFMT(*YYMD) TOSEP(*NONE)
STRQMQRY QMQRY(SHPPRT) OUTPUT(*OUTFILE) +
OUTFILE(SHIPPF) SETVAR((FDATES &CDATE) +
(TDATES &CDATE))

 SDATE is a numeric field so no quotes are necessary


SELECT SUM(SHPQTY)
FROM SHPHST
WHERE SDATE BETWEEN &FDATES AND &TDATES
Passing Parms from a CL
Program
CL Program – character field in the SQL 4 single
PGM quotes
DCL VAR(&LIB10) TYPE(*CHAR) LEN(10) +
VALUE('1234567890')
DCL VAR(&LIB12) TYPE(*CHAR) LEN(12)
CHGVAR VAR(&LIB12) VALUE(''' *TCAT &LIB10 *TCAT ''')
STRQMQRY QMQRY(TESTQM) SETVAR((FROMLIB &LIB12))

Query Manager source


select * from dspobjdpf where odlbnm = &FROMLIB
Retrieving Source from a
Query/400 query
 The Retrieve QM Query command will retrieve the
SQL source from a Query/400 object
 Puts the SQL used in a Query/400 query into a
source file so you can then view it, modify it, and
create a QM Query from it.
 Can also use this to replace a static Query with a
prompted Query Manager query
Retrieve QM Query Command
Retrieve Query Mgmt Query (RTVQMQRY)

Type choices, press Enter.

Query management query . . . . . Name

Library . . . . . . . . . . . *LIBL Name, *LIBL, *CURLIB

Source file . . . . . . . . . . Name

Library . . . . . . . . . . . *LIBL Name, *LIBL, *CURLIB

Source member . . . . . . . . . *QMQRY Name, *QMQRY

Allow information from QRYDFN . *NO *NO, *YES, *ONLY


How to find Queries that use a
file
             PGM        PARM(&LIBRARY &FILE)                            
             DCL        VAR(&LIBRARY) TYPE(*CHAR) LEN(10)              
             DCL        VAR(&FILE) TYPE(*CHAR) LEN(10)                  
             DCLF       FILE(QTEMP/QRYOBJS)                            
             DLTF       FILE(QTEMP/QRYOBJS)                            
             MONMSG     CPF0000                                        

             DLTF       FILE(QTEMP/&LIBRARY)                            
             MONMSG     CPF0000                                        

CRTSRCPF   FILE(QTEMP/&LIBRARY)                            
  DSPOBJD    OBJ(&LIBRARY/*ALL) OBJTYPE(*QRYDFN) +          
                          DETAIL(*FULL) OUTPUT(*OUTFILE) +      
                           OUTFILE(QTEMP/QRYOBJS)                        
  BEGIN:   RCVF      /* GET QUERY NAME AND LIBRARY NAME */            
                 /* IF END OF FILE REACHED, EXIT LOOP        */
            MONMSG   CPF0864  EXEC(GOTO EOF)                          

RTVQMQRY   QMQRY(&ODLBNM/&ODOBNM) +                        
                       SRCFILE(QTEMP/&LIBRARY) ALWQRYDFN(*ONLY)      
         GOTO       CMDLBL(BEGIN)                                  

EOF:     FNDSTRPDM  STRING(&FILE) FILE(QTEMP/&LIBRARY)


MBR(*ALL)  OPTION(*NONE)
PRTMBRLIST(*YES)                
            ENDPGM    
SQL Syntax Tips
Join methods – Inner Join
Inner – selects only records where a match exists in
both tables

Select a.dept, b.depnam


from slshdr a inner join depmst b
on a.dept = b.dept

Select a.dept, b.depnam


from slshdr a,
depmst b
where a.dept = b.dept
Join Methods – Outer Join
Outer – selects all records from primary table
even if a match does not exist in the
secondary table.

Select a.dept, b.depnam


from slshdr a left outer join depmst b
on a.dept = b.dept
Join Methods – 3+ tables
Joining with three tables

Select * From slshdr a


inner join slsdet c
on a.ordnum = c.ordnum
inner join depmst b
on a.dept = b.dept
Join Methods – Exception Join
Find Orders with a department not in the department
master
select * from slshdr a
exception join depmst b
on a.dept = b.dept

select * from slshdr a


where a.dept not in
(select dept from depmst)
Joining a file to itself
select * from empmst a
inner join join empmst b
on a.supervisorid = b.empid
The UNION Statement
 This takes the results of two SQL statements and
combines the output, in effect merging the two result
sets
 Details and totals
 Merge active table and history table
 The fields in both statements must have the same
attributes (char, decimal, etc.)
 Field names do NOT need to be the same
 Must have the same number of fields in both
statements
The Union Statement -
Examples
 Using UNION to get details and totals

Select ‘Order’ as rowtype, order, price, extprice


From custord
Union
Select ‘Total’ as rowtype, 0 as order, 0, sum(extprice)
From custord
Order by rowtype, order

 Using Union to merge two tables


 Field names will be from the first table referenced
Select ordnum, cust, ordamt from orders
Union
Select ordnum, cust, totamt from orderhist
Alter Table
 Use Alter Table to add, delete or change a column
in a table.
 You do NOT need to manually save the data, it does
it for you
 Example:
Alter Table Customer
Add contact char(30)
 If you add a column with “Not Null”, you must specify
a default value
Alter Table
 If you Drop a column you receive a CPF
warning that you must answer
Change of file TEST1 may cause data to be lost. (C I)
 This can be a problem if you’re running the
Alter Table in a client server application,
through ODBC for example.
Alter Table – Drop Field
 You need to manipulate the system reply list to answer
that message

call qsys.qcmdexc( 'ADDRPYLE SEQNBR(3333) MSGID(CPA32B2)


RPY(''I'')',0000000045.00000)
CALL QSYS.QCMDEXC('QSYS/CHGJOB
INQMSGRPY(*SYSRPYL)',0000000031.00000)
Alter table x drop column A
call qsys.qcmdexc( 'RMVRPYLE SEQNBR(3333)',0000000021.00000)
CALL QSYS.QCMDEXC('QSYS/CHGJOB
INQMSGRPY(*DFT)',0000000027.00000)
Using a Select in a FROM
Clause
 Where you would normally use a Table name
in a From clause – you can use a full
SELECT statement
 Useful to avoid having to create a View
 Useful if you are using embedded SQL and
the want to include host variables in the
FROM
Using a Select in a FROM
Clause - Example
 Basic Style
SELECT ordnum, ordval
FROM ordhdr a inner join
(select ordnum, qty*price as ordval from orddtl) b  
on a.ordnum = b.ordnum

 In an RPG program
SELECT ordnum, ordval
FROM ordhdr a inner join
(select ordnum, qty*price as ordval from orddtl
Where status = :linsts) b  
on a.ordnum = b.ordnum

 Get a count of distinct values in a table


Select count(*) from
(select distinct custnum from orders) a
Join fields with different
attributes
 Creating a Join LF using DDS or doing a join
in Query/400, the join fields must have the
same attributes
 Not a requirement using SQL
 Fields can be converted on the fly using built
in functions
Join fields with different
attributes - Example
If ordnum is decimal in slshdr and ordnum is character
is slsdet then you CAN’T join them in Query/400
Use a built in function to convert one of the values

select *
from slshdr a left outer join slsdet b
on a.ordnum = decimal(b.ordnum,6,0)
Subqueries
 Subqueries are queries nested inside other
queries, marked off with parentheses.
 Most often, you see subqueries in WHERE or
HAVING clauses.
 Subqueries only need to be evaluated once
Subqueries – Examples
select ordnum, price
from slsdet
where price > (select avg(price)
from slsdet)
order by price
Correlated Subqueries
 Just like a regular subquery with some
additional features
 You can refer to a field from the outer SQL
statement within it
 Will need to be evaluated for each row
returned
 Can be performance issues
Correlated Subqueries –
Examples
Select ordnum, price,
From slsdet a
Where price >
(select avg(price) from slsdet b where
a.cust = b.cust)
COALESCE
 The COALESCE function returns the value of the first non-null
expression.
 Useful anytime you have the possibility of having a null value
SELECT DEPTNO, DEPTNAME, COALESCE(MGRNO, ’ABSENT’), ADMRDEPT
FROM DEPARTMENT
Updating Data from Another
Table
 Join logical files cannot be updated
 SQL statements using a join cannot be
updated
 You can however, update a value from one
table to another
Updating Data from Another
Table – Example
update slshdr
set dept = (select dept from depmst where
depnam = 'IT')
where dept = 40

update slshdr a
set ordervalue =
(select coalesce(sum(price * orderquantity),0)
from slsdet b
where char(a.ordnum) = b.ordnum)
Updating Multiple Fields with Data
from Another Table – Example
update myfile a
set (field1, field2)=
(select b.field1, b.field2
from file2 b
where a.key1=b.key1 and
a.key2=b.key2)
where exists (select c.key1
from file2 c
where a.key1=c.key1 and
a.key2=c.key2)
Char vs. Digits
 CHAR strips leading zeros, DIGITS doesn't.
 If you have a NUMERIC(5,0) number equal to
00044:
 CHAR would give '44',
 DIGITS would give '00044'.
Case insensitive searching
 Built-in functions – UPPER and UCASE
Select * from custmst
Where upper(cusname) = upper(:fieldx)
 LOWER and LCASE perform comparable function

 Will prevent optimizer from using any indexes unless the


index was created with TRNTBL, ALTSEQ or *LANGIDSHR
on the index create. There are examples of using indexes with
translate table in the Database Performance and Query
Optimization book.
Using CASE
 Works similar to an RPG CASE
 If there is no ELSE clause – you can get a NULL
result
 Evaluated in the order listed

SELECT EMPNO, FIRSTNME, MIDINIT, LASTNAME,


CASE
WHEN EDLEVEL < 15 THEN ’SECONDARY’
WHEN EDLEVEL < 19 THEN ’COLLEGE’
ELSE ’POST GRADUATE’
END
FROM EMPLOYEE
Using the CASE expression
 Select orddate,
       Case Substr( Digits( orderdate ), 1, 2 )
         When '01' Then 'JAN'
         When '02' Then 'FEB'
         When '03' Then 'MAR'
         When '04' Then 'APR'
         When '05' Then 'MAY'
         When '06' Then 'JUN'
         When '07' Then 'JUL'
         When '08' Then 'AUG'
         When '09' Then 'SEP'
         When '10' Then 'OCT'
         When '11' Then 'NOV'
         When '12' Then 'DEC'
         Else ‘xxx'
       End
  From slshdr
More uses for CASE
 Useful to prevent Divide by 0 errors

Select ordnum, price,


Case
when cost = 0 then 0
Else price/cost
End as markup
From slsdet
Creating tables with Long Field
Names
 First use the CREATE TABLE statement to specify
the long name (customer_master)
CREATE TABLE customer_master (customer_name FOR COLUMN cusnam CHAR (20),
customer_city FOR COLUMN cuscty CHAR(40))
 The iSeries file name (not the table) is now
CUSTO00001

 Then RENAME TABLE statement to also assign a


short name (cusmst) for the non-SQL interfaces:
RENAME TABLE customer_master TO SYSTEM NAME cusmst
Using multi-member files

 If you want to identify a particular member within a


file, use the following SQL command:
CREATE ALIAS your-alias FOR your-library/your-file (your-
member)

 Then use standard SQL referencing the alias


SELECT * FROM your-alias.
Retrieve top n number of
records
 Syntax only works on V5R1 and up

select *
from myLib/myFile
fetch first 10 rows only
Accessing a remote database
with Connect
 Can be useful if you have SQL Developers kit on one iSeries
but not another one that you want to query
 Need to ADDRDBDIRE – Relational Database Directory
Entry first
 Then use CONNECT in SQL to connect to a remote SQL
database.
 Additional program products can give you the ability to
connect to Oracle, SQL Server, etc

C/exec sql
C+ connect to :host user :orauser using :pwd
C/end-exec
Date Arithmetic
 A couple of steps – but all done in one SQL
statement
 Convert numeric fields to character
 Format the character field to look like a DATE
field
 Use the DATE function to make it a DATE data
type
 Perform your Date arithmetic
Date Arithmetic
 Add to a date
select order_date,
date('20' || substr(digits(order_date),1,2) || '-' ||
substr(digits(order_date),3,2) || '-' ||
substr(digits(order_date),5,2)
) + 20 days ORDER_DATE Date expression
from slsdet
40,415 05/05/04

select order_date,
date('20' || substr(digits(order_date),1,2) || '-' ||
substr(digits(order_date),3,2) || '-' ||
substr(digits(order_date),5,2)
) + 1 month
from slsdet
ORDER_DATE Date expression
40,415 05/15/04
Date Arithmetic
 Subtract two Numeric Date fields – numeric in
database, but contain dates
select order_date, ship_date,
Days(date('20' || substr(digits(ship_date),1,2) || '-' ||
substr(digits(ship_date),3,2) || '-' ||
substr(digits(ship_date),5,2)
)) -
Days(date('20' || substr(digits(order_date),1,2) || '-' ||
substr(digits(order_date),3,2) || '-' ||
substr(digits(order_date),5,2)
ORDER_DATE SHIP_DATE Numeric
)) expression
from slsdet 40,415 40,516 31

 Result is in Days – 31 days


Date Arithmetic
 Subtract two Numeric Date fields – numeric in
database, but contain dates
select order_date, ship_date,
date('20' || substr(digits(ship_date),1,2) || '-' ||
substr(digits(ship_date),3,2) || '-' ||
substr(digits(ship_date),5,2)
) -
date('20' || substr(digits(order_date),1,2) || '-' ||
substr(digits(order_date),3,2) || '-' ||
substr(digits(order_date),5,2)
ORDER_DATE SHIP_DATE Numeric
) expression
from slsdet 40,415 40,516 101

 Result is in Months and Days – 1 month and 1 day


Date Arithmetic – Selecting
Dates
 Odcdat = 6 position character field containing a date – YYMMDD

SELECT date(substr(odcdat,3,2) || '/' ||


substr(odcdat,5,2) || '/20'||
substr(odcdat,1,2))
FROM dspobjpf
where odcdat <> ' '
and date(substr(odcdat,3,2) || '/' ||
substr(odcdat,5,2) || '/20'||
substr(odcdat,1,2)) < current date - 25 days
Embedded SQL
SQLCA Data Structure
 When a SQL statement fails, it will not generate a CPF error.
 An SQLCA is a set of variables that is updated at the end of
the execution of every SQL statement.
 Brought in automatically by the compiler (when type =
SQLRPGxx)
 DS updated when a SQL statement completes – successfully
or unsuccessfully
 Generally – you should check SQLSTT after every SQL
statement
 Key fields for our discussion are:
 SQLCOD = result code
 SQLSTT = result state -– message number
 SQLER3 = Number of rows modified by the statement
 Complete details in the SQL Reference book – Appendix B
Compile Options
 Commitment Control option
 Only affects SQL commands, not RPG Updates, etc.

 If it’s not *NONE – then your tables had better be


journalled
 Precompiler options to specify the naming convention
used
 If you are connecting to non-iSeries databases, you must
use SQL naming
The Basics of Reading via SQL
 Use an external data structure to define the fields in the
RPG
 Not required – but it can simplify defining the fields you are going
to read
 Declare the cursor – must be prior to the Open in the
source
 Open the cursor
 Fetch the next record and load data from the cursor into
RPG variables
 Repeat that Fetch until End of File is found, indicated by
SQLSTT = ‘02000’
 Close the Cursor
The Basics of Reading via SQL
*********************************************** C/exec sql
*************** C+ fetch c1 into :odobnm, :odlbnm
* Sample program to read selected fields from file via SQL
C/end-exec
***********************************************
*************** C dou sqlstt <> '00000'

* This defines all the fields in the record * Do something with each record
D InRec e ds extname(pfdata)

C/exec sql C/exec sql


C+ declare c1 cursor for C+ fetch c1 into :odobnm, :odlbnm
C+ select odobnm, odlbnm from pfdata C/end-exec
C/end-exec
C enddo
C/exec sql C
C+ open c1
C/end-exec C/exec sql
C+ close c1
C/end-exec

C move *on *inlr


Update using a Variable
 Only slightly more complex than reading
 Uses an RPG program variable in the SQL statement
 Variables are listed in the RPG as :varname
Update using a Variable
**************************************************************
* Sample program to run a single SQL statement using
* static SQL with a parameter
************************************************************** Sample program call:
D Parm1 s 6a
D Parm2 s 10a CALL PGM(SQL2) PARM(‘TEST' 'MYLIB')
C *entry plist
C parm parm1
C parm parm2

C/exec sql
C+ update pfdata set ODDDAT = :parm1
C+ where ODLBNM = :parm2
C/end-exec

/free
if sqlstt <> '00000';
// handle the error condition

else;
// completed normally - show how many records were updated
dsply sqler3;
endif;

*inlr = *on;
/end-free
Execute Immediate
 Useful when you need to build your SQL
statement on the fly based on variables or
parameters
 Need to include any quotes, etc – the SQL
needs to be syntactically correct
 You should be able to paste it into an
Interactive SQL session and execute it
 Get your quotes correct!
Execute Immediate
**************************************************************
* Sample program to run a single SQL statement using an
* EXECUTE IMMEDIATE after building the SQL on the fly
**************************************************************

***** call sql3 '''abc'''

DParm1 s 10a
DSQLStmt s 100a inz('Update pfdata set -
D odddat = ')

C *entry plist
C parm parm1

/free
// Build the SQL statement to be executed
SQLStmt = %trim(SQLStmt) + parm1;
/end-free

C/exec sql
C+ execute immediate :SQLStmt
C/end-exec

/free
if sqlstt <> '00000';
// handle the error condition
else;
// completed normally - show how many records were updated
dsply sqler3;
endif;

*inlr = *on;
/end-free
RPG Tips
 When using SQL with LIKE and RPG variables make
sure you use %TRIM
 RPG variables will be blank padded
 Use a capital C for the Spec and you can press F4 to
prompt it – won’t prompt with a lower case C but it will
compile
 COMMIT compile option – default value is *CHG
 Set that value through SQL in the code each time it executes
c/exec sql
+ Set Option DatFmt=*ISO, Commit=*None, CloSQLCsr=*EndMod
c/end-exec
Using a Host Indicator to flag
Null values or data errors
 The format is -> :host-identifier INDICATOR :host-indicator
 Can be used in SELECT INTO, FETCH, SET variable, VALUES INTO
 The indicator can specify the following:
 Null value was returned – indicator = -1
 Null value due to data mapping error – indicator = -2
 Characters could not be converted
 Numeric conversion error (underflow or overflow)
 Arithmetic expression error (division by 0)
 Date or timestamp conversion error (a date or timestamp that is not within the valid range of
the dates for the specified format)
 String representation of the datetime value is not valid
 Mixed data not properly formed
 A numeric value that is not valid
 Argument of SUBSTR scalar function is out of range
 Original length of a truncated string
 Record the seconds portion of a time if the time is truncated on assignment to a host
variable.
Using a Host Indicator to flag
Null values or data errors
D MaxSize s 15s 0
D AvgSize s 15s 0
D MaxTxt s 30a
D DivVal s 15s 0
D Ind_avg s 5I 0
D Ind_max s 5I 0
D Ind_txt s 5I 0
D Ind_div s 5I 0

C/exec sql
C+ select avg(odobsz), max(odobsz), max(odobtx), 9/0
C+ into :avgsize :Ind_avg, :maxsize :Ind_Max,
C+ :maxtxt :Ind_txt, :divval :ind_div
C+ from pfdata
C/end-exec
Stored Procedures
and
User Defined Functions
What is a Stored Procedure?
 SQL’s version of a program call
 The application waits for the Proc to complete before continuing
 Parameters can be passed back and forth
 Can be executed on a local or remote system
Benefits of Stored Procs
 Very useful in a distributed application – the Stored Proc can be called once
from the application and perform multiple updates
 Network traffic can be reduced – multiple rows read on the server and only
one summary returned to the client
 Can be used to control access to secured objects
 Users can access the procedure but not the database table it is based on
 Have the ability to re-use your existing programs within new SQL
applications
 Can return a result set to a client application
 Can’t return a result set to an RPG program
Two Types of Stored
Procedures
 SQL
 Written using SPL (SQL Procedure Language)

 External
 Written in any iSeries programming language including Java
External Stored Procedures
 Written in any iSeries programming language
 RPG, COBOL, etc.
 CREATE PROCEDURE is run in SQL to identify the *PGM object to the
database
 An External Procedure does not have to contain any SQL statements. It
can be any existing program on your system.
 A good choice if you have complex processing you want to perform or want
to re-use existing code like customer pricing routines or a sales tax routine
 Must be a *PGM object
 No display – must be a batch pgm
 Compile w/activation group (*CALLER)
External Stored Procedure
Example
 Start with an RPG program that accepts parameters and returns a parameter
 The program is compiled as PRCTST

Dadd1n s 3p 0
Dadd2n s 3p 0
Dresult s 6p 0

C *entry plist
C parm add1n
C parm add2n
C parm result

C*
/free
result = add1n + add2n;
return;
/end-free
Create the Stored Procedure
 Use iSeries Navigator to create it (easiest way)
 Or enter the source in a source file and then execute the
source file using RUNSQLSTM
 Or execute this command through any other SQL
interface Input and Output
Parameters
 Interactive, Query Manager, ODBC, etc.

CREATE PROCEDURE PRCTEST ( Actual name of the


IN ADD1 DECIMAL(3, 0) , program being run
IN ADD2 DECIMAL(3, 0) ,
OUT "RESULT" DECIMAL(6, 0) )
LANGUAGE RPGLE
SPECIFIC PRCTEST
EXTERNAL NAME 'PRCTST'
PARAMETER STYLE GENERAL ;
Execute the Stored Procedure
 Execute the stored procedure from within an RPG program using
Embedded SQL

d result s 6p 0
d a s 3a
d b s 3a
d an s 3p 0
d bn s 3p 0
C *entry plist
C parm a
C parm b

c move a an
c move b bn
C/exec sql
C+ CALL PRCTEST (:an, :bn, :Result)
C/end-exec
c dsply result
c move *on *inlr
SQL Stored Procedures
 Written in SQL Language
 Very similar to SQL Language in other databases which could make it
easier to port
 Code is converted to an ILE C program with embedded SQL

 Requirements
 Must be on V4R2 or higher
 V4R2 to V4R5
 Must have SQL Development Kit for iSeries
 Must have the ILE C Compiler
 V5R1 and higher
 Must have SQL Development Kit for iSeries on the development machine
Creating a Procedure with
Navigator
Debugging with V5R2
 Use the Debug View option in the proc
Set Option DbgView =*Source
 You can also use this option on the RUNSQLSTM command

 Debugging can only be done within the job that created the procedure because the
view is stored in QTEMP
 PTF will cause the view to be stored permanently
 V5R1 -- SI06814
 V5R2 -- SI06652

 To view the returned result, prior to V5R2 you would have to create a program to
call your proc and debug that program

 In V5R2 you can call a proc from the Run SQL Scripts window in Navigator and the
output parms are displayed on the Messages tab.

 PTF’s available for V5R1 SIO6359, SIO6310, SIO6358


Sample from a SQL Procedure
DECLARE CONTINUE HANDLER FOR record_not_found
Begin
SET at_end = 1;
End;

SET TOTCOUNT = 0 ;
SET at_end = 0;
OPEN C2 ;
WHILE AT_END = 0 DO
FETCH C2 INTO LIBNAME , LIBCOUNT ;
SET TOTCOUNT = TOTCOUNT + LIBCOUNT ;
END WHILE ;

CLOSE C2 ;
END ;
Use of QCMD/QCMDEXC
 QCMD and QCMDEXC are already registered as a stored procedure
 QCMD
 Will invoke a command line
 When you’re in Interactive SQL and need to run a command, execute
the following SQL:
CALL QCMD
 QCMDEXC
 Will run any CL command passed to it

CALL QSYS/QCMDEXC ('CHGQRYA QRYTIMLMT(120)',


0000000022.00000)
CALL QSYS.QCMDEXC('chgcurlib curlib(fred)',0000000023.00000);
User Defined Functions
 Similar to Stored Procedures, but a function
will return a value
 Can be written in SQL or External (RPG, etc.)
 Easy to create using Navigator
 Drill down to a library
 Right click – New, Function, SQL
 Can also be created in SEU – in a source file
and then executed in SQL
User Defined Functions
User Defined Functions
User Defined Functions
User Defined Functions
 Simplified SQL statement
select a.*, subnumericdates(orddat, shpdat)
from slsdet a

 Reusable
Select subnumericdates(invoice_date, payment_date)
From ardata
 Details on SQL Language syntax in Stored
Procedures, Triggers and User Defined Functions
on DB2 Universal Database for iSeries
Performance
Performance
 Run SQL under debug and review joblog for
messages regarding suggested indexes to
build.
 Joblog will contain lots of informative
messages including Access Paths that need
to be created and suggestions for you to
create permanent access paths to speed up
processing.
Starting Debug from a Client
Application
 Start Debug on the iSeries job from a VB application to generate logging info
Use the same philosophy to run an iSeries command from a client application
 To Start Debug:
set CON1 = server.createobject("ADODB.Connection")
set CMD1 = server.createobject("ADODB.Command")
/*open the connection */
CON1.Open "DSN=MYAS400;UID=myuser;PWD=mypwd;"
cmd1.activeconnection = con1
cmd1.commandtext = "call qsys.qcmdexc('strdbg updprod(*yes)',0000000020.00000)"
cmd1.execute
/* the above starts debug on the as/400 by calling qcmdexc api to execute cmd strdbg*/

Dim cn1 As New IBM.Data.DB2.iSeries.iDB2Connection


cn1.ConnectionString = "DATASOURCE=myas400; UID=myuser;PWD=mypwd"
Dim cmd As New IBM.Data.DB2.iSeries.iDB2Command
cmd.Connection = cn
cmd.CommandText = "call qsys.qcmdexc('strdbg updprod(*yes)',0000000020.00000)"
cn.Open()
cmd.ExecuteNonQuery()
cn.Close()
 To End Debug
cmd1.commandtext = "call qsys.qcmdexc('enddbg',0000000006.00000)“
cmd1.Execute
 Standard debug messages are logged in the QZDASOINIT job running in the QSERVER subsystem
Sample Joblog Messages
call sqltest '123'
PREPARE of statement SQLSTRING completed.
ODP reused.
ODP not deleted.
Row not found for UPDATE.
DSPLY No records found for TSTLIB1
ODP reused.
ODP not deleted.
Row not found for UPDATE.
DSPLY No records found for TSTLIB2
ODP reused.
ODP not deleted.
Row not found for UPDATE.
DSPLY No records found for TSTLIB3
Sample Joblog Messages
All access paths were considered for file FILEA.
Additional access path reason codes were used.
All access paths were considered for file FILEB.
Additional access path reason codes were used.
Access path built for file FILEB.
File FILEA processed in join position 1.
Access path suggestion for file FILEA.
File FILEB processed in join position 2.
Sample Joblog Messages
Additional Message Information

Message ID . . . . . . : CPI432F
Date sent . . . . . . : 11/12/03 Time sent . . . . . . : 10:27:27

Message . . . . : Access path suggestion for file FILEA.

Cause . . . . . : To improve performance the query optimizer is suggesting a


permanent access path be built with the key fields it is recommending. The
access path will access records from member FILEA of file FILEA in
library TSTLIB.
In the list of key fields that follow, the query optimizer is recommending
the first 1 key fields as primary key fields. The remaining key fields are
considered secondary key fields and are listed in order of expected
selectivity based on this query. Primary key fields are fields that
significantly reduce the number of keys selected based on the corresponding
selection predicate. Secondary key fields are fields that may or may not
significantly reduce the number of keys selected. It is up to the user to
Resources
 www.as400pro.com
 www.iseriesnetwork.com – subscription required for articles, forums are free
 IBM iSeries DB2 manuals
 http://publib.boulder.ibm.com/iseries/v5r2/ic2924/index.htm?info/rzahf/rzahfli0.htm
 Preparing for and Tuning the V5R2 SQL Query Engine on DB2 Universal Database
for iSeries Redbook
 http://publib-b.boulder.ibm.com/Redbooks.nsf/RedbookAbstracts/sg246598.html?Open
 Stored Procedures, Triggers and User Defined Functions on DB2 Universal Database
for iSeries Redbook
 this is the best resource from IBM that I’ve found on Stored Procedures
 http://publib-b.boulder.ibm.com/Redbooks.nsf/0/93beee575837a66185256e390055723e?OpenDocument
 IBM iSeries Application Development Education
 Contains a number of free online courses on SQL performance
 http://www-1.ibm.com/servers/enable/site/education/ibo/curr_apdev.html?apdv
 DB2 for iSeries Tips
http://www-1.ibm.com/servers/eserver/iseries/db2/db2tips.htm
Thanks!!!!
Fred Gamache
fred@gamacheconsulting.com
www.gamacheconsulting.com

También podría gustarte