SQL Tips & Techniques: Fred Gamache
SQL Tips & Techniques: Fred Gamache
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.
Additional Parameters
Additional Parameters
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)
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)
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
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
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
* This defines all the fields in the record * Do something with each record
D InRec e ds extname(pfdata)
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
**************************************************************
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.
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.
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
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*/
Message ID . . . . . . : CPI432F
Date sent . . . . . . : 11/12/03 Time sent . . . . . . : 10:27:27