SQL træning

DB/2 on steroids part II
Structured Query Language
SQL on IBMI
Version 1.3 - 14. April 2016
Copyright (C) 2016
System & Method A/S
By: Niels Liisberg
1
DB/2 on steroids part II
SQL on IBMi
Agenda:
Part I

NoSQL / mongoDB and JSON

BLOBS and XML
Part II

Column functions

Compound statements

Regular expression

User defined Table Functions

SQL Services

SQL Triggers

Common Table Expressions

Recursive SQL
2
DB/2 on steroids part II
Introduction:
SQL Documentation:
A good place to start:
http://www-01.ibm.com/support/knowledgecenter/ssw_ibm_i_72/db2/rbafzintro.htm?lang=en
3
DB/2 on steroids part II
Column functions (Scalar functions):
Scalar functions take form zero to N parameters, and returns on one value. They can be
cascaded, i.e. nested into each other, and they gets evaluated from the inside out... Just
like RPG or Java.
http://www-01.ibm.com/support/knowledgecenter/ssw_ibm_i_72/db2/rbafzscale.htm
E.x. CARDINALITY, DATABASE
SELECT
FROM
dayname(current date)
SYSIBM/SYSDUMMY1
SELECT
FROM
dayname(curdate())
SYSIBM/SYSDUMMY1
The same but with the new “values” – virtual row
Values (dayname(curdate()))
4
DB/2 on steroids part II
Works also in the ORDER BY and WHERE clause
SELECT desc
FROM product
ORDER BY position(upper(desc) , 'SØLV') DESC
Dirty trix!!
SELECT *
FROM product
WHERE rrn(product) = 123
5
DB/2 on steroids part II
CASE
Column expression CASE is just like in RPG SELECT / WHEN. Or a multi way IF .. ELSE
IF ELSE ...
SELECT
prodId
,
case
when stockdate < current date and stockCnt
> 0 then
'In stock'
when stockdate < current date + 10 days then
'Arriving soon'
else
'Forget it'
end as lagerstatus, stockDate, stockCnt
FROM product
Can contain sub-selects...
SELECT
prodId,
case
when stockdate < current date and stockCnt
> 0 then
'In stock'
when stockdate < current date + 20 days then
'Arriving soon'
else
'Forget it, Maybe try with '
Concat (
Select prodid
from
product p2
where p2.prodkey = p1.prodkey+10
)
end as lagerstatus,
stockDate
FROM product p1
6
DB/2 on steroids part II
Please note: The product table is used multiple times – so the name is not unique. To fix
that we rename the local name p1 and p2
7
DB/2 on steroids part II
Common Table Expressions
What if – I would like to know how many products in each status?
SELECT
prodId
,
case
when stockdate < current date and stockCnt
> 0 then
'In stock'
when stockdate < current date + 10 days then
'Arriving soon'
else
'Forget it'
end as stockStatus,
stockDate,
stockCnt
FROM product
... Wups !! Can’t be done … Or .. maybe ..
WITH a as (
SELECT
prodId
,
case
when stockdate < current date and stockCnt
> 0 then
'In stock'
when stockdate < current date + 10 days then
'Arriving soon'
else
'Forget it'
end as stockStatus,
stockDate
FROM product
)
SELECT
stockStatus , count(*) as products
FROM
a
GROUP BY stockStatus
Which manufacturer does produce goods we can sell – i.e. items in stock?
8
DB/2 on steroids part II
WITH a as (
SELECT
manuid
,
case
when stockdate < current date and stockCnt
> 0 then
'In stock'
when stockdate < current date + 10 days then
'Arriving soon'
else
'Forget it'
end as stockStatus
FROM product )
, b as (
select manuid from a where stockStatus = 'In stock'
)
Select * from manufact where manuid in (select manuid from b)
Aoutch – that was nasty! Can you store a common table expression for later user?
Yes!! Use a view
9
DB/2 on steroids part II
CREATE OR REPLACE VIEW qtemp/prodstatus as (
SELECT PRODKEY, PRODID, DESC, MANUID, PRICE, STOCKCNT, STOCKDATE,
case
when stockdate < current date and stockCnt
> 0 then
'In stock'
when stockdate < current date + 10 days then
'Arriving soon'
else
'Forget it'
end as lagerstatus
FROM product
)
And then see which manufacturer has goods to sell
SELECT *
FROM
manufact
WHERE
manuid in (
SELECT manuid
FROM
prodstatus
WHERE
lagerstatus = 'In stock'
)
10
DB/2 on steroids part II
Regular expressions
Matching a pattern using LIKE – does not look cool and performs bad  ….
SELECT str
FROM
strTable WHERE (
str LIKE '% = 0;%' OR str
str LIKE '% = 3;%' OR str
str LIKE '% = 7;%' OR str
str LIKE '% = 9;%' OR str
LIKE
LIKE
LIKE
LIKE
'%
'%
'%
'%
=
=
=
=
1;%' OR
4;%' OR
7;%' OR
0x00;%'
str LIKE '% = 2;%' OR
str LIKE '% = 5;%' OR
str LIKE '% = 8;%' OR
)
This is where regex kicks in:
DB/2 implements the following:



REGEXP_REPLACE
REGEXP_COUNT
REGEXP_LIKE
http://www.ibm.com/support/knowledgecenter/api/content/nl/en-us/ssw_ibm_i_72/db2/rbafzregexp_like.htm rbafzregexp_like__regexp_likecontrol
11
DB/2 on steroids part II
String replacement
SELECT REGEXP_REPLACE(
'Livet er en gave',
'gave','stor gave',1, 0,'c')
FROM sysibm.sysdummy1
Count words
SELECT REGEXP_COUNT(
'livet er en gave',
'[^ ]+' )
FROM sysibm.sysdummy1
SELECT REGEXP_COUNT(emNote,'[^ ]+' ) , emNote
FROM employee
Find rows with rep match ( or no match)
Select *
from employee
where not REGEXP_LIKE(ememail ,
'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]'
)
12
DB/2 on steroids part II
Is “no email address ok” ? the add:
'(^\s*$)|(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.])'
13
DB/2 on steroids part II
Compound statements
Compound statements are procedural code in SQL (aka PL/SQL). It can be used directly
from source files. Can be used in triggers and is the backbone of “stored procedures”
http://www-01.ibm.com/support/knowledgecenter/ssw_ibm_i_72/db2/rbafzcompounddynstmt.htm?lang=en
Every SQL statements can be executed from within a compound statement.
http://www-01.ibm.com/support/knowledgecenter/ssw_ibm_i_72/db2/rbafzstates.htm?lang=en
The program flow is controlled by - “SQL control statements”:
CALL
CASE
compound-statement
FOR
GET DIAGNOSTICS
GOTO
IF
ITERATE
LEAVE
LOOP
PIPE
REPEAT
RESIGNAL
RETURN
SIGNAL
WHILE
14
DB/2 on steroids part II
This actually looks more fun in a plain 5250
begin
declare id int;
declare p decimal (15, 2);
select prodkey, price
into id, p
from product
where prodkey = 10;
if (dayname(curdate()) = 'Mandag') then
set p = p * 1.01;
update product
set price = p
where prodkey = id;
end if;
end
More convenient:
put it in a source file i.e. DEMO00DB/QSQL
run it with RUNSQLSTM
15
DB/2 on steroids part II
Cursors
A cursor is a “index finger” into the current row in a result set. Just like SETLL would set a
“index finger” in RPG.
A cursor gives you the liberty to handle each row in the result set in a procedural manner –
just like I RPG.
begin
declare EOF int default 0;
declare k
int;
declare m
char(16);
declare d
varchar(128);
declare c1 cursor for
select prodkey, manuid , desc
from
product;
declare continue handler for not found begin
set eof = 1;
end;
open c1;
fetch c1 into k , m, d;
while eof = 0 do
if position (d , 'silver') > 0 then
update product
set
stockCnt = 27
where prodkey = k;
end if;
fetch c1 into k , m, d;
end while;
close c1 ;
end
16
DB/2 on steroids part II
User defined Table Functions
A UDTF (aka. Stored procedure) is either a SQL compound statement or native code
written in JAVA, RPG, C, COBOL or REXX
It can be scalar ( it returns one value only) or it can return a table of rows.
As an example: You can encapsulate a price calculation algorithm and the reuse it from
everywhere – Also from Query/400
17
DB/2 on steroids part II
A scalar function – price calculation:
DKSRV131 /DEMO00DB/QSQL/ CRTPRICECA
create or replace function demo00db/PriceCalc
(
prodKeyP decimal (11, 0),
curency varchar(3) ,
units decimal (11, 0)
)
returns decimal(11, 2)
language sql
reads
sql data
begin
declare newPrice decimal (11, 2);
declare oldPrice decimal (11, 2);
declare cur char(3);
declare c1 cursor for
select price
from
product
where prodKey = ProdKeyP;
declare exit handler for not found begin
return 0 ;
end;
open c1;
fetch c1 into oldPrice;
set newPrice = case
when Curency = 'DKK' then units * oldprice / 1.0
when Curency = 'EUR' then units * oldprice / 7.35
else 0
end;
close c1 ;
return newPrice ;
end;
18
DB/2 on steroids part II
19
DB/2 on steroids part II
And use them as any other scalar function:
select
price,
priceCalc(prodKey, 'DKK' , 10 ) as "DKK",
priceCalc(prodKey, 'EUR' , 10 ) as "EUR",
product.*
from product
20
DB/2 on steroids part II
A user defined table function:
CREATE or REPLACE FUNCTION qgpl/DspObjOwn (ParUser VarChar(10))
RETURNS TABLE (UsrPrf
Char(10)
,
Obj
Char(10)
,
ObjLib
Char(10)
,
ObjType
Char(8)
,
AutHldr
Char(10)
,
System
Char(8)
,
ObjASP
Char(10)
,
DspTime
Timestamp
)
LANGUAGE SQL
MODIFIES SQL DATA
BEGIN
DECLARE CLCmd VarChar(256) Not NULL Default '';
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
BEGIN
DECLARE ERROR_HIT INTEGER;
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
SET ERROR_HIT = 1;
CREATE TABLE QTEMP/TMPOBJOWN (
OODDAT CHAR(6), OOUSR CHAR(10), OOOBJ CHAR(10),
OOLIB CHAR(10), OOTYPE CHAR(8), OOAHLR CHAR(1),
OOSYST CHAR(8), OODTIM CHAR(6), OOASPL CHAR(10));
END;
SET ParUser = CASE WHEN LENGTH(TRIM(ParUser)) = 0
THEN SESSION_USER
ELSE UPPER(TRIM(ParUser)) END;
SET CLCmd = 'DSPUSRPRF
Concat '
Concat '
Concat '
Concat '
USRPRF(' concat ParUser concat ') '
TYPE(*OBJOWN) '
OUTPUT(*OUTFILE) '
OUTFILE(QTEMP/TMPOBJOWN) '
OUTMBR(*FIRST *REPLACE) ';
CALL QCMDEXC(CLCmd, LENGTH(CLCmd));
RETURN SELECT OOUSR, OOOBJ, OOLIB, OOTYPE,
OOAHLR, OOSYST, OOASPL,
TIMESTAMP('20' concat OODDAT concat OODTIM)
FROM QTEMP/TMPOBJOWN;
END
21
DB/2 on steroids part II
Use the UDTF with the “table” keyword – but elsewise, it's a normal table.
Select *
from table (qgpl/dspObjOwn ('QPGMR') ) myname
where objlib in ('QGPL', 'QUSRSYS')
22
DB/2 on steroids part II
SQL Services
Se the full list here:
https://www.ibm.com/developerworks/community/wikis/home?lang=en#!/wiki/IBM%20i%20
Technology%20Updates/page/DB2%20for%20i%20-%20Services
SELECT *
FROM
QSYS2/liblist;
SELECT *
FROM
QSYS2/schemata;
SELECT *
FROM
QSYS2/sysDisks;
SELECT *
FROM
QSYS2/user_info;
SELECT *
FROM
QSYS2/user_stg;
Select *
from
QSYS2/SYSTEM_VALUE_INFO;
-- this is best from 5250
CALL QSYS2/QCMDEXC('wrksplf');
23
DB/2 on steroids part II
SQL Triggers
After a update, insert or delete I/O you are able to catch the row – before and/or after and
manipulate the row, or even run other SQL statements om other tables or even call
programs
CREATE OR REPLACE TRIGGER WSHPLGETIN
AFTER INSERT ON WSHPLGET00
REFERENCING NEW AS NROW
FOR EACH ROW MODE DB2SQL
BEGIN ATOMIC
INSERT INTO WSHPAGET00_GRID_PORTFOLIO_MATERIALIZED (
SELECT *
FROM WSHPAGET00_GRID_PORTFOLIO
WHERE PAGPAGTKN = NROW.PLGPAGTKN);
END;
24
DB/2 on steroids part II
SET SCHEMA = BASDEVDB;
CREATE TABLE WSHPAGET00_GRID_PORTFOLIO_MATERIALIZED AS
(SELECT * FROM WSHPAGET00_GRID_PORTFOLIO)
WITH DATA;
ALTER TABLE WSHPAGET00_GRID_PORTFOLIO_MATERIALIZED ADD
PRIMARY KEY (PAGPAGTKN);
CREATE TRIGGER WSHPLGETUP
AFTER UPDATE ON WSHPLGET00
REFERENCING NEW AS NROW
FOR EACH ROW MODE DB2SQL
BEGIN ATOMIC
UPDATE WSHPAGET00_GRID_PORTFOLIO_MATERIALIZED
SET ROW = (
SELECT *
FROM WSHPAGET00_GRID_PORTFOLIO
WHERE PAGPAGTKN = NROW.PLGPAGTKN
)
WHERE PAGPAGTKN = NROW.PLGPAGTKN;
UPDATE WSHPLGET00
SET PLGSEOURL = JSONGETSTR(NROW.PLGPROP, 'SEOURL')
WHERE PLGPLGTKN = NROW.PLGPLGTKN
AND
PLGSEOURL <> JSONGETSTR(NROW.PLGPROP, 'SEOURL');
END;
25
DB/2 on steroids part II
26
DB/2 on steroids part II
Recursive SQL and Common Table Expressions
http://htbendix.dk
Solhjulet web
http://solhjulet.dk
Soljulet admin via VPN
http://172.16.0.2:7070/
The ANSI SQL way ..
with recursive catalog
(level, fldfldtkn, fldpnttkn, title) as (
select 0 as level, fldfldtkn, fldpnttkn, jsonGetStr(fldprop, 'title')
from wshfldet00
where fldfldtkn = 1
union all
select level + 1 as level, c.fldfldtkn, c.fldpnttkn, jsonGetStr(fldprop, 'title')
from catalog p, wshfldet00 c
where c.fldpnttkn = p.fldfldtkn
)
search depth first by fldpnttkn, fldfldtkn set hierarchySort
select *
from catalog
order by hierarchySort
27
DB/2 on steroids part II
Now supported in DB/2
(Show bread)
Select
title
from
start
connect
order
level, FLDPNTTKN , FLDFLDTKN, jsonGetStr(FLDPROP, 'title') as
wshFLDet00
with FLDfldtkn = 1
by Prior FLDFLDTKN = FLDPNTTKN
siblings by FLDFLDTKN;
And now you can do normal joins:
Select
from
left
on
and
and
start
connect
order
level, FLDPNTTKN , FLDFLDTKN, FLDPROP, ilgprop
wshFLDet00
join wshILGet00
jsongetint(FLDPROP, 'itemnumber') = ilgitmtkn
ilgitmtkn > 0
ilglngcod = 'da'
with FLDfldtkn = 1
by Prior FLDFLDTKN = FLDPNTTKN
siblings by FLDFLDTKN;
28
DB/2 on steroids part II
SQL SysFuncs
Consuming a REST service directly from SQL – and debugging
I have a REST service in application:
http://systest.icebreak.org/products.aspx
Now consider this:
https://dksrv131:2005/ibm/console/login.do?action=secure
Get data from the internet – (REST service client)
SELECT SYSTOOLS.HTTPGETCLOB('http://google.com/','') AS HTML_TEXT
FROM SYSIBM.SYSDUMMY1;
Values (SYSTOOLS.HTTPGETCLOB('http://google.com/',''));
Values (
SYSTOOLS.HTTPGETCLOB(
'http://systest.icebreak.org/products.aspx?func=getRows&limit=100','')
);
29
DB/2 on steroids part II
My UDTF jsonAsTableRow – show the debugger SVC600 module SVC600B
Select value from table (jsonAsTableRow
{ id:1, cust:"john"},
{ id:2, cust:"Åge"}
]','')) myjson
( '[
with myMap as ( Select value from table (jsonAsTableRow
{ id:1, cust:"john"},
{ id:2, cust:"Åge"}
]','')) myVirtualMap
)
select
jsonGetInt(value , '_seq_' ) as seq,
jsonGetInt(value , 'id' ) as id,
jsonGetStr(value , 'cust') as cust
from myMap;
( '[
with prod as (
Select value from table (
jsonAsTableRow (
cast
(SYSTOOLS.HTTPGETCLOB('http://systest.icebreak.org/products.aspx?func=get
Rows&limit=100','') as varchar(32000)),
'rows'
)
) myVirtualMap
)
select
jsonGetInt(value , '_seq_' ) as seq,
jsonGetStr(value , 'desc')
as desc,
jsonGetInt(value , 'stockcnt') as stockcnt,
date(jsonGetTs (value , 'stockdate')) as stockdate
from prod order by 3 desc;
30
DB/2 on steroids part II
SQL 7.3 / 7.2 / 7.1
The new stuff also is available on previous releases:
Features are shipped as “technology Refresh”.
https://www.ibm.com/developerworks/community/wikis/home?lang=en#!/wiki/IBM%20i%20Technology%20U
pdates/page/DB2%20for%20i%20Functional%20Enhancements
F.eks. technology Refresh (7.2 TR3 / 7.1 TR11) :
The CREATE TABLE statement is enhanced to CREATE OR REPLACE.
The modified default value can now be coded directly within the CREATE OR
REPLACE TABLE script.
ALTER TABLE will no longer be needed.
.. Read more at:
https://www.ibm.com/developerworks/community/wikis/home?lang=en#!/wiki/IBM%20i%20Technology%20Upd
ates/page/IBM%20i%207.2%20-%20TR2%20Enhancements?section=DB2%20Enhancements
31
DB/2 on steroids part II
JSON and SQL
User defined table functions
JSON:
items = [
{
"item": "iPhone",
"price" : 123.55 ,
"type" : "phone",
"Manufact":"APPLE",
"4Gsupprt":true,
"pixels":"5M",
"stock" : 9
},{
"item": "Ixus Plus",
"price" :
812.00 ,
"type" : "camera" ,
"Manufact" : "CANON" ,
"pixels" : "5M" ,
"stock" : 5
}
]
32
DB/2 on steroids part II
Selecting JSON data as columns
33
DB/2 on steroids part II
-- The file is just a simple source file. Any table or file will do
Select *
from qgpl.itemsJson;
-- Simple Select:
-- Pulling the item element out as the name
Select jsonGetStr(srcdta ,'item') as ItemName
from itemsJson;
-- Cast the property
Select
cast(jsonGetStr(srcdta
from itemsJson;
,'item')
as varchar(30)) as ItemName
Select
cast(jsonGetStr(srcdta
cast(jsonGetStr(srcdta
cast(jsonGetStr(srcdta
cast(jsonGetNum(srcdta
from itemsJson;
,'type')
,'item')
,'pixels')
,'price')
as
as
as
as
char(10))
varchar(30))
varchar(10))
dec(7, 2))
as
as
as
as
Type,
ItemName,
Pixels ,
Price
-- Adding the where
Select
cast(jsonGetStr(srcdta ,'type')
as char(10))
cast(jsonGetStr(srcdta ,'item')
as varchar(30))
cast(jsonGetInt(srcdta ,'pixels') as int
)
cast(jsonGetNum(srcdta ,'price') as dec(7, 2))
jsonGetInt(srcdta ,'stock')
from itemsJson
where jsonPropsIn (srcdta , 'type' , 'camera') = 1;
as
as
as
as
as
Type,
ItemName,
Pixels,
Price,
Stock
-- Create is as a view
create or replace view qtemp/camera as (
Select
cast(jsonGetStr(srcdta ,'item')
as varchar(30))
cast(jsonGetNum(srcdta ,'price')
as dec(7, 2))
cast(jsonGetInt(srcdta ,'pixels') as int
)
jsonGetInt(srcdta ,'stock')
from itemsJson
where jsonPropsIn (srcdta , 'type' , 'camera') = 1
);
as
as
as
as
ItemName,
Price,
Pixels,
Stock
Select *
from qtemp/camera
order by stock;
34
DB/2 on steroids part II
Building up the “search”
-- Building up search
-Use the "props in"
Select
cast(jsonGetStr(srcdta ,'type')
as char(10))
cast(jsonGetStr(srcdta ,'item')
as varchar(30))
ItemName,
cast(jsonGetStr(srcdta ,'Manufact') as varchar(20))
Manufact,
cast(jsonGetNum(srcdta ,'price')
as dec(7, 2))
from itemsJson
where jsonPropsIn (srcdta , 'Manufact' , 'NIKON,APPLE') = 1;
-Use the "props Like"
Select
cast(jsonGetStr(srcdta ,'type')
cast(jsonGetStr(srcdta ,'item')
ItemName,
cast(jsonGetStr(srcdta ,'Manufact')
Manufact,
cast(jsonGetNum(srcdta ,'price')
from itemsJson
where jsonPropsLike (srcdta , 'item,Manufact' ,
as Type,
as
as
as Price
as char(10))
as Type,
as varchar(30)) as
as varchar(20)) as
as dec(7, 2))
as Price
'canon') = 1;
-And the combo: "Props Like In"
Select
cast(jsonGetStr(srcdta ,'type')
as char(10))
as
cast(jsonGetStr(srcdta ,'item')
as varchar(30)) as
cast(jsonGetStr(srcdta ,'Manufact') as varchar(20)) as
cast(jsonGetNum(srcdta ,'price')
as dec(7, 2))
as
jsonGetInt(srcdta ,'stock')
as
from itemsJson
where jsonPropsLikeIn (srcdta , 'item,Manufact' , 'ix') =
Type,
ItemName,
Manufact,
Price,
Stock
1;
35
DB/2 on steroids part II
Update
- update - set an object
update itemsJson
set srcdta = jsonSetObj(srcdta ,'p' , '{x:1,y:2}');
Select * from itemsJson;
-- update - delete an object
update itemsJson
set srcdta = jsonSetObj(srcdta ,'p' , 'null');
-- update - set a string attribute
update itemsJson
set srcdta = jsonSetStr(srcdta ,'anystr' , 'Cool value');
-- Increment a numeric property
update itemsJson
set
srcdta = jsonSetStr(srcdta, 'seq' ,
cast (jsonGetInt(srcdta, 'seq') + 1 as varchar(3))
);
36
DB/2 on steroids part II
Uncovered:
Stored procedures: ASSOCIATE LOCATOR and ALLOCATE CURSOR
Arrays;
CREATE TYPE part_ids AS CHAR(3) ARRAY[10];
UNNEST / CARDINALITY
37
DB/2 on steroids part II
Data typs:
38
DB/2 on steroids part II
XML
<?xml version="1.0" encoding="UTF-8"?>
<Items>
<Link>http://www.erabiler.dk/product/toyota-04466-05041</Link>
<Desc>Bremseklods sæt</Desc>
<Produktnr>04466-05041</Produktnr>
<Producent>TOYOTA</Producent>
<ProducentensProduktnr>04466-05041</ProducentensProduktnr>
<Kvalitet>OE</Kvalitet>
<Vgt>2.54</Vgt>
<Volumen>5.66</Volumen>
<Info>4 stk. Bremseklodser fra år 2009/05 til 2011/11</Info>
</Items>
Update random data:
set current schema icebreak;
update product
set stockdate = current date + 50 days
- mod(rrn(product), 100) days,
stockCnt = mod(rrn(product), 33);
39