Author: Graeme Birchall ©
Email: Graeme_Birchall@compuserve.com
Web: http://ourworld.compuserve.com/homepages/Graeme_Birchall
Title: DB2 UDB V8.2 SQL Cookbook ©
Date: 1-Jun-2005
EMP_NM EMP_JB SELECT nm.id ANSWER
+----------+ +--------+ ,nm.name ================
|ID|NAME | |ID|JOB | ,jb.job ID NAME JOB
|--|-------| |--|-----| FROM emp_nm nm -- ------- -----
|10|Sanders| |10|Sales| ,emp_jb jb 10 Sanders Sales
|20|Pernal | |20|Clerk| WHERE nm.id = jb.id 20 Pernal Clerk
|50|Hanes | +--------+ ORDER BY 1;
+----------+
Figure 1, Join example
EMP_NM EMP_JB SELECT nm.id ANSWER
+----------+ +--------+ ,nm.name ================
|ID|NAME | |ID|JOB | ,jb.job ID NAME JOB
|--|-------| |--|-----| FROM emp_nm nm -- ------- -----
|10|Sanders| |10|Sales| LEFT OUTER JOIN 10 Sanders Sales
|20|Pernal | |20|Clerk| emp_jb jb 20 Pernal Clerk
|50|Hanes | +--------+ ON nm.id = jb.id 50 Hanes -
+----------+ ORDER BY nm.id;
Figure 2,Left-outer-join example
EMP_NM EMP_JB SELECT * ANSWER
+----------+ +--------+ FROM emp_nm nm ========
|ID|NAME | |ID|JOB | WHERE NOT EXISTS ID NAME
|--|-------| |--|-----| (SELECT * == =====
|10|Sanders| |10|Sales| FROM emp_jb jb 50 Hanes
|20|Pernal | |20|Clerk| WHERE nm.id = jb.id)
|50|Hanes | +--------+ ORDER BY id;
+----------+
Figure 3, Sub-query example
EMP_NM EMP_JB SELECT * ANSWER
+----------+ +--------+ FROM emp_nm =========
|ID|NAME | |ID|JOB | WHERE name < 'S' ID 2
|--|-------| |--|-----| UNION -- ------
|10|Sanders| |10|Sales| SELECT * 10 Sales
|20|Pernal | |20|Clerk| FROM emp_jb 20 Clerk
|50|Hanes | +--------+ ORDER BY 1,2; 20 Pernal
+----------+ 50 Hanes
Figure 4, Union example
EMP_JB SELECT id
+--------+ ,job ANSWER
|ID|JOB | ,ROW_NUMBER() OVER(ORDER BY job) AS R ==========
|--|-----| FROM emp_jb ID JOB R
|10|Sales| ORDER BY job; -- ----- -
|20|Clerk| 20 Clerk 1
+--------+ 10 Sales 2
Figure 5, Assign row-numbers example
EMP_JB SELECT id ANSWER
+--------+ ,job ===============
|ID|JOB | ,CASE ID JOB STATUS
|--|-----| WHEN job = 'Sales' -- ----- ------
|10|Sales| THEN 'Fire' 10 Sales Fire
|20|Clerk| ELSE 'Demote' 20 Clerk Demote
+--------+ END AS STATUS
FROM emp_jb;
Figure 6, Case stmt example
FAMILY WITH temp (persn, lvl) AS ANSWER
+-----------+ (SELECT parnt, 1 =========
|PARNT|CHILD| FROM family PERSN LVL
|-----|-----| WHERE parnt = 'Dad' ----- ---
|GrDad|Dad | UNION ALL Dad 1
|Dad |Dghtr| SELECT child, Lvl + 1 Dghtr 2
|Dghtr|GrSon| FROM temp, GrSon 3
|Dghtr|GrDtr| family GrDtr 3
+-----------+ WHERE persn = parnt)
SELECT *
FROM temp;
Figure 7, Recursion example
INPUT DATA Recursive SQL ANSWER
================= ============> ===========
"Some silly text" TEXT LINE#
----- -----
Some 1
silly 2
text 3
Figure 8, Convert string to rows
INPUT DATA Recursive SQL ANSWER
=========== ============> =================
TEXT LINE# "Some silly text"
----- -----
Some 1
silly 2
text 3
Figure 9, Convert rows to string
EMP_NM SELECT * ANSWER
+----------+ FROM emp_nm =========
|ID|NAME | ORDER BY id DESC ID NAME
|--|-------| FETCH FIRST 2 ROWS ONLY; -- ------
|10|Sanders| 50 Hanes
|20|Pernal | 20 Pernal
|50|Hanes |
+----------+
Figure 10, Fetch first "n" rows example
EMP_NM SELECT * ANSWER
+----------+ FROM emp_nm ==========
|ID|NAME | WHERE name like 'S%' ID NAME
|--|-------| WITH UR; -- -------
|10|Sanders| 10 Sanders
|20|Pernal |
|50|Hanes |
+----------+
Figure 11, Fetch WITH UR example
EMP_NM SELECT AVG(id) AS avg ANSWER
+----------+ ,MAX(name) AS maxn =================
|ID|NAME | ,COUNT(*) AS #rows AVG MAXN #ROWS
|--|-------| FROM emp_nm; --- ------- -----
|10|Sanders| 26 Sanders 3
|20|Pernal |
|50|Hanes |
+----------+
Figure 12, Column Functions example
SELECT job ANSWER
,dept =======================
,SUM(salary) AS sum_sal JOB DEPT SUM_SAL #EMP
,COUNT(*) AS #emps ----- ---- -------- ----
FROM staff Clerk 15 24766.70 2
WHERE dept < 30 Clerk 20 27757.35 2
AND salary < 20000 Clerk - 52524.05 4
AND job < 'S' Mgr 10 19260.25 1
GROUP BY ROLLUP(job, dept) Mgr 20 18357.50 1
ORDER BY job Mgr - 37617.75 2
,dept; - - 90141.80 6
Figure 13, Subtotal and Grand-total example
Figure 14, Syntax Diagram Conventions
--#SET DELIMITER !
SELECT name FROM staff WHERE id = 10!
--#SET DELIMITER ;
SELECT name FROM staff WHERE id = 20;
Figure 15, Set Delimiter example
CREATE TABLE employee
(empno CHARACTER (00006) NOT NULL
,firstnme VARCHAR (00012) NOT NULL
,midinit CHARACTER (00001) NOT NULL
,lastname VARCHAR (00015) NOT NULL
,workdept CHARACTER (00003)
,phoneno CHARACTER (00004)
,hiredate DATE
,job CHARACTER (00008)
,edlevel SMALLINT NOT NULL
,SEX CHARACTER (00001)
,birthdate DATE
,salary DECIMAL (00009,02)
,bonus DECIMAL (00009,02)
,comm DECIMAL (00009,02)
)
DATA CAPTURE NONE;
Figure 16, DB2 sample table - EMPLOYEE
CREATE VIEW employee_view AS
SELECT a.empno, a.firstnme, a.salary, a.workdept
FROM employee a
WHERE a.salary >=
(SELECT AVG(b.salary)
FROM employee b
WHERE a.workdept = b.workdept);
Figure 17, DB2 sample view - EMPLOYEE_VIEW
CREATE VIEW silly (c1, c2, c3)
AS VALUES (11, 'AAA', SMALLINT(22))
,(12, 'BBB', SMALLINT(33))
,(13, 'CCC', NULL);
Figure 18, Define a view using a VALUES clause
SELECT c1, c2, c3 ANSWER
FROM silly ===========
ORDER BY c1 aSC; C1 C2 C3
-- --- --
11 AAA 22
12 BBB 33
13 CCC -
Figure 19, SELECT from a view that has its own data
CREATE VIEW test_data AS
WITH temp1 (num1) AS
(VALUES (1)
UNION ALL
SELECT num1 + 1
FROM temp1
WHERE num1 < 10000)
SELECT *
FROM temp1;
Figure 20, Define a view that creates data on the fly
CREATE ALIAS employee_al1 FOR employee;
COMMIT;
CREATE ALIAS employee_al2 fOR employee_al1;
COMMIT;
CREATE ALIAS employee_al3 FOR employee_al2;
COMMIT;
Figure 21, Define three aliases, the latter on the earlier
CREATE NICKNAME emp FOR unixserver.production.employee;
Figure 22, Define a nickname
SELECT *
FROM staff TABLESAMPLE BERNOULLI(10);
Figure 23, TABLESAMPLE example
CREATE TABLE sales_record
(sales# INTEGER NOT NULL
GENERATED ALWAYS AS IDENTITY
(START WITH 1
,INCREMENT BY 1
,NO MAXVALUE
,NO CYCLE)
,sale_ts TIMESTAMP NOT NULL
,num_items SMALLINT NOT NULL
,payment_type CHAR(2) NOT NULL
,sale_value DECIMAL(12,2) NOT NULL
,sales_tax DECIMAL(12,2)
,employee# INTEGER NOT NULL
,CONSTRAINT sales1 CHECK(payment_type IN ('CS','CR'))
,CONSTRAINT sales2 CHECK(sale_value > 0)
,CONSTRAINT sales3 CHECK(num_items > 0)
,CONSTRAINT sales4 FOREIGN KEY(employee#)
REFERENCES staff(id)
ON DELETE RESTRICT
,PRIMARY KEY(sales#));
Figure 24, Sample table definition
CREATE TABLE default_values
(c1 CHAR NOT NULL
,d1 DECIMAL NOT NULL);
Figure 25, Table with default column lengths
LABELED DURATIONS ITEM WORKS WITH DATE/TIME
<------------------------> FIXED <--------------------->
SINGULAR PLURAL SIZE DATE TIME TIMESTAMP
=========== ============ ===== ==== ==== =========
YEAR YEARS N Y - Y
MONTH MONTHS N Y - Y
DAY DAYS Y Y - Y
HOUR HOURS Y - Y Y
MINUTE MINUTES Y - Y Y
SECOND SECONDS Y - Y Y
MICROSECOND MICROSECONDS Y - Y Y
Figure 26, Labeled Durations and Date/Time Types
ANSWER
==========
SELECT sales_date <= 1995-12-31
,sales_date - 10 DAY AS d1 <= 1995-12-21
,sales_date + -1 MONTH AS d2 <= 1995-11-30
,sales_date + 99 YEARS AS d3 <= 2094-12-31
,sales_date + 55 DAYS
- 22 MONTHS AS d4 <= 1994-04-24
,sales_date + (4+6) DAYS AS d5 <= 1996-01-10
FROM sales
WHERE sales_person = 'GOUNOT'
AND sales_date = '1995-12-31'
Figure 27, Example, Labeled Duration usage
ANSWER
==========
SELECT sales_date <= 1995-12-31
,sales_date + 2 MONTH AS d1 <= 1996-02-29
,sales_date + 3 MONTHS AS d2 <= 1996-03-31
,sales_date + 2 MONTH
+ 1 MONTH AS d3 <= 1996-03-29
,sales_date + (2+1) MONTHS AS d4 <= 1996-03-31
FROM sales
WHERE sales_person = 'GOUNOT'
AND sales_date = '1995-12-31';
Figure 28, Adding Months - Varying Results
DURATION-TYPE FORMAT NUMBER-REPRESENTS USE-WITH-D-TYPE
============= ============= ===================== ===============
DATE DECIMAL(8,0) yyyymmdd TIMESTAMP, DATE
TIME DECIMAL(6,0) hhmmss TIMESTAMP, TIME
TIMESTAMP DECIMAL(20,6) yyyymmddhhmmss.zzzzzz TIMESTAMP
Figure 29, Date/Time Durations
SELECT empno ANSWER
,hiredate ====================================
,birthdate EMPNO HIREDATE BIRTHDATE
,hiredate - birthdate ------ ---------- ---------- -------
FROM employee 000150 1972-02-12 1947-05-17 240826.
WHERE workdept = 'D11' 000200 1966-03-03 1941-05-29 240905.
AND lastname < 'L' 000210 1979-04-11 1953-02-23 260116.
ORDER BY empno;
Figure 30, Date Duration Generation
ANSWER
==========
SELECT hiredate <= 1972-02-12
,hiredate - 12345678. <= 0733-03-26
,hiredate - 1234 years
- 56 months
- 78 days <= 0733-03-26
FROM employee
WHERE empno = '000150';
Figure 31, Subtracting a Date Duration
SPECIAL REGISTER UPDATE DATA-TYPE
=============================================== ====== =============
CURRENT CLIENT_ACCTNG no VARCHAR(255)
CURRENT CLIENT_APPLNAME no VARCHAR(255)
CURRENT CLIENT_USERID no VARCHAR(255)
CURRENT CLIENT_WRKSTNNAME no VARCHAR(255)
CURRENT DATE no DATE
CURRENT DBPARTITIONNUM no INTEGER
CURRENT DEFAULT TRANSFORM GROUP yes VARCHAR(18)
CURRENT DEGREE yes CHAR(5)
CURRENT EXPLAIN MODE yes VARCHAR(254)
CURRENT EXPLAIN SNAPSHOT yes CHAR(8)
CURRENT ISOLATION yes CHAR(2)
CURRENT LOCK TIMEOUT yes INTEGER
CURRENT MAINTAINED TABLE TYPES FOR OPTIMIZATION yes VARCHAR(254)
CURRENT PACKAGE PATH yes VARCHAR(4096)
CURRENT PATH yes VARCHAR(254)
CURRENT QUERY OPTIMIZATION yes INTEGER
CURRENT REFRESH AGE yes DECIMAL(20,6)
CURRENT SCHEMA yes VARCHAR(128)
CURRENT SERVER no VARCHAR(18)
CURRENT TIME no TIME
CURRENT TIMESTAMP no TIMESTAMP
CURRENT TIMEZONE no DECIMAL(6,0)
CURRENT USER no VARCHAR(128)
SESSION_USER yes VARCHAR(128)
SYSTEM_USER no VARCHAR(128)
USER yes VARCHAR(128)
Figure 32, DB2 Special Registers
Figure 33, Create Distinct Type Syntax
CREATE DISTINCT TYPE JAP_YEN AS DECIMAL(15,2) WITH COMPARISONS;
DROP DISTINCT TYPE JAP_YEN;
Figure 34, Create and drop distinct type
CREATE TABLE customer
(id INTEGER NOT NULL
,fname VARCHAR(00010) NOT NULL WITH DEFAULT ''
,lname VARCHAR(00015) NOT NULL WITH DEFAULT ''
,date_of_birth DATE
,citizenship CHAR(03)
,usa_sales DECIMAL(9,2)
,eur_sales DECIMAL(9,2)
,sales_office# SMALLINT
,last_updated TIMESTAMP
,PRIMARY KEY(id));
Figure 35, Sample table, without distinct types
SELECT id
,usa_sales + eur_sales AS tot_sales
FROM customer;
Figure 36, Silly query, but works
CREATE DISTINCT TYPE USA_DOLLARS AS DECIMAL(9,2) WITH COMPARISONS;
CREATE DISTINCT TYPE EUR_DOLLARS AS DECIMAL(9,2) WITH COMPARISONS;
Figure 37, Create Distinct Type examples
CREATE TABLE customer
(id INTEGER NOT NULL
,fname VARCHAR(00010) NOT NULL WITH DEFAULT ''
,lname VARCHAR(00015) NOT NULL WITH DEFAULT ''
,date_of_birth DATE
,citizenship CHAR(03)
,usa_sales USA_DOLLARS
,eur_sales EUR_DOLLARS
,sales_office# SMALLINT
,last_updated TIMESTAMP
,PRIMARY KEY(id));
Figure 38, Sample table, with distinct types
SELECT id
,usa_sales + eur_sales AS tot_sales
FROM customer;
Figure 39, Silly query, now fails
SELECT id
,DECIMAL(usa_sales) +
DECIMAL(eur_sales) AS tot_sales
FROM customer;
Figure 40, Silly query, works again
Figure 41, SELECT Statement Syntax (general)
Figure 42, SELECT Statement Syntax
SELECT deptno ANSWER
,admrdept ===================
,'ABC' AS abc DEPTNO ADMRDEPT ABC
FROM department ------ -------- ---
WHERE deptname LIKE '%ING%' B01 A00 ABC
ORDER BY 1; D11 D01 ABC
Figure 43, Sample SELECT statement
SELECT * ANSWER (part of)
FROM department ================
WHERE deptname LIKE '%ING%' DEPTNO etc...
ORDER BY 1; ------ ------>>>
B01 PLANNING
D11 MANUFACTU
Figure 44, Use "*" to select all columns in table
SELECT deptno ANSWER (part of)
,department.* =======================
FROM department DEPTNO DEPTNO etc...
WHERE deptname LIKE '%ING%' ------ ------ ------>>>
ORDER BY 1; B01 B01 PLANNING
D11 D11 MANUFACTU
Figure 45, Select an individual column, and all columns
SELECT department.* ANSWER (part of)
,department.* ================
FROM department DEPTNO etc...
WHERE deptname LIKE '%NING%' ------ ------>>>
ORDER BY 1; B01 PLANNING
Figure 46, Select all columns twice
Figure 47, Fetch First clause Syntax
SELECT years ANSWER
,name =====================
,id YEARS NAME ID
FROM staff ------ --------- ----
FETCH FIRST 3 ROWS ONLY; 7 Sanders 10
8 Pernal 20
5 Marenghi 30
Figure 48, FETCH FIRST without ORDER BY, gets random rows
SELECT years ANSWER
,name =====================
,id YEARS NAME ID
FROM staff ------ --------- ----
WHERE years IS NOT NULL 13 Graham 310
ORDER BY years DESC 12 Jones 260
FETCH FIRST 3 ROWS ONLY; 10 Hanes 50
Figure 49, FETCH FIRST with ORDER BY, gets wrong answer
SELECT years ANSWER
,name =====================
,id YEARS NAME ID
FROM staff ------ --------- ----
WHERE years IS NOT NULL 13 Graham 310
ORDER BY years DESC 12 Jones 260
,id DESC 10 Quill 290
FETCH FIRST 3 ROWS ONLY;
Figure 50, FETCH FIRST with ORDER BY, gets right answer
SELECT a.empno ANSWER
,a.lastname =================
FROM employee a EMPNO LASTNAME
,(SELECT MAX(empno)AS empno ------ ----------
FROM employee) AS b 000340 GOUNOT
WHERE a.empno = b.empno;
Figure 51, Correlation Name usage example
SELECT a.empno ANSWER
,a.lastname ======================
,b.deptno AS dept EMPNO LASTNAME DEPT
FROM employee a ------ ---------- ----
,department b 000090 HENDERSON E11
WHERE a.workdept = b.deptno 000280 SCHNEIDER E11
AND a.job <> 'SALESREP' 000290 PARKER E11
AND b.deptname = 'OPERATIONS' 000300 SMITH E11
AND a.sex IN ('M','F') 000310 SETRIGHT E11
AND b.location IS NULL
ORDER BY 1;
Figure 52, Correlation name usage example
SELECT empno AS e_num ANSWER
,midinit AS "m int" ===================
,phoneno AS "..." E_NUM M INT ...
FROM employee ------ ----- ----
WHERE empno < '000030' 000010 I 3978
ORDER BY 1; 000020 L 3476
Figure 53, Renaming fields using AS
CREATE view emp2 AS
SELECT empno AS e_num
,midinit AS "m int"
,phoneno AS "..."
FROM employee; ANSWER
===================
SELECT * E_NUM M INT ...
FROM emp2 ------ ----- ----
WHERE "..." = '3978'; 000010 I 3978
Figure 54, View field names defined using AS
SELECT AVG(comm) AS a1 ANSWER
,SUM(comm) / COUNT(*) AS a2 ===============
FROM staff A1 A2
WHERE id < 100; ------- ------
796.025 530.68
Figure 55, AVG of data containing null values
SELECT COUNT(*) AS num ANSWER
,MAX(lastname) AS max ========
FROM employee NUM MAX
WHERE firstnme = 'FRED'; --- ---
0 -
Figure 56, Getting a NULL value from a field defined NOT NULL
SELECT AVG(comm) AS a1 ANSWER
,SUM(comm) / COUNT(*) AS a2 ===============
FROM staff A1 A2
WHERE id < 100 ------- ------
AND comm IS NOT NULL; 796.025 796.02
Figure 57, AVG of those rows that are not null
SELECT 'JOHN' AS J1
,'JOHN''S' AS J2 ANSWER
,'''JOHN''S''' AS J3 =============================
,'"JOHN''S"' AS J4 J1 J2 J3 J4
FROM staff ---- ------ -------- --------
WHERE id = 10; JOHN JOHN'S 'JOHN'S' "JOHN'S"
Figure 58, Quote usage
SELECT id AS "USER ID" ANSWER
,dept AS "D#" ===============================
,years AS "#Y" USER ID D# #Y 'TXT' "quote" fld
,'ABC' AS "'TXT'" ------- -- -- ----- -----------
,'"' AS """quote"" fld" 10 20 7 ABC "
FROM staff s 20 20 8 ABC "
WHERE id < 40 30 38 5 ABC "
ORDER BY "USER ID";
Figure 59, Double-quote usage
Figure 60, Basic Predicate syntax, 1 of 2
SELECT id, job, dept ANSWER
FROM staff ===============
WHERE job = 'Mgr' ID JOB DEPT
AND NOT job <> 'Mgr' --- ---- ----
AND NOT job = 'Sales' 10 Mgr 20
AND id <> 100 30 Mgr 38
AND id >= 0 50 Mgr 15
AND id <= 150 140 Mgr 51
AND NOT dept = 50
ORDER BY id;
Figure 61, Basic Predicate examples
Figure 62, Basic Predicate syntax, 2 of 2
SELECT id, dept, job ANSWER
FROM staff ===========
WHERE (id,dept) = (30,28) ID DEPT JOB
OR (id,years) = (90, 7) -- ---- ---
OR (dept,job) = (38,'Mgr') 30 38 Mgr
ORDER BY 1;
Figure 63, Basic Predicate example, multi-value check
SELECT id, dept, job ANSWER
FROM staff ===========
WHERE (id = 30 AND dept = 28) ID DEPT JOB
OR (id = 90 AND years = 7) -- ---- ---
OR (dept = 38 AND job = 'Mgr') 30 38 Mgr
ORDER BY 1;
Figure 64, Same query as prior, using individual predicates
Figure 65, Quantified Predicate syntax
SELECT id, job ANSWER
FROM staff ========
WHERE job = ANY (SELECT job FROM staff) ID JOB
AND id <= ALL (SELECT id FROM staff) --- ----
ORDER BY id; 10 Mgr
Figure 66, Quantified Predicate example, two single-value sub-queries
SELECT id, dept, job ANSWER
FROM staff ==============
WHERE (id,dept) = ANY ID DEPT JOB
(SELECT dept, id --- ---- -----
FROM staff) 20 20 Sales
ORDER BY 1;
Figure 67, Quantified Predicate example, multi-value sub-query
Figure 68, BETWEEN Predicate syntax
SELECT id, job ANSWER
FROM staff =========
WHERE id BETWEEN 10 AND 30 ID JOB
AND id NOT BETWEEN 30 AND 10 --- -----
AND NOT id NOT BETWEEN 10 AND 30 10 Mgr
ORDER BY id; 20 Sales
30 Mgr
Figure 69, BETWEEN Predicate examples
Figure 70, EXISTS Predicate syntax
SELECT id, job ANSWER
FROM staff a =========
WHERE EXISTS ID JOB
(SELECT * --- -----
FROM staff b 10 Mgr
WHERE b.id = a.id 20 Sales
AND b.id < 50) 30 Mgr
ORDER BY id; 40 Sales
Figure 71, EXISTS Predicate example
Figure 72, IN Predicate syntax
SELECT id, job ANSWER
FROM staff a =========
WHERE id IN (10,20,30) ID JOB
AND id IN (SELECT id --- -----
FROM staff) 10 Mgr
AND id NOT IN 99 20 Sales
ORDER BY id; 30 Mgr
Figure 73, IN Predicate examples, single values
SELECT empno, lastname ANSWER
FROM employee ===============
WHERE (empno, 'AD3113') IN EMPNO LASTNAME
(SELECT empno, projno ------ -------
FROM emp_act 000260 JOHNSON
WHERE emptime > 0.5) 000270 PEREZ
ORDER BY 1;
Figure 74, IN Predicate example, multi-value
Figure 75, LIKE Predicate syntax
SELECT id, name ANSWER
FROM staff ==============
WHERE name LIKE 'S%n' ID NAME
OR name LIKE '_a_a%' --- ---------
OR name LIKE '%r_%a' 130 Yamaguchi
ORDER BY id; 200 Scoutten
Figure 76, LIKE Predicate examples
LIKE STATEMENT TEXT WHAT VALUES MATCH
=========================== ======================
LIKE 'AB%' Finds AB, any string
LIKE 'AB%' ESCAPE '+' Finds AB, any string
LIKE 'AB+%' ESCAPE '+' Finds AB%
LIKE 'AB++' ESCAPE '+' Finds AB+
LIKE 'AB+%%' ESCAPE '+' Finds AB%, any string
LIKE 'AB++%' ESCAPE '+' Finds AB+, any string
LIKE 'AB+++%' ESCAPE '+' Finds AB+%
LIKE 'AB+++%%' ESCAPE '+' Finds AB+%, any string
LIKE 'AB+%+%%' ESCAPE '+' Finds AB%%, any string
LIKE 'AB++++' ESCAPE '+' Finds AB++
LIKE 'AB+++++%' ESCAPE '+' Finds AB++%
LIKE 'AB++++%' ESCAPE '+' Finds AB++, any string
LIKE 'AB+%++%' ESCAPE '+' Finds AB%+, any string
Figure 77, LIKE and ESCAPE examples
SELECT id ANSWER
FROM staff ======
WHERE id = 10 ID
AND 'ABC' LIKE 'AB%' ---
AND 'A%C' LIKE 'A/%C' ESCAPE '/' 10
AND 'A_C' LIKE 'A\_C' ESCAPE '\'
AND 'A_$' LIKE 'A$_$$' ESCAPE '$';
Figure 78, LIKE and ESCAPE examples
Figure 79, NULL Predicate syntax
SELECT id, comm ANSWER
FROM staff =========
WHERE id < 100 ID COMM
AND id IS NOT NULL --- ----
AND comm IS NULL 10 -
AND NOT comm IS NOT NULL 30 -
ORDER BY id; 50 -
Figure 80, NULL predicate examples
SELECT id
,name
FROM staff
WHERE name LIKE '%a' || X'3B' || '%'
ORDER BY id;
Figure 81, Refer to semi-colon in SQL text
Example: 555 + -22 / (12 - 3) * 66 ANSWER
======
^ ^ ^ ^ ^ 423
5th 2nd 3rd 1st 4th
Figure 82, Precedence rules example
SELECT (12 - 3) AS int1
, -22 / (12 - 3) AS int2
, -22 / (12 - 3) * 66 AS int3
,555 + -22 / (12 - 3) * 66 AS int4
FROM sysibm.sysdummy1; ANSWER
===================
INT1 INT2 INT3 INT4
---- ---- ---- ----
9 -2 -132 423
Figure 83, Precedence rules, integer example
SELECT (12.0 - 3) AS dec1
, -22 / (12.0 - 3) AS dec2
, -22 / (12.0 - 3) * 66 AS dec3
,555 + -22 / (12.0 - 3) * 66 AS dec4
FROM sysibm.sysdummy1; ANSWER
===========================
DEC1 DEC2 DEC3 DEC4
------ ------ ------ ------
9.0 -2.4 -161.3 393.6
Figure 84, Precedence rules, decimal example
SELECT * ANSWER>> COL1 COL2 TABLE1
FROM table1 ---- ---- +---------+
WHERE col1 = 'C' A AA |COL1|COL2|
AND col1 >= 'A' B BB |----|----|
OR col2 >= 'AA' C CC |A |AA |
ORDER BY col1; |B |BB |
|C |CC |
SELECT * ANSWER>> COL1 COL2 +---------+
FROM table1 ---- ----
WHERE (col1 = 'C' A AA
AND col1 >= 'A') B BB
OR col2 >= 'AA' C CC
ORDER BY col1;
SELECT * ANSWER>> COL1 COL2
FROM table1 ---- ----
WHERE col1 = 'C' C CC
AND (col1 >= 'A'
OR col2 >= 'AA')
ORDER BY col1;
Figure 85, Use of OR and parenthesis
Figure 86, CAST expression syntax
SELECT id ANSWER
,salary =================
,CAST(salary AS INTEGER) AS sal2 ID SALARY SAL2
FROM staff -- -------- -----
WHERE id < 30 10 18357.50 18357
ORDER BY id; 20 18171.25 18171
Figure 87, Use CAST expression to convert Decimal to Integer
SELECT id ANSWER
,job =============
,CAST(job AS CHAR(3)) AS job2 ID JOB JOB2
FROM staff -- ----- ----
WHERE id < 30 10 Mgr Mgr
ORDER BY id; 20 Sales Sal
Figure 88, Use CAST expression to truncate Char field
SELECT id ANSWER
,CAST(NULL AS SMALLINT) AS junk =======
FROM staff ID JUNK
WHERE id < 30 -- ----
ORDER BY id; 10 -
20 -
Figure 89, Use CAST expression to define SMALLINT field with null values
SELECT stf.id ANSWER
,emp.empno =========
FROM staff stf ID EMPNO
LEFT OUTER JOIN -- ------
employee emp 10 -
ON stf.id = CAST(emp.empno AS SMALLINT) 20 000020
AND emp.job = 'MANAGER' 30 000030
WHERE stf.id < 60 40 -
ORDER BY stf.id; 50 000050
Figure 90, CAST expression in join
SELECT stf.id ANSWER
,emp.empno =========
FROM staff stf ID EMPNO
LEFT OUTER JOIN -- ------
employee emp 10 -
ON stf.id = SMALLINT(emp.empno) 20 000020
AND emp.job = 'MANAGER' 30 000030
WHERE stf.id < 60 40 -
ORDER BY stf.id; 50 000050
Figure 91, Function usage in join
Figure 92, VALUES expression syntax
VALUES 6 <= 1 row, 1 column
VALUES (6) <= 1 row, 1 column
VALUES 6, 7, 8 <= 1 row, 3 columns
VALUES (6), (7), (8) <= 3 rows, 1 column
VALUES (6,66), (7,77), (8,NULL) <= 3 rows, 2 columns
Figure 93, VALUES usage examples
WITH temp1 (col1, col2) AS ANSWER
(VALUES ( 0, 'AA') =========
,( 1, 'BB') COL1 COL2
,( 2, NULL) ---- ----
) 0 AA
SELECT * 1 BB
FROM temp1; 2 -
Figure 94, Use VALUES to define a temporary table (1 of 4)
WITH temp1 (col1, col2) AS ANSWER
(VALUES (DECIMAL(0 ,3,1), 'AA') =========
,(DECIMAL(1 ,3,1), 'BB') COL1 COL2
,(DECIMAL(2 ,3,1), NULL) ---- ----
) 0.0 AA
SELECT * 1.0 BB
FROM temp1; 2.0 -
Figure 95, Use VALUES to define a temporary table (2 of 4)
WITH temp1 (col1, col2) AS ANSWER
(VALUES ( 0, CAST('AA' AS CHAR(1))) =========
,( 1, CAST('BB' AS CHAR(1))) COL1 COL2
,( 2, CAST(NULL AS CHAR(1))) ---- ----
) 0 A
SELECT * 1 B
FROM temp1; 2 -
Figure 96, Use VALUES to define a temporary table (3 of 4)
WITH temp1 (col1, col2) AS ANSWER
(VALUES ( 0, CHAR('AA',1)) =========
,( 1, CHAR('BB',1)) COL1 COL2
,( 2, NULL) ---- ----
) 0 A
SELECT * 1 B
FROM temp1; 2 -
Figure 97, Use VALUES to define a temporary table (4 of 4)
WITH temp1 (col1, col2, col3) AS ANSWER
(VALUES ( 0, 'AA', 0.00) ==========
,( 1, 'BB', 1.11) COL1B COLX
,( 2, 'CC', 2.22) ----- ----
) 0 0.00
,temp2 (col1b, colx) AS 1 2.11
(SELECT col1 2 4.22
,col1 + col3
FROM temp1
)
SELECT *
FROM temp2;
Figure 98, Derive one temporary table from another
CREATE VIEW silly (c1, c2, c3)
AS VALUES (11, 'AAA', SMALLINT(22))
,(12, 'BBB', SMALLINT(33))
,(13, 'CCC', NULL);
COMMIT;
Figure 99, Define a view using a VALUES clause
WITH temp1 (col1) AS ANSWER
(VALUES 0 ======
UNION ALL COL1
SELECT col1 + 1 ----
FROM temp1 0
WHERE col1 + 1 < 100 1
) 2
SELECT * 3
FROM temp1; etc
Figure 100, Use VALUES defined data to seed a recursive SQL statement
SELECT * ANSWER
FROM (VALUES (123,'ABC') ======
,(234,'DEF') --- ---
)AS ttt 234 DEF
ORDER BY 1 DESC; 123 ABC
Figure 101, Generate table with unnamed columns
Figure 102, CASE expression syntax
SELECT Lastname ANSWER
,sex AS sx ====================
,CASE sex LASTNAME SX SEXX
WHEN 'F' THEN 'FEMALE' ---------- -- ------
WHEN 'M' THEN 'MALE' JEFFERSON M MALE
ELSE NULL JOHNSON F FEMALE
END AS sexx JONES M MALE
FROM employee
WHERE lastname LIKE 'J%'
ORDER BY 1;
Figure 103, Use CASE (type 1) to expand a value
SELECT lastname ANSWER
,sex AS sx ====================
,CASE LASTNAME SX SEXX
WHEN sex = 'F' THEN 'FEMALE' ---------- -- ------
WHEN sex = 'M' THEN 'MALE' JEFFERSON M MALE
ELSE NULL JOHNSON F FEMALE
END AS sexx JONES M MALE
FROM employee
WHERE lastname LIKE 'J%'
ORDER BY 1;
Figure 104, Use CASE (type 2) to expand a value
SELECT lastname ANSWER
,midinit AS mi ===================
,sex AS sx LASTNAME MI SX MX
,CASE ---------- -- -- --
WHEN midinit > SEX JEFFERSON J M M
THEN midinit JOHNSON P F P
ELSE sex JONES T M T
END AS mx
FROM employee
WHERE lastname LIKE 'J%'
ORDER BY 1;
Figure 105, Use CASE to display the higher of two values
SELECT COUNT(*) AS tot ANSWER
,SUM(CASE sex WHEN 'F' THEN 1 ELSE 0 END) AS #f =========
,SUM(CASE sex WHEN 'M' THEN 1 ELSE 0 END) AS #m TOT #F #M
FROM employee --- -- --
WHERE lastname LIKE 'J%'; 3 1 2
Figure 106, Use CASE to get multiple counts in one pass
SELECT lastname ANSWER
,sex ==============
FROM employee LASTNAME SEX
WHERE lastname LIKE 'J%' ---------- ---
AND CASE sex JEFFERSON M
WHEN 'F' THEN '' JOHNSON F
WHEN 'M' THEN '' JONES M
ELSE NULL
END IS NOT NULL
ORDER BY 1;
Figure 107, Use CASE in a predicate
SELECT lastname ANSWER
,LENGTH(RTRIM(lastname)) AS len =====================
,SUBSTR(lastname,1, LASTNAME LEN LASTNM
CASE ---------- --- ------
WHEN LENGTH(RTRIM(lastname)) JEFFERSON 9 JEFFER
> 6 THEN 6 JOHNSON 7 JOHNSO
ELSE LENGTH(RTRIM(lastname)) JONES 5 JONES
END ) AS lastnm
FROM employee
WHERE lastname LIKE 'J%'
ORDER BY 1;
Figure 108, Use CASE inside a function
UPDATE staff
SET comm = CASE dept
WHEN 15 THEN comm * 1.1
WHEN 20 THEN comm * 1.2
WHEN 38 THEN
CASE
WHEN years < 5 THEN comm * 1.3
WHEN years >= 5 THEN comm * 1.4
ELSE NULL
END
ELSE comm
END
WHERE comm IS NOT NULL
AND dept < 50;
Figure 109, UPDATE statement with nested CASE expressions
WITH temp1 (c1,c2) AS ANSWER
(VALUES (88,9),(44,3),(22,0),(0,1)) ========
SELECT c1 C1 C2 C3
,c2 -- -- --
,CASE c2 88 9 9
WHEN 0 THEN NULL 44 3 14
ELSE c1/c2 22 0 -
END AS c3 0 1 0
FROM temp1;
Figure 110, Use CASE to avoid divide by zero
SELECT name ANSWER
,CASE ============
WHEN name = LCASE(name) THEN NULL NAME DUMB
ELSE CAST(NULL AS CHAR(1)) ------- ----
END AS dumb Sanders -
FROM staff Pernal -
WHERE id < 30;
Figure 111, Silly CASE expression that always returns NULL
SELECT lastname ANSWER
,sex =================
,CASE LASTNAME SX SXX
WHEN sex >= 'M' THEN 'MAL' ---------- -- ---
WHEN sex >= 'F' THEN 'FEM' JEFFERSON M MAL
END AS sxx JOHNSON F FEM
FROM employee JONES M MAL
WHERE lastname LIKE 'J%'
ORDER BY 1;
Figure 112, Use CASE to derive a value (correct)
SELECT lastname ANSWER
,sex =================
,CASE LASTNAME SX SXX
WHEN sex >= 'F' THEN 'FEM' ---------- -- ---
WHEN sex >= 'M' THEN 'MAL' JEFFERSON M FEM
END AS sxx JOHNSON F FEM
FROM employee JONES M FEM
WHERE lastname LIKE 'J%'
ORDER BY 1;
Figure 113, Use CASE to derive a value (incorrect)
Figure 114, DECLARE CURSOR statement syntax
DECLARE fred CURSOR FOR
WITH RETURN TO CALLER
SELECT id
,name
,salary
,comm
FROM staff
WHERE id < :id-var
AND salary > 1000
ORDER BY id ASC
FETCH FIRST 10 ROWS ONLY
OPTIMIZE FOR 10 ROWS
FOR FETCH ONLY
WITH UR
Figure 115, Sample cursor
DECLARE fred CURSOR WITH HOLD FOR
SELECT name
,salary
FROM staff
WHERE id > :id-var
FOR UPDDATE OF salary, comm
OPEN fred
DO UNTIL SQLCODE = 100
FETCH fred
INTO :name-var
,:salary-var
IF salary < 1000 THEN DO
UPDATE staff
SET salary = :new-salary-var
WHERE CURRENT OF fred
END-IF
END-DO
CLOSE fred
Figure 116, Use cursor in program
SELECT name
,salary
INTO :name-var
,:salary-var
FROM staff
WHERE id = :id-var
Figure 117, Singleton select
Figure 118, PREPARE statement syntax
STATEMENT CAN BE USED BY STATEMENT TYPE
======================== ==============
DESCRIBE Any statement
DECLARE CURSOR Must be SELECT
EXECUTE Must not be SELECT
Figure 119, What statements can use prepared statement
SET :host-var = CURRENT TIMESTAMP
Figure 120, SET single host-variable
SET :host-v1 = CURRENT TIME
,:host-v2 = CURRENT DEGREE
,:host-v3 = NULL
Figure 121, SET multiple host-variables
SET (:hv1
,:hv2
,:hv3) =
(SELECT id
,name
,salary
FROM staff
WHERE id = :id-var)
Figure 122, SET using row-fullselect
SET CONNECTION
SET CURRENT DEFAULT TRANSFORM GROUP
SET CURRENT DEGREE
SET CURRENT EXPLAIN MODE
SET CURRENT EXPLAIN SNAPSHOT
SET CURRENT ISOLATION
SET CURRENT LOCK TIMEOUT
SET CURRENT MAINTAINED TABLE TYPES FOR OPTIMIZATION
SET CURRENT PACKAGE PATH
SET CURRENT PACKAGESET
SET CURRENT QUERY OPTIMIZATION
SET CURRENT REFRESH AGE
SET ENCRYPTION PASSWORD
SET EVENT MONITOR STATE
SET INTEGRITY
SET PASSTHRU
SET PATH
SET SCHEMA
SET SERVER OPTION
SET SESSION AUTHORIZATION
Figure 123, Other SET statements
Figure 124, SAVEPOINT statement syntax
Figure 125, Example of savepoint usage
Figure 126, RELEASE SAVEPOINT statement syntax
Figure 127, ROLLBACK statement syntax
CREATE TABLE emp_act
(empno CHARACTER (00006) NOT NULL
,projno CHARACTER (00006) NOT NULL
,actno SMALLINT NOT NULL
,emptime DECIMAL (05,02)
,emstdate DATE
,emendate DATE);
Figure 128, EMP_ACT sample table - DDL
Figure 129, INSERT statement syntax
INSERT INTO emp_act VALUES
('100000' ,'ABC' ,10 ,1.4 ,'2003-10-22', '2003-11-24');
Figure 130, Single row insert
INSERT INTO emp_act VALUES
('200000' ,'ABC' ,10 ,1.4 ,'2003-10-22', '2003-11-24')
,('200000' ,'DEF' ,10 ,1.4 ,'2003-10-22', '2003-11-24')
,('200000' ,'IJK' ,10 ,1.4 ,'2003-10-22', '2003-11-24');
Figure 131, Multi row insert
INSERT INTO emp_act VALUES
('400000' ,'ABC' ,10 ,NULL ,DEFAULT, CURRENT DATE);
Figure 132,Using null and default values
INSERT INTO emp_act (projno, emendate, actno, empno) VALUES
('ABC' ,DATE(CURRENT TIMESTAMP) ,123 ,'500000');
Figure 133, Explicitly listing columns being populated during insert
INSERT INTO
(SELECT *
FROM emp_act
WHERE empno < '1'
)
VALUES ('510000' ,'ABC' ,10 ,1.4 ,'2003-10-22', '2003-11-24');
Figure 134, Insert into a full-select
INSERT INTO emp_act
SELECT LTRIM(CHAR(id + 600000))
,SUBSTR(UCASE(name),1,6)
,salary / 229
,123
,CURRENT DATE
,'2003-11-11'
FROM staff
WHERE id < 50;
Figure 135,Insert result of select statement
INSERT INTO emp_act (empno, actno, projno)
SELECT LTRIM(CHAR(id + 700000))
,MINUTE(CURRENT TIME)
,'DEF'
FROM staff
WHERE id < 40;
Figure 136, Insert result of select - specified columns only
INSERT INTO emp_act
SELECT *
FROM emp_act;
Figure 137, Stupid - insert - doubles rows
INSERT INTO emp_act (empno, actno, projno)
SELECT LTRIM(CHAR(id + 800000))
,77
,'XYZ'
FROM staff
WHERE id < 40
UNION
SELECT LTRIM(CHAR(id + 900000))
,SALARY / 100
,'DEF'
FROM staff
WHERE id < 50;
Figure 138, Inserting result of union
INSERT INTO emp_act (empno, actno, projno, emptime)
WITH temp1 (col1) AS
(VALUES (1),(2),(3),(4),(5),(6))
SELECT LTRIM(CHAR(col1 + 910000))
,col1
,CHAR(col1)
,col1 / 2
FROM temp1;
Figure 139, Insert from common table expression
INSERT INTO emp_act (empno, actno, projno)
SELECT LTRIM(CHAR(id + 920000))
,id
,'ABC'
FROM staff
WHERE id < 40
AND NOT EXISTS
(SELECT *
FROM emp_act
WHERE empno LIKE '92%');
Figure 140, Insert with irrelevant sub-query
CREATE TABLE us_customer CREATE TABLE intl_customer
(cust# INTEGER NOT NULL (cust# INTEGER NOT NULL
,cname CHAR(10) NOT NULL ,cname CHAR(10) NOT NULL
,country CHAR(03) NOT NULL ,country CHAR(03) NOT NULL
,CHECK (country = 'USA') ,CHECK (country <> 'USA')
,PRIMARY KEY (cust#)); ,PRIMARY KEY (cust#));
Figure 141, Customer tables - for insert usage
INSERT INTO
(SELECT *
FROM us_customer
UNION ALL
SELECT *
FROM intl_customer)
VALUES (111,'Fred','USA')
,(222,'Dave','USA')
,(333,'Juan','MEX');
Figure 142, Insert into multiple tables
UPDATE emp_act
SET emptime = NULL
,emendate = DEFAULT
,emstdate = CURRENT DATE + 2 DAYS
,actno = ACTNO / 2
,projno = 'ABC'
WHERE empno = '100000';
Figure 143, Single row update
Figure 144, UPDATE statement syntax
UPDATE emp_act
SET actno = actno / 2;
Figure 145, Mass update
UPDATE emp_act ac1
SET actno = actno * 2
,emptime = actno * 2
WHERE empno LIKE '910%';
Figure 146, Two columns get same value
UPDATE emp_act
SET actno = (SELECT MAX(salary)
FROM staff)
WHERE empno = '200000';
Figure 147, Update using select
UPDATE emp_act
SET (actno
,emstdate
,projno) = (SELECT MAX(salary)
,CURRENT DATE + 2 DAYS
,MIN(CHAR(id))
FROM staff
WHERE id <> 33)
WHERE empno LIKE '600%';
Figure 148, Multi-row update using select
UPDATE emp_act ac1
SET (actno
,emptime) = (SELECT ac2.actno + 1
,ac1.emptime / 2
FROM emp_act ac2
WHERE ac2.empno LIKE '60%'
AND SUBSTR(ac2.empno,3) = SUBSTR(ac1.empno,3))
WHERE EMPNO LIKE '700%';
Figure 149, Multi-row update using correlated select
UPDATE emp_act
SET emptime = 10
WHERE empno = '000010'
AND projno = 'MA2100';
Figure 150, Direct update of table
UPDATE
(SELECT *
FROM emp_act
WHERE empno = '000010'
AND projno = 'MA2100'
)AS ea
SET emptime = 20;
Figure 151, Update of full-select
UPDATE emp_act ea1
SET emptime = (SELECT MAX(emptime)
FROM emp_act ea2
WHERE ea1.empno = ea2.empno)
WHERE empno = '000010'
AND projno = 'MA2100';
Figure 152, Set employee-time in row to MAX - for given employee
UPDATE
(SELECT ea1.*
,MAX(emptime) OVER(PARTITION BY empno) AS maxtime
FROM emp_act ea1
)AS ea2
SET emptime = maxtime
WHERE empno = '000010'
AND projno = 'MA2100';
Figure 153, Use OLAP function to get max-time, then apply (correct)
UPDATE emp_act
SET emptime = MAX(emptime) OVER(PARTITION BY empno)
WHERE empno = '000010'
AND projno = 'MA2100';
Figure 154, Use OLAP function to get max-time, then apply (wrong)
UPDATE emp_act ac1
SET (actno
,emptime) = (SELECT ROW_NUMBER() OVER()
,ac1.emptime / 2
FROM emp_act ac2
WHERE ac2.empno LIKE '60%'
AND SUBSTR(ac2.empno,3) = SUBSTR(ac1.empno,3))
WHERE EMPNO LIKE '800%';
Figure 155, Update with correlated query
UPDATE emp_act ac1
SET (actno
,emptime) = (SELECT c1
,c2
FROM (SELECT ROW_NUMBER() OVER() AS c1
,actno / 100 AS c2
,empno
FROM emp_act
WHERE empno LIKE '60%'
)AS ac2
WHERE SUBSTR(ac2.empno,3) = SUBSTR(ac1.empno,3))
WHERE empno LIKE '900%';
Figure 156, Update with uncorrelated query
DELETE
FROM emp_act
WHERE empno = '000010'
AND projno = 'MA2100'
AND actno = 10;
Figure 157, Single-row delete
Figure 158, DELETE statement syntax
DELETE
FROM emp_act;
Figure 159, Mass delete
DELETE
FROM emp_act
WHERE empno LIKE '00%'
AND projno >= 'MA';
Figure 160, Selective delete
DELETE
FROM staff s1
WHERE id NOT IN
(SELECT MAX(id)
FROM staff s2
WHERE s1.dept = s2.dept);
Figure 161, Correlated delete (1 of 2)
DELETE
FROM staff s1
WHERE EXISTS
(SELECT *
FROM staff s2
WHERE s2.dept = s1.dept
AND s2.id > s1.id);
Figure 162, Correlated delete (2 of 2)
DELETE FROM
(SELECT id
,MAX(id) OVER(PARTITION BY dept) AS max_id
FROM staff
)AS ss
WHERE id <> max_id;
Figure 163, Delete using full-select and OLAP function
DELETE
FROM emp_act
WHERE (empno, projno, actno) IN
(SELECT empno
,projno
,actno
FROM (SELECT eee.*
,ROW_NUMBER()
OVER(ORDER BY empno, projno, actno) AS r#
FROM emp_act eee
)AS xxx
WHERE r# <= 10);
Figure 164, Delete first "n" rows
Figure 165, Select DML statement syntax
ANSWER
==============
SELECT empno EMPNO PRJ ACT
,projno AS prj ------ --- ---
,actno AS act 200000 ABC 10
FROM FINAL TABLE 200000 DEF 10
(INSERT INTO emp_act
VALUES ('200000','ABC',10 ,1,'2003-10-22','2003-11-24')
,('200000','DEF',10 ,1,'2003-10-22','2003-11-24'))
ORDER BY 1,2,3;
Figure 166, Select rows inserted
SELECT empno ANSWER
,projno AS prj =================
,actno AS act EMPNO PRJ ACT R#
,row# AS r# ------ --- --- --
FROM FINAL TABLE 300000 ZZZ 999 1
(INSERT INTO emp_act (empno, projno, actno) 300000 VVV 111 2
INCLUDE (row# SMALLINT)
VALUES ('300000','ZZZ',999,1)
,('300000','VVV',111,2))
ORDER BY row#;
Figure 167, Include column to get insert sequence
SELECT empno ANSWER
,projno AS prj =================
,actno AS act EMPNO PRJ ACT R#
,ROW_NUMBER() OVER() AS r# ------ --- --- --
FROM FINAL TABLE 400000 ZZZ 999 1
(INSERT INTO emp_act (empno, projno, actno) 400000 VVV 111 2
VALUES ('400000','ZZZ',999)
,('400000','VVV',111))
ORDER BY INPUT SEQUENCE;
Figure 168, Select rows in insert order
SELECT empno ANSWER
,projno AS prj =================
,actno AS act EMPNO PRJ ACT R#
,ROW_NUMBER() OVER() AS r# ------ --- -- --
FROM NEW TABLE 600010 1 59 1
(INSERT INTO emp_act (empno, actno, projno) 600020 563 59 2
SELECT LTRIM(CHAR(id + 600000)) 600030 193 59 3
,SECOND(CURRENT TIME)
,CHAR(SMALLINT(RAND(1) * 1000))
FROM staff
WHERE id < 40)
ORDER BY INPUT SEQUENCE;
Figure 169, Select from an insert that has unknown values
SELECT empno ANSWER
,projno AS prj ================
,emptime AS etime EMPNO PRJ ETIME
FROM OLD TABLE ------ --- -----
(UPDATE emp_act 200000 ABC 1.00
SET emptime = emptime * 2 200000 DEF 1.00
WHERE empno = '200000')
ORDER BY projno;
Figure 170, Select values - from before update
SELECT projno AS prj ANSWER
,old_t AS old_t ===============
,emptime AS new_t PRJ OLD_T NEW_T
FROM NEW TABLE --- ----- -----
(UPDATE emp_act ABC 2.00 0.02
INCLUDE (old_t DECIMAL(5,2)) DEF 2.00 11.27
SET emptime = emptime * RAND(1) * 10
,old_t = emptime
WHERE empno = '200000')
ORDER BY 1;
Figure 171, Select values - before and after update
SELECT projno AS prj ANSWER
,actno AS act =======
FROM OLD TABLE PRJ ACT
(DELETE --- ---
FROM emp_act VVV 111
WHERE empno = '300000') ZZZ 999
ORDER BY 1,2;
Figure 172, List deleted rows
SELECT empno ANSWER
,projno ====================
,actno AS act EMPNO PROJNO ACT R#
,row# AS r# ------ ------ --- --
FROM OLD TABLE 000260 AD3113 70 2
(DELETE 000260 AD3113 80 4
FROM emp_act 000260 AD3113 180 6
INCLUDE (row# SMALLINT)
SET row# = ROW_NUMBER() OVER()
WHERE empno = '000260')
WHERE row# = row# / 2 * 2
ORDER BY 1,2,3;
Figure 173, Assign row numbers to deleted rows
SELECT empno ANSWER
,(SELECT lastname ==========================
FROM (SELECT empno AS e# EMPNO LASTNAME PROJNO ACT
,lastname ------ -------- ------ ---
FROM employee 000010 HAAS AD3100 10
)AS xxx 000010 HAAS MA2100 10
WHERE empno = e#) 000010 HAAS MA2110 10
,projno AS projno 000020 THOMPSON PL2100 30
,actno AS act 000030 KWAN IF1000 10
FROM OLD TABLE
(DELETE
FROM emp_act
WHERE empno < '0001')
FETCH FIRST 5 ROWS ONLY;
Figure 174, Join result to another table
Figure 175, MERGE statement syntax
CREATE TABLE old_staff AS OLD_STAFF NEW_STAFF
(SELECT id, job, salary +-----------------+ +----------+
FROM staff) |ID|JOB |SALARY | |ID|SALARY |
WITH NO DATA; |--|-----|--------| |--|-------|
|20|Sales|18171.25| |30|1750.67|
CREATE TABLE new_staff AS |30|Mgr |17506.75| |40|1800.60|
(SELECT id, salary |40|Sales|18006.00| |50|2065.98|
FROM staff) +-----------------+ +----------+
WITH NO DATA;
INSERT INTO old_staff INSERT INTO new_staff
SELECT id, job, salary SELECT id, salary / 10
FROM staff FROM staff
WHERE id BETWEEN 20 and 40; WHERE id BETWEEN 30 and 50;
Figure 176, Sample tables for merge
MERGE INTO old_staff oo OLD_STAFF NEW_STAFF
USING new_staff nn +-----------------+ +----------+
ON oo.id = nn.id |ID|JOB |SALARY | |ID|SALARY |
WHEN MATCHED THEN |--|-----|--------| |--|-------|
UPDATE |20|Sales|18171.25| |30|1750.67|
SET oo.salary = nn.salary |30|Mgr |17506.75| |40|1800.60|
WHEN NOT MATCHED THEN |40|Sales|18006.00| |50|2065.98|
INSERT +-----------------+ +----------+
VALUES (nn.id,'?',nn.salary);
AFTER-MERGE
=================
ID JOB SALARY
-- ----- --------
20 Sales 18171.25
30 Mgr 1750.67
40 Sales 1800.60
50 ? 2065.98
Figure 177, Merge - do update or insert
MERGE INTO old_staff oo AFTER-MERGE
USING new_staff nn =================
ON oo.id = nn.id ID JOB SALARY
WHEN MATCHED THEN -- ----- --------
DELETE; 20 Sales 18171.25
Figure 178, Merge - delete if match
MERGE INTO old_staff oo OLD_STAFF NEW_STAFF
USING new_staff nn +-----------------+ +----------+
ON oo.id = nn.id |ID|JOB |SALARY | |ID|SALARY |
WHEN MATCHED |--|-----|--------| |--|-------|
AND oo.salary < 18000 THEN |20|Sales|18171.25| |30|1750.67|
UPDATE |30|Mgr |17506.75| |40|1800.60|
SET oo.salary = nn.salary |40|Sales|18006.00| |50|2065.98|
WHEN MATCHED +-----------------+ +----------+
AND oo.salary > 18000 THEN
DELETE AFTER-MERGE
WHEN NOT MATCHED =================
AND nn.id > 10 THEN ID JOB SALARY
INSERT -- ----- --------
VALUES (nn.id,'?',nn.salary) 20 Sales 18171.25
WHEN NOT MATCHED THEN 30 Mgr 1750.67
SIGNAL SQLSTATE '70001' 50 ? 2065.98
SET MESSAGE_TEXT = 'New ID <= 10';
Figure 179, Merge with multiple options
MERGE INTO old_staff AFTER-MERGE
USING =================
(SELECT MAX(id) + 1 AS max_id ID JOB SALARY
,MAX(job) AS max_job -- ----- --------
,MAX(salary) AS max_sal 20 Sales 18171.25
FROM old_staff 30 Mgr 17506.75
)AS mx 40 Sales 18006.00
ON id = max_id 41 Sales 18171.25
WHEN NOT MATCHED THEN
INSERT
VALUES (max_id, max_job, max_sal);
Figure 180, Merge MAX row into table
INSERT INTO old_staff
SELECT MAX(id) + 1 AS max_id
,MAX(job) AS max_job
,MAX(salary) AS max_sal
FROM old_staff;
Figure 181, Merge logic - done using insert
MERGE INTO OLD_STAFF NEW_STAFF
(SELECT * +-----------------+ +----------+
FROM old_staff |ID|JOB |SALARY | |ID|SALARY |
WHERE id < 40 |--|-----|--------| |--|-------|
)AS oo |20|Sales|18171.25| |30|1750.67|
USING |30|Mgr |17506.75| |40|1800.60|
(SELECT * |40|Sales|18006.00| |50|2065.98|
FROM new_staff +-----------------+ +----------+
WHERE id < 50
)AS nn AFTER-MERGE
ON oo.id = nn.id =================
WHEN MATCHED THEN ID JOB SALARY
DELETE -- ----- --------
WHEN NOT MATCHED THEN 20 Sales 18171.25
INSERT 40 ? 1800.60
VALUES (nn.id,'?',nn.salary); 40 Sales 18006.00
Figure 182, Merge using two full-selects
MERGE INTO old_staff oo AFTER-MERGE
USING new_staff nn =================
ON oo.id = nn.id ID JOB SALARY
WHEN MATCHED THEN -- ----- --------
UPDATE 20 Sales 18171.25
SET (salary,job) = (1234,'?') 30 ? 1234.00
WHEN NOT MATCHED THEN 40 ? 1234.00
INSERT (id,salary,job) 50 ? 5678.90
VALUES (id,5678.9,'?');
Figure 183, Listing columns and values in insert
Figure 184, Compound SQL Statement syntax
BEGIN ATOMIC
DECLARE cntr SMALLINT DEFAULT 1;
FOR V1 AS
SELECT id as idval
FROM staff
WHERE id < 80
ORDER BY id
DO
UPDATE staff
SET comm = cntr
WHERE id = idval;
SET cntr = cntr + 1;
END FOR;
END
Figure 185, Sample Compound SQL statement
--#SET DELIMITER !
SELECT NAME FROM STAFF WHERE ID = 10!
--#SET DELIMITER ;
SELECT NAME FROM STAFF WHERE ID = 20;
Figure 186, Set Delimiter example
BEGIN ATOMIC
DECLARE aaa, bbb, ccc SMALLINT DEFAULT 1;
DECLARE ddd CHAR(10) DEFAULT NULL;
DECLARE eee INTEGER;
SET eee = aaa + 1;
UPDATE staff
SET comm = aaa
,salary = bbb
,years = eee
WHERE id = 10;
END
Figure 187, DECLARE examples
Figure 188, FOR statement syntax
BEGIN ATOMIC
FOR V1 AS
SELECT dept AS dname
,max(id) AS max_id
FROM staff
GROUP BY dept
HAVING COUNT(*) > 1
ORDER BY dept
DO
UPDATE staff
SET id = id * -1
WHERE id = max_id;
UPDATE staff
set dept = dept / 10
WHERE dept = dname
AND dept < 30;
END FOR;
END
Figure 189, FOR statement example
Figure 190, GET DIAGNOSTICS statement syntax
BEGIN ATOMIC
DECLARE numrows INT DEFAULT 0;
UPDATE staff
SET salary = 12345
WHERE ID < 100;
GET DIAGNOSTICS numrows = ROW_COUNT;
UPDATE staff
SET salary = numrows
WHERE ID = 10;
END
Figure 191, GET DIAGNOSTICS statement example
Figure 192, IF statement syntax
BEGIN ATOMIC
DECLARE cur INT;
SET cur = MICROSECOND(CURRENT TIMESTAMP);
IF cur > 600000 THEN
UPDATE staff
SET name = CHAR(cur)
WHERE id = 10;
ELSEIF cur > 300000 THEN
UPDATE staff
SET name = CHAR(cur)
WHERE id = 20;
ELSE
UPDATE staff
SET name = CHAR(cur)
WHERE id = 30;
END IF;
END
Figure 193, IF statement example
Figure 194, ITERATE statement syntax
BEGIN ATOMIC
DECLARE cntr INT DEFAULT 0;
whileloop:
WHILE cntr < 60 DO
SET cntr = cntr + 10;
UPDATE staff
SET salary = cntr
WHERE id = cntr;
ITERATE whileloop;
UPDATE staff
SET comm = cntr + 1
WHERE id = cntr;
END WHILE;
END
Figure 195, ITERATE statement example
Figure 196, LEAVE statement syntax
BEGIN ATOMIC
DECLARE cntr INT DEFAULT 1;
whileloop:
WHILE 1 <> 2 DO
SET cntr = cntr + 1;
IF RAND() > 0.99 THEN
LEAVE whileloop;
END IF;
END WHILE;
UPDATE staff
SET salary = cntr
WHERE ID = 10;
END
Figure 197, LEAVE statement example
Figure 198, SIGNAL statement syntax
BEGIN ATOMIC
DECLARE cntr INT DEFAULT 1;
DECLARE emsg CHAR(20);
whileloop:
WHILE RAND() < .99 DO
SET cntr = cntr + 1;
END WHILE;
SET emsg = '#loops: ' || CHAR(cntr);
SIGNAL SQLSTATE '75001' SET MESSAGE_TEXT = emsg;
END
Figure 199, SIGNAL statement example
Figure 200, WHILE statement syntax
BEGIN ATOMIC
DECLARE c1, C2 INT DEFAULT 1;
WHILE c1 < 10 DO
WHILE c2 < 20 DO
SET c2 = c2 + 1;
END WHILE;
SET c1 = c1 + 1;
END WHILE;
UPDATE staff
SET salary = c1
,comm = c2
WHERE id = 10;
END
Figure 201, WHILE statement example
SELECT dept ANSWER
,count(*) as #rows ==========
FROM staff DEPT #ROWS
GROUP BY dept ---- -----
ORDER BY dept; 10 4
15 4
20 4
38 5
42 4
51 5
66 5
84 4
Figure 202, List departments in STAFF table
--#SET DELIMITER ! IMPORTANT
============
CREATE TABLE dpt This example
(dept SMALLINT NOT NULL uses an "!"
,#names SMALLINT as the stmt
,PRIMARY KEY(dept))! delimiter.
COMMIT!
CREATE TRIGGER dpt1 AFTER INSERT ON dpt
REFERENCING NEW AS NNN
FOR EACH ROW
MODE DB2SQL
BEGIN ATOMIC
DECLARE namecnt SMALLINT DEFAULT 0;
FOR getnames AS
SELECT COUNT(*) AS #n
FROM staff
WHERE dept = nnn.dept
DO
SET namecnt = #n;
END FOR;
UPDATE dpt
SET #names = namecnt
WHERE dept = nnn.dept; ANSWER
END! ===========
COMMIT! DEPT #NAMES
---- ------
INSERT INTO dpt (dept) 10 4
SELECT DISTINCT dept 15 4
FROM staff! 20 4
COMMIT! 38 5
42 4
SELECT * 51 5
FROM dpt 66 5
ORDER BY dept! 84 4
Figure 203, Trigger with compound SQL
--#SET DELIMITER ! IMPORTANT
============
CREATE FUNCTION dpt1 (deptin SMALLINT) This example
RETURNS SMALLINT uses an "!"
BEGIN ATOMIC as the stmt
DECLARE num_names SMALLINT; delimiter.
FOR getnames AS
SELECT COUNT(*) AS #n
FROM staff
WHERE dept = deptin
DO
SET num_names = #n;
END FOR; ANSWER
RETURN num_names; ===========
END! DEPT #NAMES
COMMIT! ---- ------
10 4
SELECT XXX.* 15 4
,dpt1(dept) as #names 20 4
FROM (SELECT dept 38 5
FROM staff 42 4
GROUP BY dept 51 5
)AS XXX 66 5
ORDER BY dept! 84 4
Figure 204, Scalar Function with compound SQL
--#SET DELIMITER ! IMPORTANT
============
CREATE FUNCTION dpt1 (deptin SMALLINT) This example
RETURNS SMALLINT uses an "!"
BEGIN ATOMIC as the stmt
RETURN delimiter.
SELECT COUNT(*)
FROM staff
WHERE dept = deptin;
END!
COMMIT!
SELECT XXX.*
,dpt1(dept) as #names
FROM (SELECT dept
FROM staff
GROUP BY dept
)AS XXX
ORDER BY dept!
Figure 205, Scalar Function with compound SQL
--#SET DELIMITER ! IMPORTANT
============
CREATE FUNCTION dpt2 () This example
RETURNS TABLE (dept SMALLINT uses an "!"
,#names SMALLINT) as the stmt
BEGIN ATOMIC delimiter.
RETURN
SELECT dept
,count(*) ANSWER
FROM staff ===========
GROUP BY dept DEPT #NAMES
ORDER BY dept; ---- ------
END! 10 4
COMMIT! 15 4
20 4
--#SET DELIMITER ; 38 5
42 4
SELECT * 51 5
FROM TABLE(dpt2()) T1 66 5
ORDER BY dept; 84 4
Figure 206, Table Function with compound SQL
Figure 207, AVG function syntax
SELECT AVG(dept) AS a1 ANSWER
,AVG(ALL dept) AS a2 ==============
,AVG(DISTINCT dept) AS a3 A1 A2 A3 A4 A5
,AVG(dept/10) AS a4 -- -- -- -- --
,AVG(dept)/10 AS a5 41 41 40 3 4
FROM staff
HAVING AVG(dept) > 40;
Figure 208, AVG function examples
UPDATE staff
SET comm = 0
WHERE comm IS NULL;
SELECT AVG(salary) AS salary ANSWER
,AVG(comm) AS comm1 ===================
,AVG(CASE comm SALARY COMM1 COMM2
WHEN 0 THEN NULL ------- ----- -----
ELSE comm 16675.6 351.9 513.3
END) AS comm2
FROM staff;
UPDATE staff
SET comm = NULL
WHERE comm = 0;
Figure 209, Convert zero to null before doing AVG
SELECT COUNT(*) AS c1 ANSWER
,AVG(salary) AS a1 ===========
,COALESCE(AVG(salary),0) AS a2 C1 A1 A2 A3
,CASE -- -- -- --
WHEN AVG(salary) IS NULL THEN 0 0 - 0 0
ELSE AVG(salary)
END AS a3
FROM staff
WHERE id < 10;
Figure 210, Convert null output (from AVG) to zero
SELECT AVG(DAYS(birthdate)) ANSWER
,DATE(AVG(DAYS(birthdate))) =================
FROM employee; 1 2
------ ----------
709113 1942-06-27
Figure 211, AVG of date column
SELECT AVG(avg_sal) AS avg_avg ANSWER
FROM (SELECT dept ================
,AVG(salary) AS avg_sal
FROM staff
GROUP BY dept
)AS xxx;
Figure 212, Select average of average
Figure 213, CORRELATION function syntax
WITH temp1(col1, col2, col3, col4) AS ANSWER
(VALUES (0 , 0 , 0 , RAND(1)) ===========================
UNION ALL COR11 COR12 COR23 COR34
SELECT col1 + 1 ------ ------ ------ ------
,col2 - 1 1.000 -1.000 -0.017 -0.005
,RAND()
,RAND()
FROM temp1
WHERE col1 <= 1000
)
SELECT DEC(CORRELATION(col1,col1),5,3) AS cor11
,DEC(CORRELATION(col1,col2),5,3) AS cor12
,DEC(CORRELATION(col2,col3),5,3) AS cor23
,DEC(CORRELATION(col3,col4),5,3) AS cor34
FROM temp1;
Figure 214, CORRELATION function examples
Figure 215, COUNT function syntax
SELECT COUNT(*) AS c1 ANSWER
,COUNT(INT(comm/10)) AS c2 =================
,COUNT(ALL INT(comm/10)) AS c3 C1 C2 C3 C4 C5 C6
,COUNT(DISTINCT INT(comm/10)) AS c4 -- -- -- -- -- --
,COUNT(DISTINCT INT(comm)) AS c5 35 24 24 19 24 2
,COUNT(DISTINCT INT(comm))/10 AS c6
FROM staff;
Figure 216, COUNT function examples
SELECT 'NO GP-BY' AS c1 ANSWER
,COUNT(*) AS c2 ============
FROM staff C1 C2
WHERE id = -1 -------- --
UNION NO GP-BY 0
SELECT 'GROUP-BY' AS c1
,COUNT(*) AS c2
FROM staff
WHERE id = -1
GROUP BY dept;
Figure 217, COUNT function with and without GROUP BY
Figure 218, COUNT_BIG function syntax
SELECT COUNT_BIG(*) AS c1 ANSWER
,COUNT_BIG(dept) AS c2 ===================
,COUNT_BIG(DISTINCT dept) AS c3 C1 C2 C3 C4 C5
,COUNT_BIG(DISTINCT dept/10) AS c4 --- --- --- --- ---
,COUNT_BIG(DISTINCT dept)/10 AS c5 35. 35. 8. 7. 0.
FROM STAFF;
Figure 219, COUNT_BIG function examples
Figure 220, COVARIANCE function syntax
WITH temp1(c1, c2, c3, c4) AS ANSWER
(VALUES (0 , 0 , 0 , RAND(1)) ===============================
UNION ALL COV11 COV12 COV23 COV34
SELECT c1 + 1 ------- ------- ------- -------
,c2 - 1 83666. -83666. -1.4689 -0.0004
,RAND()
,RAND()
FROM temp1
WHERE c1 <= 1000
)
SELECT DEC(COVARIANCE(c1,c1),6,0) AS cov11
,DEC(COVARIANCE(c1,c2),6,0) AS cov12
,DEC(COVARIANCE(c2,c3),6,4) AS cov23
,DEC(COVARIANCE(c3,c4),6,4) AS cov34
FROM temp1;
Figure 221, COVARIANCE function examples
Figure 222, GROUPING function syntax
SELECT dept ANSWER
,AVG(salary) AS salary ================
,GROUPING(dept) AS df DEPT SALARY DF
FROM staff ---- -------- --
GROUP BY ROLLUP(dept) 10 20865.86 0
ORDER BY dept; 15 15482.33 0
20 16071.52 0
38 15457.11 0
42 14592.26 0
51 17218.16 0
66 17215.24 0
84 16536.75 0
- 16675.64 1
Figure 223, GROUPING function example
Figure 224, MAX function syntax
SELECT MAX(dept) ANSWER
,MAX(ALL dept) ===============
,MAX(DISTINCT dept) 1 2 3 4
,MAX(DISTINCT dept/10) --- --- --- ---
FROM staff; 84 84 84 8
Figure 225, MAX function examples
SELECT MAX(hiredate) ANSWER
,CHAR(MAX(hiredate),USA) ================================
,MAX(CHAR(hiredate,USA)) 1 2 3
FROM employee; ---------- ---------- ----------
1980-09-30 09/30/1980 12/15/1976
Figure 226, MAX function with dates
SELECT MAX(id) AS id ANSWER
,MAX(CHAR(id)) AS chr ===================
,MAX(DIGITS(id)) AS dig ID CHR DIG
FROM staff; ------ ------ -----
350 90 00350
Figure 227, MAX function with numbers, 1 of 2
SELECT MAX(id - 250) AS id ANSWER
,MAX(CHAR(id - 250)) AS chr =====================
,MAX(DIGITS(id - 250)) AS dig ID CHR DIG
FROM staff; ----- ---- ----------
100 90 0000000240
Figure 228, MAX function with numbers, 2 of 2
Figure 229, MIN function syntax
SELECT MIN(dept) ANSWER
,MIN(ALL dept) ===============
,MIN(DISTINCT dept) 1 2 3 4
,MIN(DISTINCT dept/10) --- --- --- ---
FROM staff; 10 10 10 1
Figure 230, MIN function examples
Figure 231, REGRESSION functions syntax
ANSWERS
==========
SELECT DEC(REGR_SLOPE(bonus,salary) ,7,5) AS r_slope 0.01710
,DEC(REGR_INTERCEPT(bonus,salary),7,3) AS r_icpt 100.871
,INT(REGR_COUNT(bonus,salary) ) AS r_count 3
,INT(REGR_AVGX(bonus,salary) ) AS r_avgx 42833
,INT(REGR_AVGY(bonus,salary) ) AS r_avgy 833
,INT(REGR_SXX(bonus,salary) ) AS r_sxx 296291666
,INT(REGR_SXY(bonus,salary) ) AS r_sxy 5066666
,INT(REGR_SYY(bonus,salary) ) AS r_syy 86666
FROM employee
WHERE workdept = 'A00';
Figure 232, REGRESSION functions examples
Figure 233, STDDEV function syntax
ANSWER
===============================
A1 S1 S2 S3 S4
-- ------------- ---- ---- ----
SELECT AVG(dept) AS a1 41 +2.3522355E+1 23.5 23.5 24.1
,STDDEV(dept) AS s1
,DEC(STDDEV(dept),3,1) AS s2
,DEC(STDDEV(ALL dept),3,1) AS s3
,DEC(STDDEV(DISTINCT dept),3,1) AS s4
FROM staff;
Figure 234, STDDEV function examples
Figure 235, SUM function syntax
SELECT SUM(dept) AS s1 ANSWER
,SUM(ALL dept) AS s2 ========================
,SUM(DISTINCT dept) AS s3 S1 S2 S3 S4 S5
,SUM(dept/10) AS s4 ---- ---- ---- ---- ----
,SUM(dept)/10 AS s5 1459 1459 326 134 145
FROM staff;
Figure 236, SUM function examples
Figure 237, VARIANCE function syntax
ANSWER
==============================
A1 V1 V2 V3 V4
-- --------------- --- --- ---
SELECT AVG(dept) AS a1 41 +5.533012244E+2 553 553 582
,VARIANCE(dept) AS s1
,DEC(VARIANCE(dept),4,1) AS s2
,DEC(VARIANCE(ALL dept),4,1) AS s3
,DEC(VARIANCE(DISTINCT dept),4,1) AS s4
FROM staff;
Figure 238, VARIANCE function examples
SELECT s1.job, s1.id, s1.salary ANSWER
FROM staff s1 =================
WHERE s1.name LIKE '%s%' JOB ID SALARY
AND s1.id < 90 ----- -- --------
ORDER BY s1.job Clerk 80 13504.60
,s1.id; Mgr 10 18357.50
Mgr 50 20659.80
Figure 239, Select rows from STAFF table
SELECT s1.job, s1.id, s1.salary
,SUM(salary) OVER(ORDER BY job, id) AS sumsal
,ROW_NUMBER() OVER(ORDER BY job, id) AS r ANSWER
FROM staff s1 ======
WHERE s1.name LIKE '%s%' JOB ID SALARY SUMSAL R
AND s1.id < 90 ----- -- -------- -------- -
ORDER BY s1.job Clerk 80 13504.60 13504.60 1
,s1.id; Mgr 10 18357.50 31862.10 2
Mgr 50 20659.80 52521.90 3
Figure 240, Using OLAP functions to get additional fields
SELECT s1.job, s1.id, s1.salary
,xx.sumsal, xx.r
FROM staff s1
,TABLE
(SELECT SUM(s2.salary) AS sumsal
,COUNT(*) AS r
FROM staff s2
WHERE s2.name LIKE '%s%'
AND s2.id < 90
AND (s2.job < s1.job
OR (s2.job = s1.job ANSWER
AND s2.id <= s1.id)) ============================
)AS xx JOB ID SALARY SUMSAL R
WHERE s1.name LIKE '%s%' ----- -- -------- -------- -
AND s1.id < 90 Clerk 80 13504.60 13504.60 1
ORDER BY s1.job Mgr 10 18357.50 31862.10 2
,s1.id; Mgr 50 20659.80 52521.90 3
Figure 241, Using Nested Table Expression to get additional fields
SELECT s1.job, s1.id, s1.salary ANSWER
,SUM(s2.salary) AS sumsal ============================
,COUNT(*) AS r JOB ID SALARY SUMSAL R
FROM staff s1 ----- -- -------- -------- -
,staff s2 Clerk 80 13504.60 13504.60 1
WHERE s1.name LIKE '%s%' Mgr 10 18357.50 31862.10 2
AND s1.id < 90 Mgr 50 20659.80 52521.90 3
AND s2.name LIKE '%s%'
AND s2.id < 90
AND (s2.job < s1.job
OR (s2.job = s1.job
AND s2.id <= s1.id))
GROUP BY s1.job
,s1.id
,s1.salary
ORDER BY s1.job
,s1.id;
Figure 242, Using Self-Join and Group By to get additional fields
SELECT s1.job, s1.id, s1.salary
,(SELECT SUM(s2.salary)
FROM staff s2
WHERE s2.name LIKE '%s%'
AND s2.id < 90
AND (s2.job < s1.job
OR (s2.job = s1.job
AND s2.id <= s1.id))) AS sumsal
,(SELECT COUNT(*)
FROM staff s3
WHERE s3.name LIKE '%s%'
AND s3.id < 90
AND (s3.job < s1.job
OR (s3.job = s1.job
AND s3.id <= s1.id))) AS r
FROM staff s1
WHERE s1.name LIKE '%s%' ANSWER
AND s1.id < 90 ============================
ORDER BY s1.job JOB ID SALARY SUMSAL R
,s1.id; ----- -- -------- -------- -
Clerk 80 13504.60 13504.60 1
Mgr 10 18357.50 31862.10 2
Mgr 50 20659.80 52521.90 3
Figure 243, Using Nested Table Expressions in Select to get additional fields
SELECT dpt.deptname
,emp.empno
,emp.lastname
,emp.salary
,SUM(salary) OVER(ORDER BY dpt.deptname ASC
,emp.salary DESC
,emp.empno ASC) AS sumsal
,ROW_NUMBER() OVER(ORDER BY dpt.deptname ASC
,emp.salary DESC
,emp.empno ASC) AS row#
FROM employee emp
,department dpt
WHERE emp.firstnme LIKE '%S%'
AND emp.workdept = dpt.deptno
AND dpt.admrdept LIKE 'A%'
AND NOT EXISTS
(SELECT *
FROM emp_act eat
WHERE emp.empno = eat.empno
AND eat.emptime > 10)
ORDER BY dpt.deptname ASC
,emp.salary DESC
,emp.empno ASC;
Figure 244, Complicated query using OLAP functions
Figure 245, Ranking Functions syntax
SELECT id
,years
,salary
,RANK() OVER(ORDER BY years) AS rank#
,DENSE_RANK() OVER(ORDER BY years) AS dense#
,ROW_NUMBER() OVER(ORDER BY years) AS row#
FROM staff
WHERE id < 100
AND years IS NOT NULL ANSWER
ORDER BY years; ===================================
ID YEARS SALARY RANK# DENSE# ROW#
-- ----- -------- ----- ------ ----
30 5 17506.75 1 1 1
40 6 18006.00 2 2 2
90 6 18001.75 2 2 3
10 7 18357.50 4 3 4
70 7 16502.83 4 3 5
20 8 18171.25 6 4 6
50 10 20659.80 7 5 7
Figure 246, Ranking functions example
SELECT job
,years
,id
,name
,SMALLINT(RANK() OVER(ORDER BY job ASC)) AS asc1
,SMALLINT(RANK() OVER(ORDER BY job ASC
,years ASC)) AS asc2
,SMALLINT(RANK() OVER(ORDER BY job ASC
,years ASC
,id ASC)) AS asc3
,SMALLINT(RANK() OVER(ORDER BY job DESC)) AS dsc1
,SMALLINT(RANK() OVER(ORDER BY job DESC
,years DESC)) AS dsc2
,SMALLINT(RANK() OVER(ORDER BY job DESC
,years DESC
,id DESC)) AS Dsc3
,SMALLINT(RANK() OVER(ORDER BY job ASC
,years DESC
,id ASC)) AS mix1
,SMALLINT(RANK() OVER(ORDER BY job DESC
,years ASC
,id DESC)) AS mix2
FROM staff
WHERE id < 150
AND years IN (6,7)
AND job > 'L'
ORDER BY job
,years
,id;
ANSWER
================================================================
JOB YEARS ID NAME ASC1 ASC2 ASC3 DSC1 DSC2 DSC3 MIX1 MIX2
----- ----- --- ------- ---- ---- ---- ---- ---- ---- ---- ----
Mgr 6 140 Fraye 1 1 1 4 6 6 3 4
Mgr 7 10 Sanders 1 2 2 4 4 5 1 6
Mgr 7 100 Plotz 1 2 3 4 4 4 2 5
Sales 6 40 O'Brien 4 4 4 1 2 3 5 2
Sales 6 90 Koonitz 4 4 5 1 2 2 6 1
Sales 7 70 Rothman 4 6 6 1 1 1 4 3
Figure 247, ORDER BY usage
SELECT id
,years AS yr
,salary
,DENSE_RANK() OVER(ORDER BY years ASC) AS a
,DENSE_RANK() OVER(ORDER BY years ASC NULLS FIRST) AS af
,DENSE_RANK() OVER(ORDER BY years ASC NULLS LAST ) AS al
,DENSE_RANK() OVER(ORDER BY years DESC) AS d
,DENSE_RANK() OVER(ORDER BY years DESC NULLS FIRST) AS df
,DENSE_RANK() OVER(ORDER BY years DESC NULLS LAST ) AS dl
FROM staff
WHERE id < 100
ORDER BY years ANSWER
,salary; ==================================
ID YR SALARY A AF AL D DF DL
-- -- -------- -- -- -- -- -- --
30 5 17506.75 1 2 1 6 6 5
90 6 18001.75 2 3 2 5 5 4
40 6 18006.00 2 3 2 5 5 4
70 7 16502.83 3 4 3 4 4 3
10 7 18357.50 3 4 3 4 4 3
20 8 18171.25 4 5 4 3 3 2
50 10 20659.80 5 6 5 2 2 1
80 - 13504.60 6 1 6 1 1 6
60 - 16808.30 6 1 6 1 1 6
Figure 248, Overriding the default null ordering sequence
SELECT COUNT(DISTINCT years) AS y#1
,MAX(y#) AS y#2
FROM (SELECT years
,DENSE_RANK() OVER(ORDER BY years) AS y#
FROM staff
WHERE id < 100
)AS xxx ANSWER
ORDER BY 1; =======
Y#1 Y#2
--- ---
5 6
Figure 249, Counting distinct values - comparison
SELECT id ANSWER
,years AS yr =================
,salary ID YR SALARY R1
,RANK() OVER(PARTITION BY years -- -- -------- --
ORDER BY salary) AS r1 30 5 17506.75 1
FROM staff 40 6 18006.00 1
WHERE id < 80 70 7 16502.83 1
AND years IS NOT NULL 10 7 18357.50 2
ORDER BY years 20 8 18171.25 1
,salary; 50 0 20659.80 1
Figure 250, Values ranked by subset of rows
SELECT id
,years
,salary
,SMALLINT(RANK() OVER(ORDER BY years ASC)) AS rank_a
,SMALLINT(RANK() OVER(ORDER BY years DESC)) AS rank_d
,SMALLINT(RANK() OVER(ORDER BY id, years)) AS rank_iy
FROM STAFF
WHERE id < 100
AND years IS NOT NULL
ORDER BY years;
Figure 251, Multiple rankings in same query
SELECT id
,years
,name
,salary
,SMALLINT(RANK() OVER(ORDER BY SUBSTR(name,3,2))) AS dumb1
,SMALLINT(RANK() OVER(ORDER BY salary / 1000)) AS dumb2
,SMALLINT(RANK() OVER(ORDER BY years * ID)) AS dumb3
,SMALLINT(RANK() OVER(ORDER BY rand())) AS dumb4
,SMALLINT(RANK() OVER(ORDER BY 1)) AS dumb5
FROM staff
WHERE id < 40
AND years IS NOT NULL
ORDER BY 1;
Figure 252, Dumb rankings, SQL
ID YEARS NAME SALARY DUMB1 DUMB2 DUMB3 DUMB4 DUMB5
-- ----- -------- -------- ----- ----- ----- ----- -----
10 7 Sanders 18357.50 1 3 1 1 1
20 8 Pernal 18171.25 3 2 3 3 1
30 5 Marenghi 17506.75 2 1 2 2 1
Figure 253, Dumb ranking, Answer
SELECT xxx.* ANSWER
,RANK()OVER(ORDER BY id) AS r2 ================
FROM (SELECT id ID NAME R1 R2
,name -- ------- -- --
,RANK() OVER(ORDER BY id) AS r1 40 O'Brien 4 1
FROM staff 50 Hanes 5 2
WHERE id < 100 70 Rothman 6 3
AND years IS NOT NULL 90 Koonitz 7 4
)AS xxx
WHERE id > 30
ORDER BY id;
Figure 254, Subsequent processing of ranked data
SELECT id ANSWER
,RANK() OVER(PARTITION BY dept =================
ORDER BY salary DESC) AS r1 ID R1 SALARY DP
,salary -- -- -------- --
,dept AS dp 50 1 20659.80 15
FROM staff 10 1 18357.50 20
WHERE id < 80 40 1 18006.00 38
AND years IS NOT NULL 20 2 18171.25 20
ORDER BY r1 ASC 30 2 17506.75 38
,salary DESC; 70 2 16502.83 15
Figure 255, Ordering rows by rank, using RANK function
SELECT id ANSWER
,(SELECT COUNT(*) =================
FROM staff s2 ID R1 SALARY DP
WHERE s2.id < 80 -- -- -------- --
AND S2.YEARS IS NOT NULL 50 1 20659.80 15
AND s2.dept = s1.dept 10 1 18357.50 20
AND s2.salary >= s1.salary) AS R1 40 1 18006.00 38
,SALARY 20 2 18171.25 20
,dept AS dp 30 2 17506.75 38
FROM staff s1 70 2 16502.83 15
WHERE id < 80
AND years IS NOT NULL
ORDER BY r1 ASC
,salary DESC;
Figure 256, Ordering rows by rank, using sub-query
SELECT id ANSWER
,salary ==============
,dept AS dp ID SALARY DP
FROM (SELECT s1.* -- -------- --
,RANK() OVER(PARTITION BY dept 50 20659.80 15
ORDER BY salary DESC) AS r1 10 18357.50 20
FROM staff s1 40 18006.00 38
WHERE id < 80
AND years IS NOT NULL
)AS xxx
WHERE r1 = 1
ORDER BY dp;
Figure 257, Get highest salary in each department, use RANK function
SELECT id ANSWER
,salary ==============
,dept AS dp ID SALARY DP
FROM staff s1 -- -------- --
WHERE id < 80 50 20659.80 15
AND years IS NOT NULL 10 18357.50 20
AND NOT EXISTS 40 18006.00 38
(SELECT *
FROM staff s2
WHERE s2.id < 80
AND s2.years IS NOT NULL
AND s2.dept = s1.dept
AND s2.salary > s1.salary)
ORDER BY DP;
Figure 258, Get highest salary in each department, use correlated sub-query
SELECT id ANSWER
,salary ==============
,dept AS dp ID SALARY DP
FROM staff -- -------- --
WHERE id < 80 50 20659.80 15
AND years IS NOT NULL 10 18357.50 20
AND (dept, salary) IN 40 18006.00 38
(SELECT dept, MAX(salary)
FROM staff
WHERE id < 80
AND years IS NOT NULL
GROUP BY dept)
ORDER BY dp;
Figure 259, Get highest salary in each department, use uncorrelated sub-query
Figure 260, Numbering Function syntax
SELECT id ANSWER
,name =================
,ROW_NUMBER() OVER() AS r1 ID NAME R1 R2
,ROW_NUMBER() OVER(ORDER BY id) AS r2 -- -------- -- --
FROM staff 10 Sanders 1 1
WHERE id < 50 20 Pernal 2 2
AND years IS NOT NULL 30 Marenghi 3 3
ORDER BY id; 40 O'Brien 4 4
Figure 261, ORDER BY example, 1 of 3
SELECT id ANSWER
,name =================
,ROW_NUMBER() OVER() AS r1 ID NAME R1 R2
,ROW_NUMBER() OVER(ORDER BY name) AS r2 -- -------- -- --
FROM staff 10 Sanders 4 4
WHERE id < 50 20 Pernal 3 3
AND years IS NOT NULL 30 Marenghi 1 1
ORDER BY id; 40 O'Brien 2 2
Figure 262, ORDER BY example, 2 of 3
SELECT id ANSWER
,name ====================
,ROW_NUMBER() OVER() AS r1 ID NAME R1 R2 R3
,ROW_NUMBER() OVER(ORDER BY ID) AS r2 -- -------- -- -- --
,ROW_NUMBER() OVER(ORDER BY NAME) AS r3 10 Sanders 1 1 4
FROM staff 20 Pernal 2 2 3
WHERE id < 50 30 Marenghi 3 3 1
AND years IS NOT NULL 40 O'Brien 4 4 2
ORDER BY id;
Figure 263, ORDER BY example, 3 of 3
SELECT job
,years
,id
,name
,ROW_NUMBER() OVER(PARTITION BY job
ORDER BY years) AS row#
,RANK() OVER(PARTITION BY job
ORDER BY years) AS rn1#
,DENSE_RANK() OVER(PARTITION BY job
ORDER BY years) AS rn2#
FROM staff
WHERE id < 150
AND years IN (6,7) ANSWER
AND job > 'L' ======================================
ORDER BY job JOB YEARS ID NAME ROW# RN1# RN2#
,years; ----- ----- --- ------- ---- ---- ----
Mgr 6 140 Fraye 1 1 1
Mgr 7 10 Sanders 2 2 2
Mgr 7 100 Plotz 3 2 2
Sales 6 40 O'Brien 1 1 1
Sales 6 90 Koonitz 2 1 1
Sales 7 70 Rothman 3 3 2
Figure 264, Use of PARTITION phrase
SELECT * ANSWER
FROM (SELECT id =============
,name ID NAME R
,ROW_NUMBER() OVER(ORDER BY id) AS r -- -------- -
FROM staff 10 Sanders 1
WHERE id < 100 20 Pernal 2
AND years IS NOT NULL 30 Marenghi 3
)AS xxx
WHERE r <= 3
ORDER BY id;
Figure 265, Select first 3 rows, using ROW_NUMBER function
SELECT id ANSWER
,name =============
,ROW_NUMBER() OVER(ORDER BY id) AS r ID NAME R
FROM staff -- -------- -
WHERE id < 100 10 Sanders 1
AND years IS NOT NULL 20 Pernal 2
ORDER BY id 30 Marenghi 3
FETCH FIRST 3 ROWS ONLY;
Figure 266, Select first 3 rows, using FETCH FIRST notation
SELECT * ANSWER
FROM (SELECT id =============
,name ID NAME R
,ROW_NUMBER() OVER(ORDER BY id) AS r -- -------- -
FROM staff 30 Marenghi 3
WHERE id < 200 40 O'Brien 4
AND years IS NOT NULL 50 Hanes 5
)AS xxx 70 Rothman 6
WHERE r BETWEEN 3 AND 6
ORDER BY id;
Figure 267, Select 3rd through 6th rows
SELECT * ANSWER
FROM (SELECT id ==============
,name ID NAME R
,ROW_NUMBER() OVER(ORDER BY id) AS r --- ------- --
FROM staff 10 Sanders 1
WHERE id < 200 70 Rothman 6
AND years IS NOT NULL 140 Fraye 11
)AS xxx 190 Sneider 16
WHERE (r - 1) = ((r - 1) / 5) * 5
ORDER BY id;
Figure 268, Select every 5th matching row
SELECT *
FROM (SELECT id
,name
,ROW_NUMBER() OVER(ORDER BY id DESC) AS r
FROM staff
WHERE id < 200
AND years IS NOT NULL ANSWER
)AS xxx ==============
WHERE r <= 2 ID NAME R
ORDER BY id; --- -------- -
180 Abrahams 2
190 Sneider 1
Figure 269, Select last two rows
WITH
temp1(years, id, name, rnk, row) AS
(SELECT years
,id
,name
,RANK() OVER(ORDER BY years)
,ROW_NUMBER() OVER(ORDER BY years, id)
FROM staff
WHERE id < 200
AND years IS NOT NULL
),
temp2(rnk) AS
(SELECT rnk
FROM temp1
WHERE row = 3 ANSWER
) ==========================
SELECT temp1.* YEARS ID NAME RNK ROW
FROM temp1 ----- --- -------- --- ---
,temp2 3 180 Abrahams 1 1
WHERE temp1.rnk <= temp2.rnk 4 170 Kermisch 2 2
ORDER BY years 5 30 Marenghi 3 3
,id; 5 110 Ngan 3 4
Figure 270, Select first "n" rows, or more if needed
CREATE TABLE invoice
(inv# INTEGER NOT NULL
,customer# INTEGER NOT NULL
,sale_date DATE NOT NULL
,sale_value DECIMAL(9,2) NOT NULL
,CONSTRAINT ctx1 PRIMARY KEY (inv#)
,CONSTRAINT ctx2 CHECK(inv# >= 0));
Figure 271, Performance test table - definition
INSERT INTO invoice
WITH temp (n,m) AS
(VALUES (INTEGER(0),RAND(1))
UNION ALL
SELECT n+1, RAND()
FROM temp
WHERE n+1 < 500000
)
SELECT n AS inv#
,INT(m * 1000) AS customer#
,DATE('2000-11-01') + (m*40) DAYS AS sale_date
,DECIMAL((m * m * 100),8,2) AS sale_value
FROM temp;
Figure 272, Performance test table - insert 500,000 rows
SELECT s.*
FROM invoice s
ORDER BY inv#
FETCH FIRST 5 ROWS ONLY;
Figure 273, Fetch first 5 rows - 0.313 elapsed seconds
SELECT s.*
FROM invoice s
ORDER BY inv#
FETCH FIRST 5 ROWS ONLY
OPTIMIZE FOR 5 ROWS;
Figure 274, Fetch first 5 rows - 0.281 elapsed seconds
SELECT s.*
,ROW_NUMBER() OVER() AS row#
FROM invoice s
ORDER BY inv#
FETCH FIRST 5 ROWS ONLY;
Figure 275, Fetch first 5 rows+ number rows - 0.672 elapsed seconds
SELECT *
FROM (SELECT s.*
,ROW_NUMBER() OVER() AS row#
FROM invoice s
)xxx
WHERE row# <= 5
ORDER BY inv#;
Figure 276, Process and number 5 rows only - 0.000 elapsed seconds
SELECT *
FROM (SELECT s.*
,ROW_NUMBER() OVER(ORDER BY inv#) AS row#
FROM invoice s
)xxx
WHERE row# <= 5
ORDER BY inv#;
Figure 277, Process and number 5 rows only - 0.281 elapsed seconds
WITH temp (inv#, c#, sd, sv, n) AS
(SELECT inv.*
,1
FROM invoice inv
WHERE inv# =
(SELECT MIN(inv#)
FROM invoice)
UNION ALL
SELECT new.*, n + 1
FROM temp old
,invoice new
WHERE old.inv# < new.inv#
AND old.n < 5
AND new.inv# =
(SELECT MIN(xxx.inv#)
FROM invoice xxx
WHERE xxx.inv# > old.inv#)
)
SELECT *
FROM temp;
Figure 278, Fetch first 5 rows - 0.000 elapsed seconds
Figure 279, Aggregation Function syntax
SELECT id
,name
,salary
,SUM(salary) OVER() AS sum_sal
,AVG(salary) OVER() AS avg_sal
,MIN(salary) OVER() AS min_sal
,MAX(salary) OVER() AS max_sal
,COUNT(*) OVER() AS #rows
FROM staff
WHERE id < 60
ORDER BY id;
Figure 280, Aggregation function, basic usage, SQL
ID NAME SALARY SUM_SAL AVG_SAL MIN_SAL MAX_SAL #ROWS
-- -------- -------- -------- -------- -------- -------- -----
10 Sanders 18357.50 92701.30 18540.26 17506.75 20659.80 5
20 Pernal 18171.25 92701.30 18540.26 17506.75 20659.80 5
30 Marenghi 17506.75 92701.30 18540.26 17506.75 20659.80 5
40 O'Brien 18006.00 92701.30 18540.26 17506.75 20659.80 5
50 Hanes 20659.80 92701.30 18540.26 17506.75 20659.80 5
Figure 281, Aggregation function, basic usage, Answer
WITH
temp1 (id, name, salary) AS
(SELECT id, name, salary
FROM staff
WHERE id < 60
),
temp2 (sum_sal, avg_sal, min_sal, max_sal, #rows) AS
(SELECT SUM(salary)
,AVG(salary)
,MIN(salary)
,MAX(salary)
,COUNT(*)
FROM temp1
)
SELECT *
FROM temp1
,temp2
ORDER BY id;
Figure 282, Select detailed data, plus summary data
SELECT id
,name
,salary
,SUM(salary) OVER() AS sum1
,SUM(salary) OVER(ORDER BY id * 0) AS sum2
,SUM(salary) OVER(ORDER BY 'ABC') AS sum3
,SUM(salary) OVER(ORDER BY 'ABC'
RANGE BETWEEN UNBOUNDED PRECEDING
AND UNBOUNDED FOLLOWING) AS sum4
FROM staff
WHERE id < 60
ORDER BY id;
Figure 283, Logically equivalent aggregation functions, SQL
ID NAME SALARY SUM1 SUM2 SUM3 SUM4
-- -------- -------- -------- -------- -------- --------
10 Sanders 18357.50 92701.30 92701.30 92701.30 92701.30
20 Pernal 18171.25 92701.30 92701.30 92701.30 92701.30
30 Marenghi 17506.75 92701.30 92701.30 92701.30 92701.30
40 O'Brien 18006.00 92701.30 92701.30 92701.30 92701.30
50 Hanes 20659.80 92701.30 92701.30 92701.30 92701.30
Figure 284, Logically equivalent aggregation functions, Answer
SELECT dept
,name
,salary
,SUM(salary) OVER(ORDER BY dept) AS sum1
,SUM(salary) OVER(ORDER BY dept DESC) AS sum2
,SUM(salary) OVER(ORDER BY dept, NAME) AS sum3
,SUM(salary) OVER(ORDER BY dept DESC, name DESC) AS sum4
,COUNT(*) OVER(ORDER BY dept) AS row1
,COUNT(*) OVER(ORDER BY dept, NAME) AS row2
FROM staff
WHERE id < 60
ORDER BY dept
,name;
Figure 285, Aggregation function, order by usage, SQL
DEPT NAME SALARY SUM1 SUM2 SUM3 SUM4 ROW1 ROW2
---- -------- -------- -------- -------- -------- -------- ---- ----
15 Hanes 20659.80 20659.80 92701.30 20659.80 92701.30 1 1
20 Pernal 18171.25 57188.55 72041.50 38831.05 72041.50 3 2
20 Sanders 18357.50 57188.55 72041.50 57188.55 53870.25 3 3
38 Marenghi 17506.75 92701.30 35512.75 74695.30 35512.75 5 4
38 O'Brien 18006.00 92701.30 35512.75 92701.30 18006.00 5 5
Figure 286, Aggregation function, order by usage, Answer
SELECT dept
,name
,years
,SMALLINT(SUM(years) OVER(ORDER BY dept)) AS d
,SMALLINT(SUM(years) OVER(ORDER BY dept, name)) AS dn
,SMALLINT(SUM(years) OVER(ORDER BY dept, name
ROWS UNBOUNDED PRECEDING))AS dnu
,SMALLINT(SUM(years) OVER(ORDER BY dept, name
ROWS 3 PRECEDING)) AS dn3
,SMALLINT(SUM(years) OVER(ORDER BY dept, name
ROWS 1 PRECEDING)) AS dn1
,SMALLINT(SUM(years) OVER(ORDER BY dept, name
ROWS 0 PRECEDING)) AS dn0
,SMALLINT(SUM(years) OVER(ORDER BY dept, name
ROWS CURRENT ROW)) AS dnc
,SMALLINT(SUM(years) OVER(ORDER BY dept DESC, name DESC
ROWS 1 PRECEDING)) AS dnx
FROM staff
WHERE id < 100
AND years IS NOT NULL
ORDER BY dept
,name;
Figure 287, Starting ROWS usage. Implied end is current row, SQL
DEPT NAME YEARS D DN DNU DN3 DN1 DN0 DNC DNX
---- -------- ----- -- -- --- --- --- --- --- ---
15 Hanes 10 17 10 10 10 10 10 10 17
15 Rothman 7 17 17 17 17 17 7 7 15
20 Pernal 8 32 25 25 25 15 8 8 15
20 Sanders 7 32 32 32 32 15 7 7 12
38 Marenghi 5 43 37 37 27 12 5 5 11
38 O'Brien 6 43 43 43 26 11 6 6 12
42 Koonitz 6 49 49 49 24 12 6 6 6
Figure 288, Starting ROWS usage. Implied end is current row, Answer
SELECT dept
,name
,years
,SMALLINT(SUM(years) OVER(ORDER BY dept, name)) AS uc1
,SMALLINT(SUM(years) OVER(ORDER BY dept, name
ROWS UNBOUNDED PRECEDING)) AS uc2
,SMALLINT(SUM(years) OVER(ORDER BY dept, name
ROWS BETWEEN UNBOUNDED PRECEDING
AND CURRENT ROW)) AS uc3
,SMALLINT(SUM(years) OVER(ORDER BY dept, name
ROWS BETWEEN CURRENT ROW
AND CURRENT ROW)) AS cu1
,SMALLINT(SUM(years) OVER(ORDER BY dept, name
ROWS BETWEEN 1 PRECEDING
AND 1 FOLLOWING)) AS pf1
,SMALLINT(SUM(years) OVER(ORDER BY dept, name
ROWS BETWEEN 2 PRECEDING
AND 2 FOLLOWING)) AS pf2
,SMALLINT(SUM(years) OVER(ORDER BY dept, name
ROWS BETWEEN 3 PRECEDING
AND 3 FOLLOWING)) AS pf3
,SMALLINT(SUM(years) OVER(ORDER BY dept, name
ROWS BETWEEN CURRENT ROW
AND UNBOUNDED FOLLOWING)) AS cu1
,SMALLINT(SUM(years) OVER(ORDER BY dept, name
ROWS BETWEEN UNBOUNDED PRECEDING
AND UNBOUNDED FOLLOWING)) AS uu1
FROM staff
WHERE id < 100
AND years IS NOT NULL
ORDER BY dept
,name;
Figure 289, ROWS usage, with BETWEEN phrase, SQL
DEPT NAME YEARS UC1 UC2 UC3 CU1 PF1 PF2 PF3 CU1 UU1
---- -------- ----- --- --- --- --- --- --- --- --- ---
15 Hanes 10 10 10 10 10 17 25 32 49 49
15 Rothman 7 17 17 17 7 25 32 37 39 49
20 Pernal 8 25 25 25 8 22 37 43 32 49
20 Sanders 7 32 32 32 7 20 33 49 24 49
38 Marenghi 5 37 37 37 5 18 32 39 17 49
38 O'Brien 6 43 43 43 6 17 24 32 12 49
42 Koonitz 6 49 49 49 6 12 17 24 6 49
Figure 290, ROWS usage, with BETWEEN phrase, Answer
SELECT id
,name
,SMALLINT(SUM(id) OVER(ORDER BY id ASC
ROWS BETWEEN 1 PRECEDING
AND CURRENT ROW)) AS apc
,SMALLINT(SUM(id) OVER(ORDER BY id ASC
ROWS BETWEEN CURRENT ROW
AND 1 FOLLOWING)) AS acf
,SMALLINT(SUM(id) OVER(ORDER BY id DESC
ROWS BETWEEN 1 PRECEDING
AND CURRENT ROW)) AS dpc
,SMALLINT(SUM(id) OVER(ORDER BY id DESC
ROWS BETWEEN CURRENT ROW
AND 1 FOLLOWING)) AS dcf
FROM staff
WHERE id < 50
AND years IS NOT NULL ANSWER
ORDER BY id; ===========================
ID NAME APC ACF DPC DCF
-- -------- --- --- --- ---
10 Sanders 10 30 30 10
20 Pernal 30 50 50 30
30 Marenghi 50 70 70 50
40 O'Brien 70 40 40 70
Figure 291,BETWEEN and ORDER BY usage
ASC id (10,20,30,40)
READ ROWS, LEFT to RIGHT 1ST-ROW 2ND-ROW 3RD-ROW 4TH-ROW
========================== ======== ======== ======== ========
1 PRECEDING to CURRENT ROW 10=10 10+20=30 20+30=40 30+40=70
CURRENT ROW to 1 FOLLOWING 10+20=30 20+30=50 30+40=70 40 =40
DESC id (40,30,20,10)
READ ROWS, RIGHT to LEFT 1ST-ROW 2ND-ROW 3RD-ROW 4TH-ROW
========================== ======== ======== ======== ========
1 PRECEDING to CURRENT ROW 20+10=30 30+20=50 40+30=70 40 =40
CURRENT ROW to 1 FOLLOWING 10 =10 20+10=30 30+20=50 40+30=70
NOTE: Preceding row is always on LEFT of current row.
Following row is always on RIGHT of current row.
Figure 292, Explanation of query
SELECT dept
,name
,years
,SMALLINT(SUM(years) OVER(ORDER BY dept
ROWS BETWEEN 1 PRECEDING
AND CURRENT ROW)) AS row1
,SMALLINT(SUM(years) OVER(ORDER BY dept
ROWS BETWEEN 2 PRECEDING
AND CURRENT ROW)) AS row2
,SMALLINT(SUM(years) OVER(ORDER BY dept
RANGE BETWEEN 1 PRECEDING
AND CURRENT ROW)) AS rg01
,SMALLINT(SUM(years) OVER(ORDER BY dept
RANGE BETWEEN 10 PRECEDING
AND CURRENT ROW)) AS rg10
,SMALLINT(SUM(years) OVER(ORDER BY dept
RANGE BETWEEN 20 PRECEDING
AND CURRENT ROW)) AS rg20
,SMALLINT(SUM(years) OVER(ORDER BY dept
RANGE BETWEEN 10 PRECEDING
AND 20 FOLLOWING)) AS rg11
,SMALLINT(SUM(years) OVER(ORDER BY dept
RANGE BETWEEN CURRENT ROW
AND 20 FOLLOWING)) AS rg99
FROM staff
WHERE id < 100
AND years IS NOT NULL
ORDER BY dept
,name;
Figure 293, RANGE usage, SQL
DEPT NAME YEARS ROW1 ROW2 RG01 RG10 RG20 RG11 RG99
------ ------- ----- ---- ---- ---- ---- ---- ---- ----
15 Hanes 10 10 10 17 17 17 32 32
15 Rothman 7 17 17 17 17 17 32 32
20 Pernal 8 15 25 15 32 32 43 26
20 Sanders 7 15 22 15 32 32 43 26
38 Marengh 5 12 20 11 11 26 17 17
38 O'Brien 6 11 18 11 11 26 17 17
42 Koonitz 6 12 17 6 17 17 17 6
Figure 294, RANGE usage, Answer
SELECT dept
,name
,years
,SMALLINT(SUM(years) OVER(ORDER BY dept)) AS x
,SMALLINT(SUM(years) OVER(ORDER BY dept
ROWS 3 PRECEDING)) AS xo3
,SMALLINT(SUM(years) OVER(ORDER BY dept
ROWS BETWEEN 1 PRECEDING
AND 1 FOLLOWING)) AS xo11
,SMALLINT(SUM(years) OVER(PARTITION BY dept)) AS p
,SMALLINT(SUM(years) OVER(PARTITION BY dept
ORDER BY dept)) AS po
,SMALLINT(SUM(years) OVER(PARTITION BY dept
ORDER BY dept
ROWS 1 PRECEDING)) AS po1
,SMALLINT(SUM(years) OVER(PARTITION BY dept
ORDER BY dept
ROWS 3 PRECEDING)) AS po3
,SMALLINT(SUM(years) OVER(PARTITION BY dept
ORDER BY dept
ROWS BETWEEN 1 PRECEDING
AND 1 FOLLOWING)) AS po11
FROM staff
WHERE id BETWEEN 40 AND 120
AND years IS NOT NULL
ORDER BY dept
,name;
Figure 295, PARTITION usage, SQL
DEPT NAME YEARS X XO3 XO11 P PO PO1 PO3 PO11
----- ------- ----- ---- ---- ---- ---- ---- ---- ---- ----
15 Hanes 10 22 10 15 22 22 10 10 15
15 Ngan 5 22 15 22 22 22 15 15 22
15 Rothman 7 22 22 18 22 22 12 22 12
38 O'Brien 6 28 28 19 6 6 6 6 6
42 Koonitz 6 41 24 19 13 13 6 6 13
42 Plotz 7 41 26 13 13 13 13 13 13
Figure 296, PARTITION usage, Answer
SELECT dept ANSWER
,SUM(years) AS sum ================
,AVG(years) AS avg DEPT SUM AVG ROW
,COUNT(*) AS row ---- --- --- ---
FROM staff 15 22 7 3
WHERE id BETWEEN 40 AND 120 38 6 6 1
AND years IS NOT NULL 42 13 6 2
GROUP BY dept;
Figure 297, Sample query using GROUP BY
SELECT dept ANSWER
,SUM(years) OVER(PARTITION BY dept) AS sum =================
,AVG(years) OVER(PARTITION BY dept) AS avg DEPT SUM AVG ROW
,COUNT(*) OVER(PARTITION BY dept) AS row ----- --- --- ---
FROM staff 15 22 7 3
WHERE id BETWEEN 40 AND 120 15 22 7 3
AND years IS NOT NULL 15 22 7 3
ORDER BY dept; 38 6 6 1
42 13 6 2
42 13 6 2
Figure 298, Sample query using PARTITION
SELECT DISTINCT dept ANSWER
,SUM(years) OVER(PARTITION BY dept) AS sum =================
,AVG(years) OVER(PARTITION BY dept) AS avg DEPT SUM AVG ROW
,COUNT(*) OVER(PARTITION BY dept) AS row ----- --- --- ---
FROM staff 15 22 7 3
WHERE id BETWEEN 40 AND 120 38 6 6 1
AND years IS NOT NULL 42 13 6 2
ORDER BY dept;
Figure 299, Sample query using PARTITION and DISTINCT
CREATE VIEW scalar (d1,f1,s1,c1,v1,ts1,dt1,tm1,tc1) AS
WITH temp1 (n1, c1, t1) AS
(VALUES (-2.4,'ABCDEF','1996-04-22-23.58.58.123456')
,(+0.0,'ABCD ','1996-08-15-15.15.15.151515')
,(+1.8,'AB ','0001-01-01-00.00.00.000000'))
SELECT DECIMAL(n1,3,1)
,DOUBLE(n1)
,SMALLINT(n1)
,CHAR(c1,6)
,VARCHAR(RTRIM(c1),6)
,TIMESTAMP(t1)
,DATE(t1)
,TIME(t1)
,CHAR(t1)
FROM temp1;
Figure 300, Sample View DDL - Scalar functions
D1 F1 S1 C1 V1 TS1
------ --------- -- ------ ------ --------------------------
-2.4 -2.4e+000 -2 ABCDEF ABCDEF 1996-04-22-23.58.58.123456
0.0 0.0e+000 0 ABCD ABCD 1996-08-15-15.15.15.151515
1.8 1.8e+000 1 AB AB 0001-01-01-00.00.00.000000
DT1 TM1 TC1
---------- -------- --------------------------
1996-04-22 23:58:58 1996-04-22-23.58.58.123456
1996-08-15 15:15:15 1996-08-15-15.15.15.151515
0001-01-01 00:00:00 0001-01-01-00.00.00.000000
Figure 301, SCALAR view, contents (3 rows)
SELECT d1 AS d1 ANSWER (float output shortened)
,ABS(D1) AS d2 ================================
,f1 AS f1 D1 D2 F1 F2
,ABS(f1) AS f2 ---- --- ---------- ---------
FROM scalar; -2.4 2.4 -2.400e+0 2.400e+00
0.0 0.0 0.000e+0 0.000e+00
1.8 1.8 1.800e+0 1.800e+00
Figure 302, ABS function examples
SELECT c1 ANSWER
,ASCII(c1) AS ac1 ================
,ASCII(SUBSTR(c1,2)) AS ac2 C1 AC1 AC2
FROM scalar ------ --- ---
WHERE c1 = 'ABCDEF'; ABCDEF 65 66
Figure 303, ASCII function examples
WITH temp (big) AS ANSWER
(VALUES BIGINT(1) ====================
UNION ALL BIG
SELECT big * 256 --------------------
FROM temp 1
WHERE big < 1E16 256
) 65536
SELECT big 16777216
FROM temp; 4294967296
1099511627776
281474976710656
72057594037927936
Figure 304, BIGINT function example
WITH temp (f1) AS
(VALUES FLOAT(1.23456789)
UNION ALL
SELECT f1 * 100
FROM temp
WHERE f1 < 1E18
)
SELECT f1 AS float1
,DEC(f1,19) AS decimal1
,BIGINT(f1) AS bigint1
FROM temp;
Figure 305, Convert FLOAT to DECIMAL and BIGINT, SQL
FLOAT1 DECIMAL1 BIGINT1
---------------------- ------------------- --------------------
+1.23456789000000E+000 1. 1
+1.23456789000000E+002 123. 123
+1.23456789000000E+004 12345. 12345
+1.23456789000000E+006 1234567. 1234567
+1.23456789000000E+008 123456789. 123456788
+1.23456789000000E+010 12345678900. 12345678899
+1.23456789000000E+012 1234567890000. 1234567889999
+1.23456789000000E+014 123456789000000. 123456788999999
+1.23456789000000E+016 12345678900000000. 12345678899999996
+1.23456789000000E+018 1234567890000000000. 1234567889999999488
Figure 306, Convert FLOAT to DECIMAL and BIGINT, answer
Figure 307, BLOB function syntax
Figure 308, CEILING function syntax
SELECT d1 ANSWER (float output shortened)
,CEIL(d1) AS d2 ==================================
,f1 D1 D2 F1 F2
,CEIL(f1) AS f2 ---- ---- ---------- ----------
FROM scalar; -2.4 -2. -2.400E+0 -2.000E+0
0.0 0. +0.000E+0 +0.000E+0
1.8 2. +1.800E+0 +2.000E+0
Figure 309, CEIL function examples
Figure 310, CHAR function syntax
SELECT name ANSWER
,CHAR(name,3) =====================================
,comm NAME 2 COMM 4 5
,CHAR(comm) ------- --- ------- -------- --------
,CHAR(comm,'@') James Jam 128.20 00128.20 00128@20
FROM staff Koonitz Koo 1386.70 01386.70 01386@70
WHERE id BETWEEN 80 Plotz Plo - - -
AND 100
ORDER BY id;
Figure 311, CHAR function examples - characters and numbers
ANSWER
==========================================
INT CHAR_INT CHAR_FLT CHAR_DEC
-------- -------- ----------- ------------
WITH temp1 (n) AS 3 3 3.0E0 00000000003.
(VALUES (3) 9 9 9.0E0 00000000009.
UNION ALL 81 81 8.1E1 00000000081.
SELECT n * n 6561 6561 6.561E3 00000006561.
FROM temp1 43046721 43046721 4.3046721E7 00043046721.
WHERE n < 9000
)
SELECT n AS int
,CHAR(INT(n)) AS char_int
,CHAR(FLOAT(n)) AS char_flt
,CHAR(DEC(n)) AS char_dec
FROM temp1;
Figure 312, CHAR function examples - positive numbers
WITH temp1 (n1, n2) AS ANSWER
(VALUES (SMALLINT(+3) ===================================
,SMALLINT(-7)) N1 I1 I2 D1 D2
UNION ALL ------ ----- ------ ------- -------
SELECT n1 * n2 3 3 +3 00003. +00003.
,n2 -21 -21 -21 -00021. -00021.
FROM temp1 147 147 +147 00147. +00147.
WHERE n1 < 300 -1029 -1029 -1029 -01029. -01029.
) 7203 7203 +7203 07203. +07203.
SELECT n1
,CHAR(n1) AS i1
,CASE
WHEN n1 < 0 THEN CHAR(n1)
ELSE '+' CONCAT CHAR(n1)
END AS i2
,CHAR(DEC(n1)) AS d1
,CASE
WHEN n1 < 0 THEN CHAR(DEC(n1))
ELSE '+' CONCAT CHAR(DEC(n1))
END AS d2
FROM temp1;
Figure 313, Align CHAR function output - numbers
SELECT CHAR(hiredate,ISO) ANSWER
,CHAR(hiredate,USA) ================================
,CHAR(hiredate,EUR) 1 2 3
FROM employee ---------- ---------- ----------
WHERE lastname < 'C' 1972-02-12 02/12/1972 12.02.1972
ORDER BY 2; 1966-03-03 03/03/1966 03.03.1966
Figure 314, CHAR function examples - dates
SELECT d2 ANSWER
,CHAR(d2) AS cd2 ================
,DIGITS(d2) AS dd2 D2 CD2 DD2
FROM (SELECT DEC(d1,4,1) AS d2 ---- ------ ----
FROM scalar -2.4 -002.4 0024
)AS xxx 0.0 000.0 0000
ORDER BY 1; 1.8 001.8 0018
Figure 315, DIGITS vs. CHAR
SELECT 'A' AS "c" ANSWER
,ASCII('A') AS "c>n" =================
,CHR(ASCII('A')) AS "c>n>c" C C>N C>N>C NL
,CHR(333) AS "nl" - --- ----- --
FROM staff A 65 A ÿ
WHERE id = 10;
Figure 316, CHR function examples
SELECT c1 ANSWER
,CLOB(c1) AS cc1 ===================
,CLOB(c1,3) AS cc2 C1 CC1 CC2
FROM scalar; ------ ------ ---
ABCDEF ABCDEF ABC
ABCD ABCD ABC
AB AB AB
Figure 317, CLOB function examples
SELECT id ANSWER
,comm ==================
,COALESCE(comm,0) ID COMM 3
FROM staff -- ------ ------
WHERE id < 30 10 - 0.00
ORDER BY id; 20 612.45 612.45
Figure 318, COALESCE function example
WITH temp1(c1,c2,c3) AS ANSWER
(VALUES (CAST(NULL AS SMALLINT) ========
,CAST(NULL AS SMALLINT) CC1 CC2
,CAST(10 AS SMALLINT))) --- ---
SELECT COALESCE(c1,c2,c3) AS cc1 10 10
,CASE
WHEN c1 IS NOT NULL THEN c1
WHEN c2 IS NOT NULL THEN c2
WHEN c3 IS NOT NULL THEN c3
END AS cc2
FROM TEMP1;
Figure 319, COALESCE and equivalent CASE expression
SELECT COUNT(*) AS #rows ANSWER
,MIN(id) AS min_id ===================
,COALESCE(MIN(id),-1) AS ccc_id #ROWS MIN_ID CCC_ID
FROM staff ----- ------ ------
WHERE id < 5; 0 - -1
Figure 320, NOT NULL field returning null value
SELECT 'A' || 'B' ANSWER
,'A' CONCAT 'B' ===================
,CONCAT('A','B') 1 2 3 4 5
,'A' || 'B' || 'C' --- --- --- --- ---
,CONCAT(CONCAT('A','B'),'C') AB AB AB ABC ABC
FROM staff
WHERE id = 10;
Figure 321, CONCAT function examples
WITH temp1 (col1, col2) AS ANSWER
(VALUES ('A' , 'YYY') ===============
,('AE', 'OOO') COL1 COL2 COL3
,('AE', 'YYY') ---- ---- -----
) AE OOO AEOOO
SELECT col1 AE YYY AEYYY
,col2 A YYY AYYY
,col1 CONCAT col2 AS col3
FROM temp1
ORDER BY col3;
Figure 322, CONCAT used with ORDER BY - wrong output sequence
WITH temp1 (col1, col2) AS ANSWER
(VALUES ('A' , 'YYY') ===============
,('AE', 'OOO') COL1 COL2 COL3
,('AE', 'YYY') ---- ---- -----
) A YYY A YYY
SELECT col1 AE OOO AEOOO
,col2 AE YYY AEYYY
,CHAR(col1,2) CONCAT
CHAR(col2,3) AS col3
FROM temp1
ORDER BY col3;
Figure 323, CONCAT used with ORDER BY - correct output sequence
WITH temp1(n1) AS ANSWER
(VALUES (0) =======================
UNION ALL N1 RAN COS SIN
SELECT n1 + 10 -- ----- ----- -----
FROM temp1 0 0.000 1.000 0.000
WHERE n1 < 90) 10 0.174 0.984 0.173
SELECT n1 20 0.349 0.939 0.342
,DEC(RADIANS(n1),4,3) AS ran 30 0.523 0.866 0.500
,DEC(COS(RADIANS(n1)),4,3) AS cos 40 0.698 0.766 0.642
,DEC(SIN(RADIANS(n1)),4,3) AS sin 50 0.872 0.642 0.766
FROM temp1; 60 1.047 0.500 0.866
70 1.221 0.342 0.939
80 1.396 0.173 0.984
90 1.570 0.000 1.000
Figure 324, RADIAN, COS, and SIN functions example
Figure 325, DATE function syntax
SELECT ts1 ANSWER
,DATE(ts1) AS dt1 ======================================
FROM scalar; TS1 DT1
-------------------------- ----------
1996-04-22-23.58.58.123456 1996-04-22
1996-08-15-15.15.15.151515 1996-08-15
0001-01-01-00.00.00.000000 0001-01-01
Figure 326, DATE function example - timestamp input
WITH temp1(n1) AS ANSWER
(VALUES (000001) ===================
,(728000) N1 D1
,(730120)) ------- ----------
SELECT n1 1 0001-01-01
,DATE(n1) AS d1 728000 1994-03-13
FROM temp1; 730120 2000-01-01
Figure 327, DATE function example - numeric input
SELECT dt1 ANSWER
,DAY(dt1) AS day1 ================
FROM scalar DT1 DAY1
WHERE DAY(dt1) > 10; ---------- ----
1996-04-22 22
1996-08-15 15
Figure 328, DAY function examples
SELECT dt1 ANSWER
,DAY(dt1) AS day1 =========================
,dt1 -'1996-04-30' AS dur2 DT1 DAY1 DUR2 DAY2
,DAY(dt1 -'1996-04-30') AS day2 ---------- ---- ---- ----
FROM scalar 1996-04-22 22 -8. -8
WHERE DAY(dt1) > 10 1996-08-15 15 315. 15
ORDER BY dt1;
Figure 329, DAY function, using date-duration input
SELECT dt1 ANSWER
,DAYNAME(dt1) AS dy1 ========================
,LENGTH(DAYNAME(dt1)) AS dy2 DT1 DY1 DY2
FROM scalar ---------- ------- ---
WHERE DAYNAME(dt1) LIKE '%a%y' 0001-01-01 Monday 6
ORDER BY dt1; 1996-04-22 Monday 6
1996-08-15 Thursday 8
Figure 330, DAYNAME function example
SELECT dt1 ANSWER
,DAYOFWEEK(dt1) AS dwk =========================
,DAYNAME(dt1) AS dnm DT1 DWK DNM
FROM scalar ---------- --- --------
ORDER BY dwk 0001-01-01 2 Monday
,dnm; 1996-04-22 2 Monday
1996-08-15 5 Thursday
Figure 331, DAYOFWEEK function example
WITH ANSWER
temp1 (n) AS ========================
(VALUES (0) DATE DAY W D WI I
UNION ALL ---------- --- -- - -- -
SELECT n+1 1999-12-25 Sat 52 7 51 6
FROM temp1 1999-12-26 Sun 53 1 51 7
WHERE n < 9), 1999-12-27 Mon 53 2 52 1
temp2 (dt1) AS 1999-12-28 Tue 53 3 52 2
(VALUES(DATE('1999-12-25')) 1999-12-29 Wed 53 4 52 3
,(DATE('2000-12-24'))), 1999-12-30 Thu 53 5 52 4
temp3 (dt2) AS 1999-12-31 Fri 53 6 52 5
(SELECT dt1 + n DAYS 2000-01-01 Sat 1 7 52 6
FROM temp1 2000-01-02 Sun 2 1 52 7
,temp2) 2000-01-03 Mon 2 2 1 1
SELECT CHAR(dt2,ISO) AS date 2000-12-24 Sun 53 1 51 7
,SUBSTR(DAYNAME(dt2),1,3) AS day 2000-12-25 Mon 53 2 52 1
,WEEK(dt2) AS w 2000-12-26 Tue 53 3 52 2
,DAYOFWEEK(dt2) AS d 2000-12-27 Wed 53 4 52 3
,WEEK_ISO(dt2) AS wi 2000-12-28 Thu 53 5 52 4
,DAYOFWEEK_ISO(dt2) AS i 2000-12-29 Fri 53 6 52 5
FROM temp3 2000-12-30 Sat 53 7 52 6
ORDER BY 1; 2000-12-31 Sun 54 1 52 7
2001-01-01 Mon 1 2 1 1
2001-01-02 Tue 1 3 1 2
Figure 332, DAYOFWEEK_ISO function example
SELECT dt1 ANSWER
,DAYOFYEAR(dt1) AS dyr ===============
FROM scalar DT1 DYR
ORDER BY dyr; ---------- ---
0001-01-01 1
1996-04-22 113
1996-08-15 228
Figure 333, DAYOFYEAR function example
SELECT dt1 ANSWER
,DAYS(dt1) AS dy1 ==================
FROM scalar DT1 DY1
ORDER BY dy1 ---------- ------
,dt1; 0001-01-01 1
1996-04-22 728771
1996-08-15 728886
Figure 334, DAYS function example
Figure 335, DBPARTITIONNUM function syntax
SELECT DBPARTITIONNUM(id) AS dbnum ANSWER
FROM staff ======
WHERE id = 10; DBNUM
-----
0
Figure 336, DBPARTITIONNUM function example
Figure 337, DECIMAL function syntax
WITH temp1(n1,n2,c1,c2) AS ANSWER
(VALUES (123 ==========================
,1E2 DEC1 DEC2 DEC3 DEC4
,'123.4' ----- ------ ------ ------
,'567$8')) 123. 100.0 123.4 567.8
SELECT DEC(n1,3) AS dec1
,DEC(n2,4,1) AS dec2
,DEC(c1,4,1) AS dec3
,DEC(c2,4,1,'$') AS dec4
FROM temp1;
Figure 338, DECIMAL function examples
Figure 339, DECRYPT function syntax
SELECT id
,name
,DECRYPT_CHAR(name2,'CLUELESS') AS name3
,GETHINT(name2) AS hint
,name2
FROM (SELECT id
,name
,ENCRYPT(name,'CLUELESS','MY BOSS') AS name2
FROM staff
WHERE id < 30
)AS xxx
ORDER BY id;
Figure 340, DECRYPT_CHAR function example
SELECT a.name AS n1 ANSWER
,SOUNDEX(a.name) AS s1 ==============================
,b.name AS n2 N1 S1 N2 S2 DF
,SOUNDEX(b.name) AS s2 ------- ---- --------- ---- --
,DIFFERENCE Sanders S536 Sneider S536 4
(a.name,b.name) AS df Sanders S536 Smith S530 3
FROM staff a Sanders S536 Lundquist L532 2
,staff b Sanders S536 Daniels D542 1
WHERE a.id = 10 Sanders S536 Molinare M456 1
AND b.id > 150 Sanders S536 Scoutten S350 1
AND b.id < 250 Sanders S536 Abrahams A165 0
ORDER BY df DESC Sanders S536 Kermisch K652 0
,n2 ASC; Sanders S536 Lu L000 0
Figure 341, DIFFERENCE function example
SELECT s1 ANSWER
,DIGITS(s1) AS ds1 =========================
,d1 S1 DS1 D1 DD1
,DIGITS(d1) AS dd1 ------ ----- ----- ---
FROM scalar; -2 00002 -2.4 024
0 00000 0.0 000
1 00001 1.8 018
Figure 342, DIGITS function examples
WITH temp1(c1,d1) AS ANSWER (output shortened)
(VALUES ('12345',12.4) ==================================
,('-23.5',1234) C1D D1D
,('1E+45',-234) ---------------- ----------------
,('-2e05',+2.4)) +1.23450000E+004 +1.24000000E+001
SELECT DOUBLE(c1) AS c1d -2.35000000E+001 +1.23400000E+003
,DOUBLE(d1) AS d1d +1.00000000E+045 -2.34000000E+002
FROM temp1; -2.00000000E+005 +2.40000000E+000
Figure 343, DOUBLE function examples
Figure 344, DECRYPT function syntax
SELECT id
,name
,ENCRYPT(name,'THAT IDIOT','MY BROTHER') AS name2
FROM staff
WHERE ID < 30
ORDER BY id;
Figure 345, ENCRYPT function example
WITH temp1(n1) AS ANSWER
(VALUES (0) ==============================
UNION ALL N1 E1 E2
SELECT n1 + 1 -- --------------------- -----
FROM temp1 0 +1.00000000000000E+0 1
WHERE n1 < 10) 1 +2.71828182845904E+0 2
SELECT n1 2 +7.38905609893065E+0 7
,EXP(n1) AS e1 3 +2.00855369231876E+1 20
,SMALLINT(EXP(n1)) AS e2 4 +5.45981500331442E+1 54
FROM temp1; 5 +1.48413159102576E+2 148
6 +4.03428793492735E+2 403
7 +1.09663315842845E+3 1096
8 +2.98095798704172E+3 2980
9 +8.10308392757538E+3 8103
10 +2.20264657948067E+4 22026
Figure 346, EXP function examples
SELECT d1 ANSWER (float output shortened)
,FLOOR(d1) AS d2 ===================================
,f1 D1 D2 F1 F2
,FLOOR(f1) AS f2 ----- ---- ---------- ----------
FROM scalar; -2.4 -3. -2.400E+0 -3.000E+0
0.0 +0. +0.000E+0 +0.000E+0
1.8 +1. +1.800E+0 +1.000E+0
Figure 347, FLOOR function examples
SELECT id
,GENERATE_UNIQUE() AS unique_val#1
,DEC(HEX(GENERATE_UNIQUE()),26) AS unique_val#2
FROM staff
WHERE id < 50
ORDER BY id;
ANSWER
================= ===========================
ID UNIQUE_VAL#1 UNIQUE_VAL#2
-- -------------- ---------------------------
NOTE: 2ND FIELD => 10 20011017191648990521000000.
IS UNPRINTABLE. => 20 20011017191648990615000000.
30 20011017191648990642000000.
40 20011017191648990669000000.
Figure 348, GENERATE_UNIQUE function examples
SELECT u1
,SUBSTR(u1,20,1) CONCAT SUBSTR(u1,19,1) CONCAT
SUBSTR(u1,18,1) CONCAT SUBSTR(u1,17,1) CONCAT
SUBSTR(u1,16,1) CONCAT SUBSTR(u1,15,1) CONCAT
SUBSTR(u1,14,1) CONCAT SUBSTR(u1,13,1) CONCAT
SUBSTR(u1,12,1) CONCAT SUBSTR(u1,11,1) CONCAT
SUBSTR(u1,10,1) CONCAT SUBSTR(u1,09,1) CONCAT
SUBSTR(u1,08,1) CONCAT SUBSTR(u1,07,1) CONCAT
SUBSTR(u1,06,1) CONCAT SUBSTR(u1,05,1) CONCAT
SUBSTR(u1,04,1) CONCAT SUBSTR(u1,03,1) CONCAT
SUBSTR(u1,02,1) CONCAT SUBSTR(u1,01,1) AS U2
FROM (SELECT HEX(GENERATE_UNIQUE()) AS u1
FROM staff
WHERE id < 50) AS xxx
ORDER BY u2;
ANSWER
================================================
U1 U2
-------------------------- --------------------
20000901131649119940000000 04991194613110900002
20000901131649119793000000 39791194613110900002
20000901131649119907000000 70991194613110900002
20000901131649119969000000 96991194613110900002
Figure 349, GENERATE_UNIQUE output, characters reversed to make pseudo-random
SELECT u1
,SUBSTR(reverse(CHAR(u1)),7,20) AS u2
FROM (SELECT HEX(GENERATE_UNIQUE()) AS u1
FROM STAFF
WHERE ID < 50) AS xxx
ORDER BY U2;
Figure 350, GENERATE_UNIQUE output, characters reversed using function
SELECT id
,name
,GETHINT(name2) AS hint
FROM (SELECT id
,name
,ENCRYPT(name,'THAT IDIOT','MY BROTHER') AS name2
FROM staff
WHERE id < 30 ANSWER
)AS xxx =====================
ORDER BY id; ID NAME HINT
-- ------- ----------
10 Sanders MY BROTHER
20 Pernal MY BROTHER
Figure 351, GETHINT function example
SELECT HASHEDVALUE(id) AS hvalue ANSWER
FROM staff ======
WHERE id = 10; HVALUE
------
0
Figure 352, HASHEDVALUE function example
WITH temp1(n1) AS ANSWER
(VALUES (-3) ===============================
UNION ALL S SHX DHX FHX
SELECT n1 + 1 -- ---- ------ ----------------
FROM temp1 -3 FDFF 00003D 00000000000008C0
WHERE n1 < 3) -2 FEFF 00002D 00000000000000C0
SELECT SMALLINT(n1) AS s -1 FFFF 00001D 000000000000F0BF
,HEX(SMALLINT(n1)) AS shx 0 0000 00000C 0000000000000000
,HEX(DEC(n1,4,0)) AS dhx 1 0100 00001C 000000000000F03F
,HEX(DOUBLE(n1)) AS fhx 2 0200 00002C 0000000000000040
FROM temp1; 3 0300 00003C 0000000000000840
Figure 353, HEX function examples, numeric data
SELECT c1 ANSWER
,HEX(c1) AS chx =======================================
,v1 C1 CHX V1 VHX
,HEX(v1) AS vhx ------ ------------ ------ ------------
FROM scalar; ABCDEF 414243444546 ABCDEF 414243444546
ABCD 414243442020 ABCD 41424344
AB 414220202020 AB 4142
Figure 354, HEX function examples, character & varchar
SELECT dt1 ANSWER
,HEX(dt1) AS dthx ===================================
,tm1 DT1 DTHX TM1 TMHX
,HEX(tm1) AS tmhx ---------- -------- -------- ------
FROM scalar; 1996-04-22 19960422 23:58:58 235858
1996-08-15 19960815 15:15:15 151515
0001-01-01 00010101 00:00:00 000000
Figure 355, HEX function examples, date & time
SELECT tm1 ANSWER
,HOUR(tm1) AS hr ============
FROM scalar TM1 HR
ORDER BY tm1; -------- --
00:00:00 0
15:15:15 15
23:58:58 23
Figure 356, HOUR function example
CREATE TABLE seq#
(ident_val INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY
,cur_ts TIMESTAMP NOT NULL
,PRIMARY KEY (ident_val));
COMMIT;
INSERT INTO seq# VALUES(DEFAULT,CURRENT TIMESTAMP);
ANSWER
WITH temp (idval) AS ======
(VALUES (IDENTITY_VAL_LOCAL())) IDVAL
SELECT * -----
FROM temp; 1.
Figure 357, IDENTITY_VAL_LOCAL function usage
Figure 358, INSERT function syntax
SELECT name ANSWER (4K output fields shortened)
,INSERT(name,3,2,'A') ===================================
,INSERT(name,3,2,'AB') NAME 2 3 4
,INSERT(name,3,2,'ABC') -------- ------- -------- ---------
FROM staff Sanders SaAers SaABers SaABCers
WHERE id < 40; Pernal PeAal PeABal PeABCal
Marenghi MaAnghi MaABnghi MaABCnghi
Figure 359, INSERT function examples
SELECT d1 ANSWER
,INTEGER(d1) ====================================
,INT('+123') D1 2 3 4 5
,INT('-123') ----- ----- ------ ------ ------
,INT(' 123 ') -2.4 -2 123 -123 123
FROM scalar; 0.0 0 123 -123 123
1.8 1 123 -123 123
Figure 360, INTEGER function examples
WITH temp1(dt1) AS ANSWER
(VALUES ('0001-01-01-00.00.00') =========================
,('1752-09-10-00.00.00') DT DY DJ
,('1993-01-03-00.00.00') ---------- ------ -------
,('1993-01-03-23.59.59')) 0001-01-01 1 1721426
SELECT DATE(dt1) AS dt 1752-09-10 639793 2361218
,DAYS(dt1) AS dy 1993-01-03 727566 2448991
,JULIAN_DAY(dt1) AS dj 1993-01-03 727566 2448991
FROM temp1;
Figure 361, JULIAN_DAY function example
SELECT bd
,JULIAN_DAY(bd)
,(1461 * (YEAR(bd) + 4800 + (MONTH(bd)-14)/12))/4
+( 367 * (MONTH(bd)- 2 - 12*((MONTH(bd)-14)/12)))/12
-( 3 * ((YEAR(bd) + 4900 + (MONTH(bd)-14)/12)/100))/4
+DAY(bd) - 32075
FROM (SELECT birthdate AS bd
FROM employee
WHERE midinit = 'R' ANSWER
) AS xxx ==========================
ORDER BY bd; BD 2 3
---------- ------- -------
1926-05-17 2424653 2424653
1936-03-28 2428256 2428256
1946-07-09 2432011 2432011
1955-04-12 2435210 2435210
Figure 362, JULIAN_DAY function examples
ANSWER
=============================
DT DJ1 DJ2
WITH temp1(dt1) AS ---------- ---------- -------
(VALUES ('1997-01-01') 1997-01-01 1996-12-17 1997001
,('1997-01-02') 1997-01-02 1996-12-18 1997002
,('1997-12-31')) 1997-12-31 1997-12-16 1997365
SELECT DATE(dt1) AS dt
,DATE(dt1) - 15 DAYS AS dj1
,YEAR(dt1) * 1000 + DAYOFYEAR(dt1) AS dj2
FROM temp1;
Figure 363, Julian Date outputs
SELECT name ANSWER
,LCASE(name) AS lname =========================
,UCASE(name) AS uname NAME LNAME UNAME
FROM staff ------- ------- -------
WHERE id < 30; Sanders sanders SANDERS
Pernal pernal PERNAL
Figure 364, LCASE function example
WITH temp1(c1) AS ANSWER
(VALUES (' ABC') ================
,(' ABC ') C1 C2 L2
,('ABC ')) ----- ----- --
SELECT c1 ABC AB 4
,LEFT(c1,4) AS c2 ABC ABC 4
,LENGTH(LEFT(c1,4)) AS l2 ABC ABC 4
FROM temp1;
Figure 365, LEFT function examples
SELECT LENGTH(d1) ANSWER
,LENGTH(f1) =======================
,LENGTH(s1) 1 2 3 4 5
,LENGTH(c1) --- --- --- --- ---
,LENGTH(RTRIM(c1)) 2 8 2 6 6
FROM scalar; 2 8 2 6 4
2 8 2 6 2
Figure 366, LENGTH function examples
WITH temp1(n1) AS ANSWER
(VALUES (1),(123),(1234) ===============================
,(12345),(123456)) N1 L1
SELECT n1 ------ -----------------------
,LOG(n1) AS l1 1 +0.00000000000000E+000
FROM temp1; 123 +4.81218435537241E+000
1234 +7.11801620446533E+000
12345 +9.42100640177928E+000
123456 +1.17236400962654E+001
Figure 367, LOG function example
Figure 368, LOCATE function syntax
SELECT c1 ANSWER
,LOCATE('D', c1) ==========================
,LOCATE('D', c1,2) C1 2 3 4 5
,LOCATE('EF',c1) ------ --- --- --- ---
,LOCATE('A', c1,2) ABCDEF 4 4 5 0
FROM scalar; ABCD 4 4 0 0
AB 0 0 0 0
Figure 369, LOCATE function examples
WITH temp1(n1) AS ANSWER
(VALUES (1),(123),(1234) ===============================
,(12345),(123456)) N1 L1
SELECT n1 ------ -----------------------
,LOG10(n1) AS l1 1 +0.00000000000000E+000
FROM temp1; 123 +2.08990511143939E+000
1234 +3.09131515969722E+000
12345 +4.09149109426795E+000
123456 +5.09151220162777E+000
Figure 370, LOG10 function example
WITH temp1(c1) AS ANSWER
(VALUES (' ABC') ================
,(' ABC ') C1 C2 L2
,('ABC ')) ----- ----- --
SELECT c1 ABC ABC 3
,LTRIM(c1) AS c2 ABC ABC 4
,LENGTH(LTRIM(c1)) AS l2 ABC ABC 5
FROM temp1;
Figure 371, LTRIM function example
SELECT ts1 ANSWER
,MICROSECOND(ts1) ======================================
FROM scalar TS1 2
ORDER BY ts1; -------------------------- -----------
0001-01-01-00.00.00.000000 0
1996-04-22-23.58.58.123456 123456
1996-08-15-15.15.15.151515 151515
Figure 372, MICROSECOND function example
SELECT ts1 ANSWER
,MIDNIGHT_SECONDS(ts1) ======================================
,HOUR(ts1)*3600 + TS1 2 3
MINUTE(ts1)*60 + -------------------------- ----- -----
SECOND(ts1) 0001-01-01-00.00.00.000000 0 0
FROM scalar 1996-04-22-23.58.58.123456 86338 86338
ORDER BY ts1; 1996-08-15-15.15.15.151515 54915 54915
Figure 373, MIDNIGHT_SECONDS function example
ANSWER
==============
MS TM
----- --------
WITH temp1 (ms) AS 0 00:00:00
(SELECT MIDNIGHT_SECONDS(ts1) 54915 15:15:15
FROM scalar 86338 23:58:58
)
SELECT ms
,SUBSTR(DIGITS(ms/3600 ),9) || ':' ||
SUBSTR(DIGITS((ms-((MS/3600)*3600))/60 ),9) || ':' ||
SUBSTR(DIGITS(ms-((MS/60)*60) ),9) AS tm
FROM temp1
ORDER BY 1;
Figure 374, Convert MIDNIGHT_SECONDS output back to a time value
SELECT ts1 ANSWER
,MINUTE(ts1) ======================================
FROM scalar TS1 2
ORDER BY ts1; -------------------------- -----------
0001-01-01-00.00.00.000000 0
1996-04-22-23.58.58.123456 58
1996-08-15-15.15.15.151515 15
Figure 375, MINUTE function example
WITH temp1(n1,n2) AS ANSWER
(VALUES (-31,+11) =======================
UNION ALL N1 N2 DIV MD1 MD2
SELECT n1 + 13 --- --- --- --- ---
,n2 - 4 -31 11 -2 -9 -9
FROM temp1 -18 7 -2 -4 -4
WHERE n1 < 60 -5 3 -1 -2 -2
) 8 -1 -8 0 0
SELECT n1 21 -5 -4 1 1
,n2 34 -9 -3 7 7
,n1/n2 AS div 47 -13 -3 8 8
,n1-((n1/n2)*n2) AS md1 60 -17 -3 9 9
,MOD(n1,n2) AS md2
FROM temp1
ORDER BY 1;
Figure 376, MOD function example
SELECT dt1 ANSWER
,MONTH(dt1) =======================
,MONTHNAME(dt1) DT1 2 3
FROM scalar ---------- -- -------
ORDER BY dt1; 0001-01-01 1 January
1996-04-22 4 April
1996-08-15 8 August
Figure 377, MONTH and MONTHNAME functions example
WITH temp1 (n1,n2) AS
(VALUES (DECIMAL(1234,10) ANSWER
,DECIMAL(1234,10))) ========
SELECT n1 >> 1234.
,n2 >> 1234.
,n1 * n2 AS p1 >> 1522756.
,"*"(n1,n2) AS p2 >> 1522756.
,MULTIPLY_ALT(n1,n2) AS p3 >> 1522756.
FROM temp1;
Figure 378, Multiplying numbers - examples
<--MULTIPLY_ALT->
RESULT RESULT SCALE PRECSION
INPUT#1 INPUT#2 "*" OPERATOR MULTIPLY_ALT TRUNCATD TRUNCATD
========== ========== ============ ============ ======== =======
DEC(05,00) DEC(05,00) DEC(10,00) DEC(10,00) NO NO
DEC(10,05) DEC(11,03) DEC(21,08) DEC(21,08) NO NO
DEC(20,15) DEC(21,13) DEC(31,28) DEC(31,18) YES NO
DEC(26,23) DEC(10,01) DEC(31,24) DEC(31,19) YES NO
DEC(31,03) DEC(15,08) DEC(31,11) DEC(31,03) YES YES
Figure 379, Decimal multiplication - same output lengths
SELECT s1 ANSWER
,NULLIF(s1,0) =====================
,c1 S1 2 C1 4
,NULLIF(c1,'AB') --- --- ------ ------
FROM scalar -2 -2 ABCDEF ABCDEF
WHERE NULLIF(0,0) IS NULL; 0 - ABCD ABCD
1 1 AB -
Figure 380, NULLIF function examples
SELECT PARTITION(id) AS pp ANSWER
FROM staff ======
WHERE id = 10; PP
--
0
SELECT c1 ANSWER
,POSSTR(c1,' ') AS p1 ==================
,POSSTR(c1,'CD') AS p2 C1 P1 P2 P3
,POSSTR(c1,'cd') AS p3 ------ -- -- --
FROM scalar AB 3 0 0
ORDER BY 1; ABCD 5 3 0
ABCDEF 0 3 0
Figure 381, POSSTR function examples
SELECT c1 ANSWER
,POSSTR(c1,' ') AS p1 ===========================
,LOCATE(' ',c1) AS l1 C1 P1 L1 P2 L2 P3 L3 L4
,POSSTR(c1,'CD') AS p2 ------ -- -- -- -- -- -- --
,LOCATE('CD',c1) AS l2 AB 3 3 0 0 0 0 0
,POSSTR(c1,'cd') AS p3 ABCD 5 5 3 3 0 0 4
,LOCATE('cd',c1) AS l3 ABCDEF 0 0 3 3 0 0 4
,LOCATE('D',c1,2) AS l4
FROM scalar
ORDER BY 1;
Figure 382, POSSTR vs. LOCATE functions
WITH temp1(n1) AS ANSWER
(VALUES (1),(10),(100)) ===============================
SELECT n1 N1 P1 P2 P3
,POWER(n1,1) AS p1 ------- ------- ------- -------
,POWER(n1,2) AS p2 1 1 1 1
,POWER(n1,3) AS p3 10 10 100 1000
FROM temp1; 100 100 10000 1000000
Figure 383, POWER function examples
Figure 384, RAISE_ERROR function syntax
SELECT s1 ANSWER
,CASE ==============
WHEN s1 < 1 THEN s1 S1 S2
ELSE RAISE_ERROR('80001',c1) ------ ------
END AS s2 -2 -2
FROM scalar; 0 0
SQLSTATE=80001
Figure 385, RAISE_ERROR function example
WITH temp (num, ran) AS
(VALUES (INT(1)
,RAND(2))
UNION ALL
SELECT num + 1
,RAND()
FROM temp
WHERE num < 100000 ANSWER
) =============
SELECT COUNT(*) AS #rows ==> 100000
,COUNT(DISTINCT ran) AS #values ==> 31242
,DEC(AVG(ran),7,6) AS avg_ran ==> 0.499838
,DEC(STDDEV(ran),7,6) AS std_dev 0.288706
,DEC(MIN(ran),7,6) AS min_ran 0.000000
,DEC(MAX(ran),7,6) AS max_ran 1.000000
,DEC(MAX(ran),7,6) -
DEC(MIN(ran),7,6) AS range 1.000000
,DEC(VAR(ran),7,6) AS variance 0.083351
FROM temp;
Figure 386, Sample output from RAND function
SELECT deptno AS dno ANSWER
,RAND(0) AS ran ===========================
FROM department DNO RAN
WHERE deptno < 'E' --- ----------------------
ORDER BY 1; A00 +1.15970336008789E-003
B01 +2.35572374645222E-001
C01 +6.48152104251228E-001
D01 +7.43736075930052E-002
D11 +2.70241401409955E-001
D21 +3.60026856288339E-001
Figure 387, Make reproducible random numbers (use seed)
SELECT deptno AS dno ANSWER
,RAND() AS ran ===========================
FROM department DNO RAN
WHERE deptno < 'D' --- ----------------------
ORDER BY 1; A00 +2.55287331766717E-001
B01 +9.85290078432569E-001
C01 +3.18918424024171E-001
Figure 388, Make non-reproducible random numbers (no seed)
WITH Temp1 (col1, col2, col3) AS ANSWER
(VALUES (0 ===================
,SMALLINT(RAND(2)*35)*10 COL1 COL2 COL3
,DECIMAL(RAND()*10000,7,2)) ---- ---- -------
UNION ALL 0 0 9342.32
SELECT col1 + 1 1 250 8916.28
,SMALLINT(RAND()*35)*10 2 310 5430.76
,DECIMAL(RAND()*10000,7,2) 3 150 5996.88
FROM temp1 4 110 8066.34
WHERE col1 + 1 < 10 5 50 5589.77
) 6 130 8602.86
SELECT * 7 340 184.94
FROM temp1; 8 310 5441.14
9 70 9267.55
Figure 389, Use RAND to make sample data
WITH temp1 (col1,ran1,ran2) AS ANSWER
(VALUES (0 ===================
,RAND(2) COL#1 RAN#1 RAN#2
,RAND()+(RAND()/1E5) ) ----- ----- -----
UNION ALL 30000 19698 29998
SELECT col1 + 1
,RAND()
,RAND() +(RAND()/1E5)
FROM temp1
WHERE col1 + 1 < 30000
)
SELECT COUNT(*) AS col#1
,COUNT(DISTINCT ran1) AS ran#1
,COUNT(DISTINCT ran2) AS ran#2
FROM temp1;
Figure 390, Use RAND to make many distinct random values
SELECT id ANSWER
,name ============
FROM staff ID NAME
WHERE RAND() < 0.1 --- --------
ORDER BY id; 140 Fraye
190 Sneider
290 Quill
Figure 391, Randomly select 10% of matching rows
SELECT id ANSWER
,name ============
FROM (SELECT s.* ID NAME
,ROW_NUMBER() OVER(ORDER BY RAND()) AS r --- --------
FROM staff s 10 Sanders
)AS xxx 30 Marenghi
WHERE r <= 5 190 Sneider
ORDER BY id; 270 Lea
280 Wilson
Figure 392, Select five random rows
UPDATE staff
SET salary = RAND()*10000
WHERE id < 50;
Figure 393, Use RAND to assign random salaries
ANSWERS
================================
SELECT n1 AS dec => 1234567890.123456789012345678901
,DOUBLE(n1) AS dbl => 1.23456789012346e+009
,REAL(n1) AS rel => 1.234568e+009
,INTEGER(n1) AS int => 1234567890
,BIGINT(n1) AS big => 1234567890
FROM (SELECT 1234567890.123456789012345678901 AS n1
FROM staff
WHERE id = 10) AS xxx;
Figure 394, REAL and other numeric function examples
Figure 395, REPEAT function syntax
SELECT id ANSWER
,CHAR(REPEAT(name,3),40) ===========================
FROM staff ID 2
WHERE id < 40 -- ------------------------
ORDER BY id; 10 SandersSandersSanders
20 PernalPernalPernal
30 MarenghiMarenghiMarenghi
Figure 396, REPEAT function example
Figure 397, REPLACE function syntax
SELECT c1 ANSWER
,REPLACE(c1,'AB','XY') AS r1 ======================
,REPLACE(c1,'BA','XY') AS r2 C1 R1 R2
FROM scalar; ------ ------ ------
ABCDEF XYCDEF ABCDEF
ABCD XYCD ABCD
AB XY AB
Figure 398, REPLACE function examples
SELECT c1 ANSWER
,REPLACE(REPLACE( ==============
REPLACE(REPLACE(c1, C1 R1
'AB','XY'),'ab','XY'), ------ ------
'Ab','XY'),'aB','XY') ABCDEF XYCDEF
FROM scalar; ABCD XYCD
AB XY
Figure 399, Nested REPLACE functions
WITH temp1(c1) AS ANSWER
(VALUES (' ABC') ================
,(' ABC ') C1 C2 L2
,('ABC ')) ----- ----- --
SELECT c1 ABC ABC 4
,RIGHT(c1,4) AS c2 ABC ABC 4
,LENGTH(RIGHT(c1,4)) as l2 ABC BC 4
FROM temp1;
Figure 400, RIGHT function examples
ANSWER
===============================================
D1 P2 P1 P0 N1 N2
------- ------- ------- ------- ------- -------
WITH temp1(d1) AS 123.400 123.400 123.400 123.000 120.000 100.000
(VALUES (123.400) 23.450 23.450 23.400 23.000 20.000 0.000
,( 23.450) 3.456 3.460 3.500 3.000 0.000 0.000
,( 3.456) 0.056 0.060 0.100 0.000 0.000 0.000
,( .056))
SELECT d1
,DEC(ROUND(d1,+2),6,3) AS p2
,DEC(ROUND(d1,+1),6,3) AS p1
,DEC(ROUND(d1,+0),6,3) AS p0
,DEC(ROUND(d1,-1),6,3) AS n1
,DEC(ROUND(d1,-2),6,3) AS n2
FROM temp1;
Figure 401, ROUND function examples
SELECT c1 ANSWER
,RTRIM(c1) AS r1 ======================
,LENGTH(c1) AS r2 C1 R1 R2 R3
,LENGTH(RTRIM(c1)) AS r3 ------ ------ -- --
FROM scalar; ABCDEF ABCDEF 6 6
ABCD ABCD 6 4
AB AB 6 2
Figure 402, RTRIM function example
SELECT d1 ANSWER (float output shortened)
,SIGN(d1) =========================================
,f1 D1 2 F1 4
,SIGN(f1) ----- ---------- ---------- ----------
FROM scalar; -2.4 -1.000E+0 -2.400E+0 -1.000E+0
0.0 +0.000E+0 +0.000E+0 +0.000E+0
1.8 +1.000E+0 +1.800E+0 +1.000E+0
Figure 403, SIGN function examples
WITH temp1(n1) AS ANSWER
(VALUES (0) =======================
UNION ALL N1 RAN SIN TAN
SELECT n1 + 10 -- ----- ----- -----
FROM temp1 0 0.000 0.000 0.000
WHERE n1 < 80) 10 0.174 0.173 0.176
SELECT n1 20 0.349 0.342 0.363
,DEC(RADIANS(n1),4,3) AS ran 30 0.523 0.500 0.577
,DEC(SIN(RADIANS(n1)),4,3) AS sin 40 0.698 0.642 0.839
,DEC(TAN(RADIANS(n1)),4,3) AS tan 50 0.872 0.766 1.191
FROM temp1; 60 1.047 0.866 1.732
70 1.221 0.939 2.747
80 1.396 0.984 5.671
Figure 404, SIN function example
SELECT d1 ANSWER
,SMALLINT(d1) ==================================
,SMALLINT('+123') D1 2 3 4 5
,SMALLINT('-123') ----- ------ ------ ------ ------
,SMALLINT(' 123 ') -2.4 -2 123 -123 123
FROM scalar; 0.0 0 123 -123 123
1.8 1 123 -123 123
Figure 405, SMALLINT function examples
SELECT a.name AS n1 ANSWER
,SOUNDEX(a.name) AS s1 ==============================
,b.name AS n2 N1 S1 N2 S2 DF
,SOUNDEX(b.name) AS s2 ------- ---- --------- ---- --
,DIFFERENCE Sanders S536 Sneider S536 4
(a.name,b.name) AS df Sanders S536 Smith S530 3
FROM staff a Sanders S536 Lundquist L532 2
,staff b Sanders S536 Daniels D542 1
WHERE a.id = 10 Sanders S536 Molinare M456 1
AND b.id > 150 Sanders S536 Scoutten S350 1
AND b.id < 250 Sanders S536 Abrahams A165 0
ORDER BY df DESC Sanders S536 Kermisch K652 0
,n2 ASC; Sanders S536 Lu L000 0
Figure 406, SOUNDEX function example
WITH temp1(n1) AS ANSWER
(VALUES (1),(2),(3)) ==================
SELECT n1 N1 S1 S2 S3
,SPACE(n1) AS s1 -- ---- -- ----
,LENGTH(SPACE(n1)) AS s2 1 1 X
,SPACE(n1) || 'X' AS s3 2 2 X
FROM temp1; 3 3 X
Figure 407, SPACE function examples
DB2 GET SNAPSHOT FOR DYNAMIC SQL ON SAMPLE WRITE TO FILE
ANSWER - PART OF (ONE OF THE STATEMENTS IN THE SQL CACHE)
=============================================================
Number of executions = 8
Number of compilations = 1
Worst preparation time (ms) = 3
Best preparation time (ms) = 3
Rows deleted = Not Collected
Rows inserted = Not Collected
Rows read = Not Collected
Rows updated = Not Collected
Rows written = Not Collected
Statement sorts = Not Collected
Total execution time (sec.ms) = Not Collected
Total user cpu time (sec.ms) = Not Collected
Total system cpu time (sec.ms) = Not Collected
Statement text = select min(dept) from staff
Figure 408, GET SNAPSHOT command
SELECT *
FROM TABLE(SQLCACHE_SNAPSHOT()) SS
WHERE SS.NUM_EXECUTIONS <> 0;
Figure 409, SQLCACHE_SNAPSHOT function example
SELECT ORDINAL AS COLNO
,CHAR(PARMNAME,18) AS COLNAME
,TYPENAME AS COLTYPE
,LENGTH
,SCALE
FROM SYSCAT.FUNCPARMS
WHERE FUNCSCHEMA = 'SYSFUN'
AND FUNCNAME = 'SQLCACHE_SNAPSHOT'
ORDER BY COLNO;
Figure 410, List columns returned by SQLCACHE_SNAPSHOT
WITH temp1(n1) AS ANSWER
(VALUES (0.5),(0.0) ============
,(1.0),(2.0)) N1 S1
SELECT DEC(n1,4,3) AS n1 ----- -----
,DEC(SQRT(n1),4,3) AS s1 0.500 0.707
FROM temp1; 0.000 0.000
1.000 1.000
2.000 1.414
Figure 411, SQRT function example
Figure 412, SUBSTR function syntax
WITH temp1 (len, dat1) AS ANSWER
(VALUES ( 6,'123456789') =========================
,( 4,'12345' ) LEN DAT1 LDAT SUBDAT
,( 16,'123' ) --- --------- ---- ------
) 6 123456789 9 123456
SELECT len 4 12345 5 1234
,dat1
,LENGTH(dat1) AS ldat
,SUBSTR(dat1,1,len) AS subdat
FROM temp1;
Figure 413, SUBSTR function - error because length parm too long
WITH temp1 (len, dat1) AS ANSWER
(VALUES ( 6,'123456789') =========================
,( 4,'12345' ) LEN DAT1 LDAT SUBDAT
,( 16,'123' ) --- --------- ---- ------
) 6 123456789 9 123456
SELECT len 4 12345 5 1234
,dat1 16 123 3 123
,LENGTH(dat1) AS ldat
,SUBSTR(dat1,1,CASE
WHEN len < LENGTH(dat1) THEN len
ELSE LENGTH(dat1)
END ) AS subdat
FROM temp1;
Figure 414, SUBSTR function - avoid error using CASE (see previous)
SELECT name ANSWER
,LENGTH(name) AS len ===========================
,SUBSTR(name,5) AS s1 NAME LEN S1 L1 S2 L2
,LENGTH(SUBSTR(name,5)) AS l1 -------- --- ---- -- --- --
,SUBSTR(name,5,3) AS s2 Sanders 7 ers 3 ers 3
,LENGTH(SUBSTR(name,5,3)) AS l2 Pernal 6 al 2 al 3
FROM staff Marenghi 8 nghi 4 ngh 3
WHERE id < 60; O'Brien 7 ien 3 ien 3
Hanes 5 s 1 s 3
Figure 415, SUBSTR function - fixed length output if third parm. used
SELECT a.id ANSWER
,a.dept =========================
,a.salary ID DEPT SALARY DEPTSAL
,b.deptsal -- ---- -------- --------
FROM staff a 10 20 18357.50 64286.10
,TABLE 20 20 18171.25 64286.10
(SELECT b.dept 30 38 17506.75 77285.55
,SUM(b.salary) AS deptsal
FROM staff b
WHERE b.dept = a.dept
GROUP BY b.dept
)AS b
WHERE a.id < 40
ORDER BY a.id;
Figure 416, Full-select with external table reference
CREATE ALIAS emp1 FOR employee; ANSWER
CREATE ALIAS emp2 FOR emp1; =======================
TABSCHEMA TABNAME CARD
SELECT tabschema --------- -------- ----
,tabname graeme employee -1
,card
FROM syscat.tables
WHERE tabname = TABLE_NAME('emp2','graeme');
Figure 417, TABLE_NAME function example
CREATE VIEW fred1 (c1, c2, c3) ANSWER
AS VALUES (11, 'AAA', 'BBB'); ===========================
TAB_SCH TAB_NME
CREATE ALIAS fred2 FOR fred1; -------- ------------------
CREATE ALIAS fred3 FOR fred2; graeme fred1
graeme xxxxx
DROP VIEW fred1;
WITH temp1 (tab_sch, tab_nme) AS
(VALUES (TABLE_SCHEMA('fred3','graeme'),TABLE_NAME('fred3')),
(TABLE_SCHEMA('xxxxx') ,TABLE_NAME('xxxxx','xxx')))
SELECT *
FROM temp1;
Figure 418, TABLE_SCHEMA and TABLE_NAME functions example
SELECT TIMESTAMP('1997-01-11-22.44.55.000000')
,TIMESTAMP('1997-01-11-22.44.55.000')
,TIMESTAMP('1997-01-11-22.44.55')
,TIMESTAMP('19970111224455')
,TIMESTAMP('1997-01-11','22.44.55')
FROM staff
WHERE id = 10;
Figure 419, TIMESTAMP function examples
WITH temp1 (ts1) AS
(VALUES ('1999-12-31 23:59:59')
,('2002-10-30 11:22:33')
)
SELECT ts1
,TIMESTAMP_FORMAT(ts1,'YYYY-MM-DD HH24:MI:SS') AS ts2
FROM temp1
ORDER BY ts1; ANSWER
===============================================
TS1 TS2
------------------- --------------------------
1999-12-31 23:59:59 1999-12-31-23.59.59.000000
2002-10-30 11:22:33 2002-10-30-11.22.33.000000
Figure 420, TIMESTAMP_FORMAT function example
SELECT tm1 ANSWER
,TIMESTAMP_ISO(tm1) ===================================
FROM scalar; TM1 2
-------- --------------------------
23:58:58 2000-09-01-23.58.58.000000
15:15:15 2000-09-01-15.15.15.000000
00:00:00 2000-09-01-00.00.00.000000
Figure 421, TIMESTAMP_ISO function example
WITH
temp1 (ts1,ts2) AS
(VALUES ('1996-03-01-00.00.01','1995-03-01-00.00.00')
,('1996-03-01-00.00.00','1995-03-01-00.00.01')),
temp2 (ts1,ts2) AS
(SELECT TIMESTAMP(ts1)
,TIMESTAMP(ts2)
FROM temp1),
temp3 (ts1,ts2,df) AS
(SELECT ts1
,ts2
,CHAR(TS1 - TS2) AS df ANSWER
FROM temp2) =============================
SELECT df DF DIF DYS
,TIMESTAMPDIFF(16,df) AS dif --------------------- --- ---
,DAYS(ts1) - DAYS(ts2) AS dys 00010000000001.000000 365 366
FROM temp3; 00001130235959.000000 360 366
Figure 422, TIMESTAMPDIFF function example
CREATE FUNCTION ts_diff_works(in_hi TIMESTAMP,in_lo TIMESTAMP)
RETURNS BIGINT
RETURN (BIGINT(DAYS(in_hi)) * 86400000000
+ BIGINT(MIDNIGHT_SECONDS(in_hi)) * 1000000
+ BIGINT(MICROSECOND(in_hi)))
-(BIGINT(DAYS(in_lo)) * 86400000000
+ BIGINT(MIDNIGHT_SECONDS(in_lo)) * 1000000
+ BIGINT(MICROSECOND(in_lo)));
Figure 423, Function to get difference between two timestamps
Figure 424, TRANSLATE function syntax
ANS. NOTES
==== =================
SELECT 'abcd' ==> abcd No change
,TRANSLATE('abcd') ==> ABCD Make upper case
,TRANSLATE('abcd','','a') ==> bcd 'a'=>' '
,TRANSLATE('abcd','A','A') abcd 'A'=>'A'
,TRANSLATE('abcd','A','a') Abcd 'a'=>'A'
,TRANSLATE('abcd','A','ab') A cd 'a'=>'A','b'=>' '
,TRANSLATE('abcd','A','ab',' ') A cd 'a'=>'A','b'=>' '
,TRANSLATE('abcd','A','ab','z') Azcd 'a'=>'A','b'=>'z'
,TRANSLATE('abcd','AB','a') Abcd 'a'=>'A'
FROM staff
WHERE id = 10;
Figure 425, TRANSLATE function examples
ANSWER
======
SELECT c1 ==> ABCD
,REPLACE(c1,'AB','XY') ==> XYCD
,REPLACE(c1,'BA','XY') ==> ABCD
,TRANSLATE(c1,'XY','AB') XYCD
,TRANSLATE(c1,'XY','BA') YXCD
FROM scalar
WHERE c1 = 'ABCD';
Figure 426, REPLACE vs. TRANSLATE
ANSWER
===============================================
D1 POS2 POS1 ZERO NEG1 NEG2
------- ------- ------- ------- ------- -------
WITH temp1(d1) AS 123.400 123.400 123.400 123.000 120.000 100.000
(VALUES (123.400) 23.450 23.440 23.400 23.000 20.000 0.000
,( 23.450) 3.456 3.450 3.400 3.000 0.000 0.000
,( 3.456) 0.056 0.050 0.000 0.000 0.000 0.000
,( .056))
SELECT d1
,DEC(TRUNC(d1,+2),6,3) AS pos2
,DEC(TRUNC(d1,+1),6,3) AS pos1
,DEC(TRUNC(d1,+0),6,3) AS zero
,DEC(TRUNC(d1,-1),6,3) AS neg1
,DEC(TRUNC(d1,-2),6,3) AS neg2
FROM temp1
ORDER BY 1 DESC;
Figure 427, TRUNCATE function examples
SELECT name ANSWER
,LCASE(name) AS lname =========================
,UCASE(name) AS uname NAME LNAME UNAME
FROM staff ------- ------- -------
WHERE id < 30; Sanders sanders SANDERS
Pernal pernal PERNAL
Figure 428, UCASE function example
SELECT c1 ANSWER
,LENGTH(c1) AS l1 ========================
,VARCHAR(c1) AS v2 C1 L1 V2 L2 V3
,LENGTH(VARCHAR(c1)) AS l2 ------ -- ------ -- ----
,VARCHAR(c1,4) AS v3 ABCDEF 6 ABCDEF 6 ABCD
FROM scalar; ABCD 6 ABCD 6 ABCD
AB 6 AB 6 AB
Figure 429, VARCHAR function examples
WITH temp1 (ts1) AS
(VALUES (TIMESTAMP('1999-12-31-23.59.59'))
,(TIMESTAMP('2002-10-30-11.22.33'))
)
SELECT ts1
,VARCHAR_FORMAT(ts1,'YYYY-MM-DD HH24:MI:SS') AS ts2
FROM temp1
ORDER BY ts1; ANSWER
==============================================
TS1 TS2
-------------------------- -------------------
1999-12-31-23.59.59.000000 1999-12-31 23:59:59
2002-10-30-11.22.33.000000 2002-10-30 11:22:33
Figure 430, VARCHAR_FORMAT function example
SELECT WEEK(DATE('2000-01-01')) AS w1 ANSWER
,WEEK(DATE('2000-01-02')) AS w2 ==================
,WEEK(DATE('2001-01-02')) AS w3 W1 W2 W3 W4 W5
,WEEK(DATE('2000-12-31')) AS w4 -- -- -- -- --
,WEEK(DATE('2040-12-31')) AS w5 1 2 1 54 53
FROM sysibm.sysdummy1;
Figure 431, WEEK function examples
WITH ANSWER
temp1 (n) AS ==========================
(VALUES (0) DTE DY WK DY WI DI
UNION ALL ---------- --- -- -- -- --
SELECT n+1 1998-12-27 Sun 53 1 52 7
FROM temp1 1998-12-28 Mon 53 2 53 1
WHERE n < 10), 1998-12-29 Tue 53 3 53 2
temp2 (dt2) AS 1998-12-30 Wed 53 4 53 3
(SELECT DATE('1998-12-27') + y.n YEARS 1998-12-31 Thu 53 5 53 4
+ d.n DAYS 1999-01-01 Fri 1 6 53 5
FROM temp1 y 1999-01-02 Sat 1 7 53 6
,temp1 d 1999-01-03 Sun 2 1 53 7
WHERE y.n IN (0,2)) 1999-01-04 Mon 2 2 1 1
SELECT CHAR(dt2,ISO) dte 1999-01-05 Tue 2 3 1 2
,SUBSTR(DAYNAME(dt2),1,3) dy 1999-01-06 Wed 2 4 1 3
,WEEK(dt2) wk 2000-12-27 Wed 53 4 52 3
,DAYOFWEEK(dt2) dy 2000-12-28 Thu 53 5 52 4
,WEEK_ISO(dt2) wi 2000-12-29 Fri 53 6 52 5
,DAYOFWEEK_ISO(dt2) di 2000-12-30 Sat 53 7 52 6
FROM temp2 2000-12-31 Sun 54 1 52 7
ORDER BY 1; 2001-01-01 Mon 1 2 1 1
2001-01-02 Tue 1 3 1 2
2001-01-03 Wed 1 4 1 3
2001-01-04 Thu 1 5 1 4
2001-01-05 Fri 1 6 1 5
2001-01-06 Sat 1 7 1 6
Figure 432, WEEK_ISO function example
SELECT dt1 ANSWER
,YEAR(dt1) AS yr ======================
,WEEK(dt1) AS wk DT1 YR WK
FROM scalar; ---------- ---- ----
1996-04-22 1996 17
1996-08-15 1996 33
0001-01-01 1 1
Figure 433, YEAR and WEEK functions example
SELECT id ANSWER
,salary =============================
,"+"(salary) AS s2 ID SALARY S2 S3
,"+"(salary,id) AS s3 -- -------- -------- --------
FROM staff 10 18357.50 18357.50 18367.50
WHERE id < 40 20 18171.25 18171.25 18191.25
ORDER BY id; 30 17506.75 17506.75 17536.75
Figure 434, PLUS function examples
SELECT empno
,CHAR(birthdate,ISO) AS bdate1
,CHAR(birthdate + 1 YEAR,ISO) AS bdate2
,CHAR("+"(birthdate,DEC(00010000,8)),ISO) AS bdate3
,CHAR("+"(birthdate,DOUBLE(1),SMALLINT(1)),ISO) AS bdate4
FROM employee
WHERE empno < '000040'
ORDER BY empno; ANSWER
==================================================
EMPNO BDATE1 BDATE2 BDATE3 BDATE4
------ ---------- ---------- ---------- ----------
000010 1933-08-24 1934-08-24 1934-08-24 1934-08-24
000020 1948-02-02 1949-02-02 1949-02-02 1949-02-02
000030 1941-05-11 1942-05-11 1942-05-11 1942-05-11
Figure 435, Adding one year to date value
SELECT id ANSWER
,salary ==============================
,"-"(salary) AS s2 ID SALARY S2 S3
,"-"(salary,id) AS s3 -- -------- --------- --------
FROM staff 10 18357.50 -18357.50 18347.50
WHERE id < 40 20 18171.25 -18171.25 18151.25
ORDER BY id; 30 17506.75 -17506.75 17476.75
Figure 436, MINUS function examples
SELECT id ANSWER
,salary ===============================
,salary * id AS s2 ID SALARY S2 S3
,"*"(salary,id) AS s3 -- -------- --------- ---------
FROM staff 10 18357.50 183575.00 183575.00
WHERE id < 40 20 18171.25 363425.00 363425.00
ORDER BY id; 30 17506.75 525202.50 525202.50
Figure 437, MULTIPLY function examples
SELECT id ANSWER
,salary =============================
,salary / id AS s2 ID SALARY S2 S3
,"/"(salary,id) AS s3 -- -------- -------- --------
FROM staff 10 18357.50 1835.750 1835.750
WHERE id < 40 20 18171.25 908.562 908.562
ORDER BY id; 30 17506.75 583.558 583.558
Figure 438, DIVIDE function examples
SELECT id ANSWER
,name || 'Z' AS n1 ===========================
,name CONCAT 'Z' AS n2 ID N1 N2 N3 N4
,"||"(name,'Z') As n3 --- ----- ----- ----- -----
,CONCAT(name,'Z') As n4 110 NganZ NganZ NganZ NganZ
FROM staff 210 LuZ LuZ LuZ LuZ
WHERE LENGTH(name) < 5 270 LeaZ LeaZ LeaZ LeaZ
ORDER BY id;
Figure 439, CONCAT function examples
SELECT dept ANSWER
,name ====================
,comm DEPT NAME COMM
FROM staff ---- ------- -------
WHERE dept < 30 15 Hanes -
AND id < 100 15 Rothman 1152.00
ORDER BY dept 20 James 128.20
,name; 20 Pernal 612.45
20 Sanders -
Figure 440, Sample query - returns raw data
15Hanes
15Rothman01152.00
20James00128.20
20Pernal00612.45
20Sanders
Figure 441, XML version of above data
15
1501152.00
2000128.20
2000612.45
20
Figure 442, Made name an attribute of employee
Figure 443, XMLSERIALIZE function syntax
SELECT id ANSWER
,XMLSERIALIZE(CONTENT ==================
XMLELEMENT(NAME "Dept", dept) ID XMLDATA
AS CHAR(30)) AS xmldata -- ---------------
FROM staff 20 20
WHERE id BETWEEN 20 AND 30 30 38
ORDER BY id;
Figure 444, XMLSERIALIZE function example
Figure 445, XML2CLOB function syntax
Figure 446, XMLAGG function syntax
SELECT dept AS dp
,XMLSERIALIZE(CONTENT
XMLAGG(
XMLELEMENT(NAME "Nm", name)
ORDER BY id)
AS CHAR(40)) AS xmldata ANSWER
FROM staff ==================================
WHERE dept < 30 DP XMLDATA
AND id < 80 -- -------------------------------
GROUP BY dept 15 HanesRothman
ORDER BY dept; 20 SandersPernal
Figure 447, XMLAGG function example
Figure 448, XMLCONCAT function syntax
SELECT id
,XMLSERIALIZE(CONTENT
XMLCONCAT(
XMLELEMENT(NAME "dp", dept)
,XMLELEMENT(NAME "nm", name)
)
AS CHAR(40)) AS xmldata ANSWER
FROM staff ==============================
WHERE dept < 30 ID XMLDATA
AND id < 70 -- ---------------------------
ORDER BY id; 10 20Sanders
20 20Pernal
50 15Hanes
Figure 449, XMLCONCAT function example
Figure 450, XMLELEMENT function syntax
SELECT XMLSERIALIZE(CONTENT
XMLELEMENT(NAME "staff"
,XMLELEMENT(NAME "nm", name)
,XMLELEMENT(NAME "sc", salary, '+', comm)
)
AS CHAR(90)) AS xmldata
FROM staff
WHERE dept < 30
AND id < 60
ORDER BY id;
ANSWER
========================================================
Sanders18357.50+
Pernal18171.25+00612.45
Figure 451, XMLELEMENT function example
Figure 452, XMLATTRIBUTES function syntax
SELECT XMLSERIALIZE(CONTENT
XMLELEMENT(NAME "Emp",
XMLATTRIBUTES(name AS "Nm", dept)
)
AS VARCHAR(100)) AS xmldata
FROM staff ANSWER
WHERE dept < 30 ==================================
AND id < 60
ORDER BY dept
,name;
Figure 453, XMLATTRIBUTES function example
Figure 454, XMLFOREST function syntax
SELECT XMLSERIALIZE(CONTENT
XMLFOREST(name AS "Nm", dept AS "dp", comm)
AS VARCHAR(100)) AS xmldata
FROM staff
WHERE id IN (10,20)
ORDER BY id DESC; ANSWER
===============================================
Pernal2000612.45
Sanders20
Figure 455, XMLFOREST function example
Figure 456, XMLNAMESPACES function syntax
SELECT XMLSERIALIZE(CONTENT
XMLFOREST(
XMLNAMESPACES(DEFAULT 'http:\t1.com'
, 'http:\t2.com' AS "t2"
, 'http:\t3.com' AS "t3")
,name AS "nm", salary AS "sal")
AS VARCHAR(300)) AS xmldata
FROM staff
WHERE id = 20;
ANSWER (line breaks/indentation added)
===========================================
Pernal
18171.25
Figure 457, XMLNAMESPACES function example
SELECT dept ANSWER
,name ====================
,comm DEPT NAME COMM
FROM staff ---- ------- -------
WHERE dept < 30 15 Hanes -
AND id < 100 15 Rothman 1152.00
ORDER BY dept 20 James 128.20
,name; 20 Pernal 612.45
20 Sanders -
Figure 458, Sample query - returns raw data
SELECT XMLSERIALIZE(CONTENT
XMLELEMENT(NAME "Emp",
XMLELEMENT(NAME "Dept", dept),
XMLELEMENT(NAME "Name", name),
XMLELEMENT(NAME "Comm", comm)
)
AS VARCHAR(100))
FROM staff
WHERE dept < 30
AND id < 100
ORDER BY dept
,name;
ANSWER
===================================================================
15Hanes
15Rothman01152.00
20James00128.20
20Pernal00612.45
20Sanders
Figure 459, Sample query - returns XML data
SELECT XMLSERIALIZE(CONTENT
XMLELEMENT(NAME "Emp",
XMLATTRIBUTES(name AS "Name"),
XMLELEMENT(NAME "Dept", dept),
XMLELEMENT(NAME "Comm", comm)
)
AS VARCHAR(100))
FROM staff
WHERE dept < 30
AND id < 100
ORDER BY dept
,name;
ANSWER
==============================================================
15
1501152.00
2000128.20
2000612.45
20
Figure 460, Sample query - returns XML data + attribute
SELECT XMLSERIALIZE(CONTENT
XMLELEMENT(NAME "Data",
XMLELEMENT(NAME "Chr1", CHAR (c1,3)),
XMLELEMENT(NAME "Chr2", CHAR (c1,5)),
XMLELEMENT(NAME "VChr", VARCHAR(c1,5)),
XMLELEMENT(NAME "Dec1", DECIMAL(n1,7,2)),
XMLELEMENT(NAME "Dec2", DECIMAL(n2,9,1)),
XMLELEMENT(NAME "Flt1", FLOAT (n2)),
XMLELEMENT(NAME "Int1", INTEGER(n1)),
XMLELEMENT(NAME "Int2", INTEGER(n2)),
XMLELEMENT(NAME "Time", TIME (t1)),
XMLELEMENT(NAME "Date", DATE (t1)),
XMLELEMENT(NAME "Ts" , TIMESTAMP(t1))
)
AS VARCHAR(300)) AS xmldata
FROM (SELECT 'ABC' AS c1
,1234.56 AS n1
,1234567 AS n2
,TIMESTAMP('2004-09-14-22.33.44.123456') AS t1
FROM staff
WHERE id = 10
)AS xxx;
ANSWER (line-breaks/indentation added)
======================================
ABC
ABC
ABC
01234.56
01234567.0
1.234567E6
1234
1234567
2004-09-14
2004-09-14T22:33:44.123456
Figure 461, XMLELEMENT output examples
ANSWER
WITH temp1 (indata) AS ===========================
(VALUES ('') ------ --------------------
,('&txt') <txt
,('"txt')) txt> txt>
SELECT indata &txt &txt
,XMLSERIALIZE(CONTENT "txt "txt
XMLELEMENT(NAME "Out", indata))
AS CHAR(50)) AS outdata
FROM temp1;
Figure 462, Convert XML input strings
SELECT XMLSERIALIZE(CONTENT
XMLELEMENT(NAME "Emp", dept, name, comm)
AS CHAR(50)) AS outdata
FROM staff ANSWER
WHERE dept < 30 ===========================
AND id < 100 15Hanes
ORDER BY dept 15Rothman01152.0020James00128.20
20Pernal00612.45
20Sanders
Figure 463, Concatenation done in XML function
SELECT XMLSERIALIZE(CONTENT
XMLELEMENT(NAME "Emp", CHAR(dept) || name || CHAR(comm))
AS CHAR(50)) AS outdata
FROM staff
WHERE dept < 30 ANSWER
AND id < 100 =================================
ORDER BY dept
,name; 15 Rothman01152.00
20 James00128.20
20 Pernal00612.45
Figure 464, Concatenation done before XML function
SELECT XMLSERIALIZE(CONTENT
XMLELEMENT(NAME "Emp",
XMLATTRIBUTES(name), dept, comm)
)
AS CHAR(100)) AS xmldata
FROM staff
WHERE dept < 30 ANSWER
AND id < 100 ====================================
ORDER BY dept 15
,name; 1501152.00
2000128.20
2000612.45
20
Figure 465, One element, one attribute, two data-items
SELECT XMLSERIALIZE(CONTENT
XMLELEMENT(NAME "Emp",
XMLATTRIBUTES(name, dept, comm)
)
AS VARCHAR(100)) AS xmldata
FROM staff
WHERE dept < 30
AND id < 100
ORDER BY dept ANSWER
,name; ====================================================
Figure 466, One element, three attributes, no data-items
SELECT XMLSERIALIZE(CONTENT
XMLELEMENT(NAME "Emp",
XMLATTRIBUTES(name AS "Nm", dept AS "Dpt", comm)
)
AS VARCHAR(100)) AS xmldata
FROM staff
WHERE dept < 30 ANSWER
AND id < 100 =================================================
ORDER BY dept
,name;
Figure 467, Assign names to attributes
SELECT XMLSERIALIZE(CONTENT
XMLELEMENT(NAME "Dpt",
XMLATTRIBUTES(dept),
XMLAGG(
XMLELEMENT(NAME "Emp",
XMLELEMENT(NAME "Nm", name),
XMLELEMENT(NAME "Cm", comm))
ORDER BY id)
)
AS VARCHAR(300)) AS xmldata
FROM staff
WHERE dept < 30
AND id < 100
GROUP BY dept; ANSWER (line-breaks/indentation added)
===============================================
Hanes
Rothman01152.00
Sanders
Pernal00612.45
James00128.20
Figure 468, XMLAGG function example
Figure 469, REC2XML function syntax
WITH temp1 (indata) AS
(VALUES ('')
,('&txt')
,('"txt')
,('''txt'))
SELECT indata
,REC2XML (1.0, 'COLATTVAL', 'row', indata) AS outdata
FROM temp1;
ANSWER
==========================================================
INDATA OUTDATA
------ ---------------------------------------------------
<txt
txt> txt>
&txt &txt
"txt "txt
'txt 'txt
Figure 470, REC2XML function character conversion
WITH temp1 (indata) AS
(VALUES ('')
,('&txt')
,('"txt')
,('''txt'))
SELECT indata
,REC2XML (1.0, 'COLATTVAL_XML', 'row', indata) AS outdata
FROM temp1;
ANSWER
=====================================================
INDATA OUTDATA
------ ----------------------------------------------
txt> txt>
&txt &txt
"txt "txt
'txt 'txt
Figure 471, REC2XML function without character conversion
SELECT REC2XML(1.0, 'COLATTVAL', 'row', dept, name, comm) AS txt
FROM staff
WHERE id BETWEEN 30 AND 40
ORDER BY dept
,name; ANSWER (line-breaks/indentation added)
============================================
38
Marenghi
38
O'Brien
00846.55
Figure 472, REC2XML function example
Figure 473, Sourced function syntax
CREATE FUNCTION digi_int (SMALLINT)
RETURNS CHAR(5)
SOURCE SYSIBM.DIGITS(SMALLINT);
Figure 474, Create sourced function
SELECT id AS ID ANSWER
,DIGITS(id) AS I2 ==============
,digi_int(id) AS I3 ID I2 I3
FROM staff -- ----- -----
WHERE id < 40 10 00010 00010
ORDER BY id; 20 00020 00020
30 00030 00030
Figure 475, Using sourced function - works
SELECT id ANSWER
,digi_int(INT(id)) =======
FROM staff
WHERE id < 50;
Figure 476, Using sourced function - fails
CREATE DISTINCT TYPE us_dollars AS DEC(7,2) WITH COMPARISONS;
CREATE TABLE customers
(ID SMALLINT NOT NULL
,balance us_dollars NOT NULL);
ANSWER
INSERT INTO customers VALUES (1 ,111.11),(2 ,222.22); ==========
ID balance
SELECT * -- -------
FROM customers 1 111.11
ORDER BY ID; 2 222.22
Figure 477, Create distinct type and test table
SELECT id ANSWER
,balance * 10 =======
FROM customers
ORDER BY id;
Figure 478, Do multiply - fails
CREATE FUNCTION "*" (us_dollars,INT)
RETURNS us_dollars
SOURCE SYSIBM."*"(DECIMAL,INT);
Figure 479, Create sourced function
SELECT id ANSWER
,balance * 10 AS newbal ==========
FROM customers ID NEWBAL
ORDER BY id; -- -------
1 1111.10
2 2222.20
Figure 480, Do multiply - works
SELECT id ANSWER
,"*"(balance,10) AS newbal ==========
FROM customers ID NEWBAL
ORDER BY id; -- -------
1 1111.10
2 2222.20
Figure 481, Do multiply - works
Figure 482, Scalar and Table function syntax
CREATE FUNCTION Test() RETURNS CHAR(5) RETURN 'abcde';
Figure 483, Function returns nullable, but never null, value
CREATE FUNCTION returns_zero() RETURNS SMALLINT RETURN 0;
ANSWER
SELECT id AS id ======
,returns_zero() AS zz ID ZZ
FROM staff -- --
WHERE id = 10; 10 0
Figure 484, Simple function usage
CREATE FUNCTION calc(inval SMALLINT) RETURNS INT RETURN inval * 10;
CREATE FUNCTION calc(inval INTEGER) RETURNS INT RETURN inval * 5;
SELECT id AS id ANSWER
,calc(SMALLINT(id)) AS c1 ==========
,calc(INTEGER (id)) AS C2 ID C1 C2
FROM staff -- --- ---
WHERE id < 30 10 100 50
ORDER BY id; 20 200 100
DROP FUNCTION calc(SMALLINT);
DROP FUNCTION calc(INTEGER);
Figure 485, Two functions with same name
CREATE FUNCTION rnd(inval INT)
RETURNS SMALLINT
NOT DETERMINISTIC
RETURN RAND() * 50; ANSWER
======
SELECT id AS id ID RND
,rnd(1) AS RND -- ---
FROM staff 10 37
WHERE id < 40 20 8
ORDER BY id; 30 42
Figure 486, Not deterministic function
CREATE FUNCTION get_sal(inval SMALLINT)
RETURNS DECIMAL(7,2)
RETURN SELECT salary
FROM staff
WHERE id = inval; ANSWER
===========
SELECT id AS id ID SALARY
,get_sal(id) AS salary -- --------
FROM staff 10 18357.50
WHERE id < 40 20 18171.25
ORDER BY id; 30 17506.75
Figure 487, Function using query
CREATE FUNCTION max_sal(inval SMALLINT)
RETURNS DECIMAL(7,2)
RETURN WITH
ddd (max_sal) AS
(SELECT MAX(S2.salary)
FROM staff S1
,staff S2
WHERE S1.id = inval
AND S1.dept = s2.dept)
,yyy (max_sal) AS
(SELECT MAX(S2.salary)
FROM staff S1
,staff S2
WHERE S1.id = inval
AND S1.years = s2.years)
SELECT CASE
WHEN ddd.max_sal > yyy.max_sal
THEN ddd.max_sal
ELSE yyy.max_sal
END
FROM ddd, yyy;
ANSWER
SELECT id AS id ====================
,salary AS SAL1 ID SAL1 SAL2
,max_sal(id) AS SAL2 -- -------- --------
FROM staff 10 18357.50 22959.20
WHERE id < 40 20 18171.25 18357.50
ORDER BY id; 30 17506.75 19260.25
Figure 488, Function using common table expression
CREATE FUNCTION remove_e(instr VARCHAR(50))
RETURNS VARCHAR(50)
RETURN replace(instr,'e','');
UPDATE staff
SET name = remove_e(name)
WHERE id < 40;
Figure 489, Function used in update
--#SET DELIMITER ! IMPORTANT
============
CREATE FUNCTION reverse(instr VARCHAR(50)) This example
RETURNS VARCHAR(50) uses an "!"
BEGIN ATOMIC as the stmt
DECLARE outstr VARCHAR(50) DEFAULT ''; delimiter.
DECLARE curbyte SMALLINT DEFAULT 0;
SET curbyte = LENGTH(RTRIM(instr));
WHILE curbyte >= 1 DO
SET outstr = outstr || SUBSTR(instr,curbyte,1);
SET curbyte = curbyte - 1;
END WHILE;
RETURN outstr;
END!
ANSWER
SELECT id AS id ====================
,name AS name1 ID NAME1 NAME2
,reverse(name) AS name2 -- -------- -------
FROM staff 10 Sanders srednaS
WHERE id < 40 20 Pernal lanreP
ORDER BY id! 30 Marenghi ihgneraM
Figure 490, Function using compound SQL
--#SET DELIMITER ! IMPORTANT
============
CREATE FUNCTION check_len(instr VARCHAR(50)) This example
RETURNS SMALLINT uses an "!"
BEGIN ATOMIC as the stmt
IF instr IS NULL THEN delimiter.
RETURN NULL;
END IF;
IF length(instr) < 6 THEN
SIGNAL SQLSTATE '75001'
SET MESSAGE_TEXT = 'Input string is < 6';
ELSEIF length(instr) < 7 THEN
RETURN -1;
END IF;
RETURN length(instr); ANSWER
END! =================
ID NAME1 NAME2
SELECT id AS id -- -------- -----
,name AS name1 10 Sanders 7
,check_len(name) AS name2 20 Pernal -1
FROM staff 30 Marenghi 8
WHERE id < 60 40 O'Brien 7
ORDER BY id!
Figure 491, Function with error checking logic
CREATE FUNCTION get_staff()
RETURNS TABLE (ID SMALLINT
,name VARCHAR(9)
,YR SMALLINT)
RETURN SELECT id
,name
,years ANSWER
FROM staff; ==============
ID NAME YR
SELECT * -- -------- --
FROM TABLE(get_staff()) AS s 10 Sanders 7
WHERE id < 40 20 Pernal 8
ORDER BY id; 30 Marenghi 5
Figure 492, Simple table function
Figure 493, Table function usage - syntax
CREATE FUNCTION get_st(inval INTEGER)
RETURNS TABLE (id SMALLINT
,name VARCHAR(9)
,yr SMALLINT)
RETURN SELECT id
,name
,years
FROM staff ANSWER
WHERE id = inval; ==============
ID NNN YY
SELECT * -- -------- --
FROM TABLE(get_st(30)) AS sss (id, nnn, yy); 30 Marenghi 5
Figure 494, Table function with parameters
CREATE FUNCTION make_data()
RETURNS TABLE (KY SMALLINT
,DAT CHAR(5))
RETURN WITH temp1 (k#) AS (VALUES (1),(2),(3)) ANSWER
SELECT k# ========
,DIGITS(SMALLINT(k#)) KY DAT
FROM temp1; -- -----
1 00001
SELECT * 2 00002
FROM TABLE(make_data()) AS ttt; 3 00003
Figure 495, Table function that creates data
CREATE FUNCTION staff_list(lo_key INTEGER IMPORTANT
,lo_sal INTEGER) ============
RETURNS TABLE (id SMALLINT This example
,salary DECIMAL(7,2) uses an "!"
,max_sal DECIMAL(7,2) as the stmt
,id_max SMALLINT) delimiter.
LANGUAGE SQL
READS SQL DATA
EXTERNAL ACTION
DETERMINISTIC
BEGIN ATOMIC
DECLARE hold_sal DECIMAL(7,2) DEFAULT 0;
DECLARE hold_key SMALLINT;
IF lo_sal < 0 THEN
SIGNAL SQLSTATE '75001'
SET MESSAGE_TEXT = 'Salary too low';
END IF;
FOR get_max AS
SELECT id AS in_key
,salary As in_sal
FROM staff
WHERE id >= lo_key
DO
IF in_sal > hold_sal THEN
SET hold_sal = in_sal;
SET hold_key = in_key;
END IF;
END FOR;
RETURN
SELECT id
,salary
,hold_sal
,hold_key ANSWER
FROM staff ============================
WHERE id >= lo_key; ID SALARY MAX_SAL ID_MAX
END! --- -------- -------- ------
70 16502.83 22959.20 160
SELECT * 80 13504.60 22959.20 160
FROM TABLE(staff_list(66,1)) AS ttt 90 18001.75 22959.20 160
WHERE id < 111 100 18352.80 22959.20 160
ORDER BY id! 110 12508.20 22959.20 160
Figure 496, Table function with compound SQL
CREATE FUNCTION julian_out(inval DATE)
RETURNS CHAR(7)
RETURN RTRIM(CHAR(YEAR(inval)))
|| SUBSTR(DIGITS(DAYOFYEAR(inval)),8);
ANSWER
SELECT empno =========================
,CHAR(hiredate,ISO) AS h_date EMPNO H_DATE J_DATE
,JULIAN_OUT(hiredate) AS j_date ------ ---------- -------
FROM employee 000010 1965-01-01 1965001
WHERE empno < '000050' 000020 1973-10-10 1973283
ORDER BY empno; 000030 1975-04-05 1975095
Figure 497, Convert Date into Julian Date
CREATE FUNCTION julian_in(inval CHAR(7))
RETURNS DATE
RETURN DATE('0001-01-01')
+ (INT(SUBSTR(inval,1,4)) - 1) YEARS
+ (INT(SUBSTR(inval,5,3)) - 1) DAYS;
Figure 498, Convert Julian Date into Date
SELECT empno
,hiredate
FROM employee
WHERE YEAR(hiredate) = YEAR(CURRENT DATE) - 1;
Figure 499, Select rows where hire-date = prior year
CREATE FUNCTION year_month(inval DATE)
RETURNS INTEGER
RETURN (YEAR(inval) * 12) + MONTH(inval);
Figure 500, Create year-month function
SELECT empno
,hiredate
FROM employee
WHERE YEAR_MONTH(hiredate) = YEAR_MONTH(CURRENT DATE) - 1;
Figure 501, Select rows where hire-date = prior month
CREATE FUNCTION sunday_week(inval DATE)
RETURNS INTEGER
RETURN DAYS(inval) / 7;
Figure 502, Create week-number function
CREATE FUNCTION monday_week(inval DATE)
RETURNS INTEGER
RETURN (DAYS(inval) - 1) / 7;
Figure 503, Create week-number function
WITH ANSWER
temp1 (num,dt) AS ==================================
(VALUES (1 DATE DAY WK IS SUN_WK MON_WK
,DATE('2004-12-29')) ---------- --- -- -- ------ ------
UNION ALL 2004-12-29 Wed 53 53 104563 104563
SELECT num + 1 2004-12-30 Thu 53 53 104563 104563
,dt + 1 DAY 2004-12-31 Fri 53 53 104563 104563
FROM temp1 2005-01-01 Sat 1 53 104563 104563
WHERE num < 15 2005-01-02 Sun 2 53 104564 104563
), 2005-01-03 Mon 2 1 104564 104564
temp2 (dt,dy) AS 2005-01-04 Tue 2 1 104564 104564
(SELECT dt 2005-01-05 Wed 2 1 104564 104564
,SUBSTR(DAYNAME(dt),1,3) 2005-01-06 Thu 2 1 104564 104564
FROM temp1 2005-01-07 Fri 2 1 104564 104564
) 2005-01-08 Sat 2 1 104564 104564
SELECT CHAR(dt,ISO) AS date 2005-01-09 Sun 3 1 104565 104564
,dy AS day 2005-01-10 Mon 3 2 104565 104565
,WEEK(dt) AS wk 2005-01-11 Tue 3 2 104565 104565
,WEEK_ISO(dt) AS is 2005-01-12 Wed 3 2 104565 104565
,sunday_week(dt) AS sun_wk
,monday_week(dt) AS mon_wk
FROM temp2
ORDER BY 1;
Figure 504, Use week-number functions
CREATE FUNCTION NumList(max_num INTEGER)
RETURNS TABLE(num INTEGER)
LANGUAGE SQL
RETURN
WITH temp1 (num) AS
(VALUES (0)
UNION ALL
SELECT num + 1
FROM temp1
WHERE num < max_num
)
SELECT num
FROM temp1;
Figure 505, Create num-list function
ANSWERS
SELECT * =======
FROM TABLE(NumList(-1)) AS xxx; 0
SELECT *
FROM TABLE(NumList(+0)) AS xxx; 0
SELECT *
FROM TABLE(NumList(+3)) AS xxx; 0
1
2
3
SELECT *
FROM TABLE(NumList(CAST(NULL AS INTEGER))) AS xxx; 0
Figure 506, Using num-list function
SELECT actno ANSWER
,emstdate =================================
,emendate ACTNO EMSTDATE EMENDATE #DAYS
,DAYS(emendate) - ----- ---------- ---------- -----
DAYS(emstdate) AS #days 70 1982-06-15 1982-07-01 16
FROM emp_act act 80 1982-03-01 1982-04-15 45
WHERE empno = '000260'
AND projno = 'AD3113'
AND actno < 100
AND emptime = 0.5
ORDER BY actno;
Figure 507, Select activity start & end date
SELECT actno ANSWER
,#days ==========================
,num ACTNO #DAYS NUM NEW_DATE
,emstdate + num DAYS AS new_date ----- ----- --- ----------
FROM (SELECT actno 70 16 0 1982-06-15
,emstdate 70 16 1 1982-06-16
,emendate 70 16 2 1982-06-17
,DAYS(emendate) - 70 16 3 1982-06-18
DAYS(emstdate) AS #days 70 16 4 1982-06-19
FROM emp_act act 70 16 5 1982-06-20
WHERE empno = '000260' 70 16 6 1982-06-21
AND projno = 'AD3113' 70 16 7 1982-06-22
AND actno < 100 70 16 8 1982-06-23
AND emptime = 0.5 70 16 9 1982-06-24
)AS aaa 70 16 10 1982-06-25
,TABLE(NumList(#days)) AS ttt etc...
ORDER BY actno
,num;
Figure 508, Generate one row per date between start & end dates (1 of 2)
SELECT actno
,#days
,num ACTNO #DAYS NUM NEW_DATE
,emstdate + num DAYS AS new_date ----- ----- --- ----------
FROM (SELECT actno 70 16 0 1982-06-15
,emstdate 70 16 1 1982-06-16
,emendate 70 16 2 1982-06-17
,DAYS(emendate) - 70 16 3 1982-06-18
DAYS(emstdate) AS #days 70 16 4 1982-06-19
FROM emp_act act 70 16 5 1982-06-20
WHERE empno = '000260' 70 16 6 1982-06-21
AND projno = 'AD3113' 70 16 7 1982-06-22
AND actno < 100 70 16 8 1982-06-23
AND emptime = 0.5 70 16 9 1982-06-24
)AS aaa 70 16 10 1982-06-25
LEFT OUTER JOIN etc...
TABLE(NumList(#days)) AS ttt
ON 1 = 1
ORDER BY actno
,num;
Figure 509, Generate one row per date between start & end dates (2 of 2)
Figure 510, ORDER BY syntax
CREATE VIEW SEQ_DATA(col1,col2)
AS VALUES ('ab','xy')
,('AB','xy')
,('ac','XY')
,('AB','XY')
,('Ab','12');
Figure 511, ORDER BY sample data definition
SELECT col1 ANSWER SEQ_DATA
,col2 ========= +---------+
FROM seq_data COL1 COL2 |COL1|COL2|
ORDER BY col1 ASC ---- ---- |----+----|
,col2; ab xy |ab |xy |
ac XY |AB |xy |
Ab 12 |ac |XY |
AB xy |AB |XY |
AB XY |Ab |12 |
+---------+
Figure 512, Simple ORDER BY
SELECT col1 ANSWER
,col2 =========
FROM seq_data COL1 COL2
ORDER BY TRANSLATE(col1) ASC ---- ----
,TRANSLATE(col2) ASC Ab 12
ab xy
AB XY
AB xy
ac XY
Figure 513, Case insensitive ORDER BY
SELECT col2 ANSWER
FROM seq_data ======
ORDER BY col1 COL2
,col2; ----
xy
XY
12
xy
XY
Figure 514, ORDER BY on not-displayed column
SELECT col1 ANSWER
,col2 =========
FROM seq_data COL1 COL2
ORDER BY SUBSTR(col1,2) DESC ---- ----
,col2 ac XY
,1; AB xy
AB XY
Ab 12
ab xy
Figure 515, ORDER BY second byte of first column
SELECT col1 ANSWER
,HEX(col1) AS hex1 ===================
,col2 COL1 HEX1 COL2 HEX2
,HEX(col2) AS hex2 ---- ---- ---- ----
FROM seq_data AB 4142 XY 5859
ORDER BY HEX(col1) AB 4142 xy 7879
,HEX(col2) Ab 4162 12 3132
ab 6162 xy 7879
ac 6163 XY 5859
Figure 516, ORDER BY in bit-data sequence
SELECT col1 ANSWER SEQ_DATA
FROM (SELECT col1 ====== +---------+
FROM seq_data COL1 |COL1|COL2|
ORDER BY col2 ---- |----+----|
) AS xxx Ab |ab |xy |
ORDER BY ORDER OF xxx; ab |AB |xy |
AB |ac |XY |
ac |AB |XY |
AB |Ab |12 |
+---------+
Figure 517, ORDER BY nested ORDER BY
SELECT * ANSWER
FROM (SELECT * =========
FROM (SELECT * COL1 COL2
FROM seq_data ---- ----
ORDER BY col2 Ab 12
)AS xxx ab xy
ORDER BY ORDER OF xxx AB xy
,SUBSTR(col1,2) AB XY
)AS yyy ac XY
ORDER BY ORDER OF yyy
,col1;
Figure 518, Multiple nested ORDER BY statements
SELECT empno ANSWER
,projno AS prj =================
,actno AS act EMPNO PRJ ACT R#
,ROW_NUMBER() OVER() AS r# ------ --- --- --
FROM FINAL TABLE 400000 ZZZ 999 1
(INSERT INTO emp_act (empno, projno, actno) 400000 VVV 111 2
VALUES ('400000','ZZZ',999)
,('400000','VVV',111))
ORDER BY INPUT SEQUENCE;
Figure 519, ORDER BY insert input sequence
Figure 520, GROUP BY syntax
GROUP BY division, department, team
GROUP BY division, department
GROUP BY division
GROUP BY division, team
GROUP BY department, team
GROUP BY department
GROUP BY team
GROUP BY () <= grand-total
Figure 521, Possible groupings
GROUP BY division, department, team
UNION ALL
GROUP BY division, department
UNION ALL
GROUP BY division
UNION ALL
GROUP BY ()
GROUP BY GROUPING SETS ((division, department, team)
,(division, department)
,(division)
,())
GROUP BY ROLLUP (division, department, team)
Figure 522, Three ways to write the same GROUP BY
CREATE VIEW employee_view AS ANSWER
SELECT SUBSTR(workdept,1,1) AS d1 ==================
,workdept AS dept D1 DEPT SEX SALARY
,sex AS sex -- ---- --- ------
,INTEGER(salary) AS salary A A00 F 52750
FROM employee A A00 M 29250
WHERE workdept < 'D20'; A A00 M 46500
COMMIT; B B01 M 41250
C C01 F 23800
C C01 F 28420
C C01 F 38250
D D11 F 21340
SELECT * D D11 F 22250
FROM employee_view D D11 F 29840
ORDER BY 1,2,3,4; D D11 M 18270
D D11 M 20450
D D11 M 24680
D D11 M 25280
D D11 M 27740
D D11 M 32250
Figure 523, GROUP BY Sample Data
SELECT d1, dept, sex ANSWER
,SUM(salary) AS salary ========================
,SMALLINT(COUNT(*)) AS #rows D1 DEPT SEX SALARY #ROWS
FROM employee_view -- ---- --- ------ -----
WHERE dept <> 'ABC' A A00 F 52750 1
GROUP BY d1, dept, sex A A00 M 75750 2
HAVING dept > 'A0' B B01 M 41250 1
AND (SUM(salary) > 100 C C01 F 90470 3
OR MIN(salary) > 10 D D11 F 73430 3
OR COUNT(*) <> 22) D D11 M 148670 6
ORDER BY d1, dept, sex;
Figure 524, Simple GROUP BY
SELECT sex ANSWER
,SUM(salary) AS salary ================
,SMALLINT(COUNT(*)) AS #rows SEX SALARY #ROWS
FROM employee_view --- ------ -----
WHERE sex IN ('F','M') F 52750 1
GROUP BY dept F 90470 3
,sex F 73430 3
ORDER BY sex; M 75750 2
M 41250 1
M 148670 6
Figure 525, GROUP BY on non-displayed field
SELECT SUM(salary) AS salary ANSWER
,SMALLINT(COUNT(*)) AS #rows ============
FROM employee_view SALARY #ROWS
WHERE d1 <> 'X' ------ -----
GROUP BY SUBSTR(dept,3,1) 128500 3
HAVING COUNT(*) <> 99; 353820 13
Figure 526, GROUP BY on derived field, not shown
SELECT SUBSTR(dept,3,1) AS wpart ANSWER
,SUM(salary) AS salary ==================
,SMALLINT(COUNT(*)) AS #rows WPART SALARY #ROWS
FROM employee_view ----- ------ -----
GROUP BY SUBSTR(dept,3,1) 1 353820 13
ORDER BY wpart DESC; 0 128500 3
Figure 527, GROUP BY on derived field, shown
GROUP BY GROUPING SETS ((A,B,C)) is equivalent to GROUP BY A
,B
,C
GROUP BY GROUPING SETS (A,B,C) is equivalent to GROUP BY A
UNION ALL
GROUP BY B
UNION ALL
GROUP BY C
GROUP BY GROUPING SETS (A,(B,C)) is equivalent to GROUP BY A
UNION ALL
GROUP BY B
,BY C
Figure 528, GROUPING SETS in parenthesis vs. not
GROUP BY GROUPING SETS (A) is equivalent to GROUP BY A
,GROUPING SETS (B) ,B
,GROUPING SETS (C) ,C
GROUP BY GROUPING SETS (A) is equivalent to GROUP BY A
,GROUPING SETS ((B,C)) ,B
,C
GROUP BY GROUPING SETS (A) is equivalent to GROUP BY A
,GROUPING SETS (B,C) ,B
UNION ALL
GROUP BY A
,C
Figure 529, Multiple GROUPING SETS
GROUP BY A is equivalent to GROUP BY A
,GROUPING SETS ((B,C)) ,B
,C
Figure 530, Simple GROUP BY expression and GROUPING SETS combined
GROUP BY A is equivalent to GROUP BY A
,B ,B
,GROUPING SETS ((B,C)) ,C
GROUP BY A is equivalent to GROUP BY A
,B ,B
,GROUPING SETS (B,C) ,C
UNION ALL
GROUP BY A
,B
GROUP BY A is equivalent to GROUP BY A
,B ,B
,C ,C
,GROUPING SETS (B,C) UNION ALL
GROUP BY A
,B
,C
Figure 531, Mixing simple GROUP BY expressions and GROUPING SETS
GROUP BY GROUPING SETS ((A,B,C) is equivalent to GROUP BY A
,(A,B) ,B
,(C)) ,C
UNION ALL
GROUP BY A
,B
UNION ALL
GROUP BY C
GROUP BY GROUPING SETS ((A) is equivalent to GROUP BY A
,(B,C) UNION ALL
,(A) GROUP BY B
,A ,C
,((C))) UNION ALL
GROUP BY A
UNION ALL
GROUP BY A
UNION ALL
GROUP BY C
Figure 532, GROUPING SETS with multiple components
GROUP BY GROUPING SETS ((A,B,C) is equivalent to GROUP BY A
,(A,B) ,B
,(A) ,C
,()) UNION ALL
GROUP BY A
,B
is equivalent to UNION ALL
GROUP BY A
UNION ALL
ROLLUP(A,B,C) grand-totl
Figure 533, GROUPING SET with multiple components, using grand-total
GROUP BY GROUPING SETS ((A,B,C) is equivalent to GROUP BY A
,(A,B) ,B
,(A,C) ,C
,(B,C) UNION ALL
,(A) GROUP BY A
,(B) ,B
,(C) UNION ALL
,()) GROUP BY A
,C
UNION ALL
GROUP BY B
is equivalent to ,C
UNION ALL
GROUP BY A
UNION ALL
CUBE(A,B,C) GROUP BY B
UNION ALL
GROUP BY C
UNION ALL
grand-totl
Figure 534, GROUPING SET with multiple components, using grand-total
SELECT d1 ANSWER
,dept ==============================
,sex D1 DEPT SEX SAL #R DF WF SF
,SUM(salary) AS sal -- ---- --- ------ -- -- -- --
,SMALLINT(COUNT(*)) AS #r A A00 F 52750 1 0 0 0
,GROUPING(d1) AS f1 A A00 M 75750 2 0 0 0
,GROUPING(dept) AS fd B B01 M 41250 1 0 0 0
,GROUPING(sex) AS fs C C01 F 90470 3 0 0 0
FROM employee_view D D11 F 73430 3 0 0 0
GROUP BY GROUPING SETS (d1) D D11 M 148670 6 0 0 0
,GROUPING SETS ((dept,sex))
ORDER BY d1
,dept
,sex;
Figure 535, Multiple GROUPING SETS, making one GROUP BY
SELECT d1 ANSWER
,dept ==============================
,sex D1 DEPT SEX SAL #R F1 FD FS
,SUM(salary) AS sal -- ---- --- ------ -- -- -- --
,SMALLINT(COUNT(*)) AS #r A A00 - 128500 3 0 0 1
,GROUPING(d1) AS f1 A - F 52750 1 0 1 0
,GROUPING(dept) AS fd A - M 75750 2 0 1 0
,GROUPING(sex) AS fs B B01 - 41250 1 0 0 1
FROM employee_view B - M 41250 1 0 1 0
GROUP BY GROUPING SETS (d1) C C01 - 90470 3 0 0 1
,GROUPING SETS (dept,sex) C - F 90470 3 0 1 0
ORDER BY d1 D D11 - 222100 9 0 0 1
,dept D - F 73430 3 0 1 0
,sex; D - M 148670 6 0 1 0
Figure 536, Multiple GROUPING SETS, making two GROUP BY results
SELECT d1 ANSWER
,dept ==============================
,sex D1 DEPT SEX SAL #R F1 FD FS
,SUM(salary) AS sal ------------------------------
,SMALLINT(COUNT(*)) AS #r A A00 F 52750 1 0 0 0
,GROUPING(d1) AS f1 A A00 M 75750 2 0 0 0
,GROUPING(dept) AS fd B B01 M 41250 1 0 0 0
,GROUPING(sex) AS fs C C01 F 90470 3 0 0 0
FROM employee_view D D11 F 73430 3 0 0 0
GROUP BY d1 D D11 M 148670 6 0 0 0
,dept
,GROUPING SETS ((dept,sex))
ORDER BY d1
,dept
,sex;
Figure 537, Repeated field essentially ignored
SELECT d1 ANSWER
,dept ==============================
,sex D1 DEPT SEX SAL #R F1 FD FS
,SUM(salary) AS sal ------------------------------
,SMALLINT(COUNT(*)) AS #r A A00 F 52750 1 0 0 0
,GROUPING(d1) AS f1 A A00 M 75750 2 0 0 0
,GROUPING(dept) AS fd A A00 - 128500 3 0 0 1
,GROUPING(sex) AS fs B B01 M 41250 1 0 0 0
FROM employee_view B B01 - 41250 1 0 0 1
GROUP BY d1 C C01 F 90470 3 0 0 0
,DEPT C C01 - 90470 3 0 0 1
,GROUPING SETS (dept,sex) D D11 F 73430 3 0 0 0
ORDER BY d1 D D11 M 148670 6 0 0 0
,dept D D11 - 222100 9 0 0 1
,sex;
Figure 538, Repeated field impacts query result
GROUP BY d1 is equivalent to GROUP BY d1
,dept ,dept
,GROUPING SETS ((dept,sex)) sex
GROUP BY d1 is equivalent to GROUP BY d1
,dept ,dept
,GROUPING SETS (dept,sex) sex
UNION ALL
GROUP BY d1
,dept
,dept
Figure 539, Repeated field impacts query result
GROUP BY ROLLUP(A,B,C) ===> GROUP BY GROUPING SETS((A,B,C)
,(A,B)
,(A)
,())
GROUP BY ROLLUP(C,B) ===> GROUP BY GROUPING SETS((C,B)
,(C)
,())
GROUP BY ROLLUP(A) ===> GROUP BY GROUPING SETS((A)
,())
Figure 540, ROLLUP vs. GROUPING SETS
GROUP BY ROLLUP(A,(B,C)) ===> GROUP BY GROUPING SETS((A,B,C)
,(A)
,())
Figure 541, ROLLUP vs. GROUPING SETS
GROUP BY ROLLUP(A) ===> GROUP BY GROUPING SETS((A,B,C)
,ROLLUP(B,C) ,(A,B)
,(A)
,(B,C)
,(B)
,())
Figure 542, ROLLUP vs. GROUPING SETS
ROLLUP(A) * ROLLUP(B,C) = GROUPING SETS((A,B,C)
,(A,B)
,(A)
GROUPING SETS((A) * GROUPING SETS((B,C) = ,(B,C)
,()) ,(B) ,(B)
()) ,(())
Figure 543, Multiplying GROUPING SETS
SELECT dept ANSWER
,SUM(salary) AS salary ====================
,SMALLINT(COUNT(*)) AS #rows DEPT SALARY #ROWS FD
,GROUPING(dept) AS fd ---- ------ ----- --
FROM employee_view A00 128500 3 0
GROUP BY dept B01 41250 1 0
ORDER BY dept; C01 90470 3 0
D11 222100 9 0
Figure 544, Simple GROUP BY
SELECT dept ANSWER
,SUM(salary) AS salary ====================
,SMALLINT(COUNT(*)) AS #rows DEPT SALARY #ROWS FD
,GROUPING(dept) AS FD ---- ------ ----- --
FROM employee_view A00 128500 3 0
GROUP BY ROLLUP(dept) B01 41250 1 0
ORDER BY dept; C01 90470 3 0
D11 222100 9 0
- 482320 16 1
Figure 545, GROUP BY with ROLLUP
SELECT dept ANSWER
,SUM(salary) AS salary ====================
,SMALLINT(COUNT(*)) AS #rows DEPT SALARY #ROWS FD
,GROUPING(dept) AS fd ---- ------ ----- --
FROM employee_view A00 128500 3 0
GROUP BY dept B01 41250 1 0
UNION ALL C01 90470 3 0
SELECT CAST(NULL AS CHAR(3)) AS dept D11 222100 9 0
,SUM(salary) AS salary - 482320 16 1
,SMALLINT(COUNT(*)) AS #rows
,CAST(1 AS INTEGER) AS fd
FROM employee_view
ORDER BY dept;
Figure 546, ROLLUP done the old-fashioned way
SELECT dept ANSWER
,SUM(salary) AS salary ====================
,SMALLINT(COUNT(*)) AS #rows DEPT SALARY #ROWS FD
,GROUPING(dept) AS fd ---- ------ ----- --
FROM employee_view A00 128500 3 0
GROUP BY dept A00 128500 3 0
,ROLLUP(dept) B01 41250 1 0
ORDER BY dept; B01 41250 1 0
C01 90470 3 0
C01 90470 3 0
D11 222100 9 0
D11 222100 9 0
Figure 547, Repeating a field in GROUP BY and ROLLUP (error)
GROUP BY dept => GROUP BY dept => GROUP BY dept
,ROLLUP(dept) ,GROUPING SETS((dept) UNION ALL
,()) GROUP BY dept
,()
Figure 548, Repeating a field, explanation
SELECT dept ANSWER
,sex ===========================
,SUM(salary) AS salary DEPT SEX SALARY #ROWS FD FS
,SMALLINT(COUNT(*)) AS #rows ---- --- ------ ----- -- --
,GROUPING(dept) AS fd A00 F 52750 1 0 0
,GROUPING(sex) AS fs A00 M 75750 2 0 0
FROM employee_view A00 - 128500 3 0 1
GROUP BY dept B01 M 41250 1 0 0
,ROLLUP(sex) B01 - 41250 1 0 1
ORDER BY dept C01 F 90470 3 0 0
,sex; C01 - 90470 3 0 1
D11 F 73430 3 0 0
D11 M 148670 6 0 0
D11 - 222100 9 0 1
Figure 549, GROUP BY on 1st field, ROLLUP on 2nd
SELECT dept ANSWER
,sex ===========================
,SUM(salary) AS salary DEPT SEX SALARY #ROWS FD FS
,SMALLINT(COUNT(*)) AS #rows ---- --- ------ ----- -- --
,GROUPING(dept) AS fd A00 F 52750 1 0 0
,GROUPING(sex) AS fs A00 M 75750 2 0 0
FROM employee_view A00 - 128500 3 0 1
GROUP BY ROLLUP(dept B01 M 41250 1 0 0
,sex) B01 - 41250 1 0 1
ORDER BY dept C01 F 90470 3 0 0
,sex; C01 - 90470 3 0 1
D11 F 73430 3 0 0
D11 M 148670 6 0 0
D11 - 222100 9 0 1
- - 482320 16 1 1
Figure 550, ROLLUP on DEPT, then SEX
SELECT sex ANSWER
,dept ===========================
,SUM(salary) AS salary SEX DEPT SALARY #ROWS FD FS
,SMALLINT(COUNT(*)) AS #rows --- ---- ------ ----- -- --
,GROUPING(dept) AS fd F A00 52750 1 0 0
,GROUPING(sex) AS fs F C01 90470 3 0 0
FROM employee_view F D11 73430 3 0 0
GROUP BY ROLLUP(sex F - 216650 7 1 0
,dept) M A00 75750 2 0 0
ORDER BY sex M B01 41250 1 0 0
,dept; M D11 148670 6 0 0
M - 265670 9 1 0
- - 482320 16 1 1
Figure 551, ROLLUP on SEX, then DEPT
SELECT sex ANSWER
,dept ===========================
,SUM(salary) AS salary SEX DEPT SALARY #ROWS FD FS
,SMALLINT(COUNT(*)) AS #rows --- ---- ------ ----- -- --
,GROUPING(dept) AS fd F A00 52750 1 0 0
,GROUPING(sex) AS fs F C01 90470 3 0 0
FROM employee_view F D11 73430 3 0 0
GROUP BY GROUPING SETS ((sex, dept) F - 216650 7 1 0
,(sex) M A00 75750 2 0 0
,()) M B01 41250 1 0 0
ORDER BY sex M D11 148670 6 0 0
,dept; M - 265670 9 1 0
- - 482320 16 1 1
Figure 552, ROLLUP on SEX, then DEPT
SELECT sex ANSWER
,dept ===========================
,SUM(salary) AS salary SEX DEPT SALARY #ROWS FD FS
,SMALLINT(COUNT(*)) AS #rows --- ---- ------ ----- -- --
,GROUPING(dept) AS fd F A00 52750 1 0 0
,GROUPING(sex) AS fs F C01 90470 3 0 0
FROM employee_view F D11 73430 3 0 0
GROUP BY ROLLUP(sex) F - 216650 7 1 0
,ROLLUP(dept) M A00 75750 2 0 0
ORDER BY sex M B01 41250 1 0 0
,dept; M D11 148670 6 0 0
M - 265670 9 1 0
- A00 128500 3 0 1
- B01 41250 1 0 1
- C01 90470 3 0 1
- D11 222100 9 0 1
- - 482320 16 1 1
Figure 553, Two independent ROLLUPS
SELECT dept ANSWER
,sex ===========================
,SUM(salary) AS salary DEPT SEX SALARY #ROWS FD FS
,SMALLINT(COUNT(*)) AS #rows ---- --- ------ ----- -- --
,GROUPING(dept) AS fd A00 F 52750 1 0 0
,GROUPING(sex) AS fs A00 M 75750 2 0 0
FROM employee_view B01 M 41250 1 0 0
GROUP BY ROLLUP((dept,sex)) C01 F 90470 3 0 0
ORDER BY dept D11 F 73430 3 0 0
,sex; D11 M 148670 6 0 0
- - 482320 16 1 1
Figure 554, Combined-field ROLLUP
SELECT SUM(salary) AS salary ANSWER
,SMALLINT(COUNT(*)) AS #rows ============
FROM employee_view SALARY #ROWS
GROUP BY ROLLUP(sex ------ -----
,dept) 482320 16
HAVING GROUPING(dept) = 1
AND GROUPING(sex) = 1
ORDER BY salary;
Figure 555, Use HAVING to get only grand-total row
SELECT SUM(salary) AS salary ANSWER
,SMALLINT(COUNT(*)) AS #rows ============
FROM employee_view SALARY #ROWS
GROUP BY GROUPING SETS(()); ------ -----
482320 16
Figure 556, Use GROUPING SETS to get grand-total row
SELECT SUM(salary) AS salary ANSWER
,SMALLINT(COUNT(*)) AS #rows ============
FROM employee_view SALARY #ROWS
GROUP BY (); ------ -----
482320 16
Figure 557, Use GROUP BY to get grand-total row
SELECT SUM(salary) AS salary ANSWER
,SMALLINT(COUNT(*)) AS #rows ============
FROM employee_view; SALARY #ROWS
------ -----
482320 16
Figure 558, Get grand-total row directly
GROUP BY CUBE(A,B,C) ===> GROUP BY GROUPING SETS((A,B,C)
,(A,B)
,(A,C)
,(B,C)
,(A)
,(B)
,(C)
,())
GROUP BY CUBE(C,B) ===> GROUP BY GROUPING SETS((C,B)
,(C)
,(B)
,())
GROUP BY CUBE(A) ===> GROUP BY GROUPING SETS((A)
,())
Figure 559, CUBE vs. GROUPING SETS
GROUP BY CUBE(A,(B,C)) ===> GROUP BY GROUPING SETS((A,B,C)
,(B,C)
,(A)
,())
Figure 560, CUBE vs. GROUPING SETS
GROUP BY CUBE(A,B) ==> GROUPING SETS((A,B,C),(A,B),(A,B,C),(A,B)
,CUBE(B,C) ,(A,B,C),(A,B),(A,C),(A)
,(B,C),(B),(B,C),(B)
,(B,C),(B),(C),())
Figure 561, CUBE vs. GROUPING SETS
SELECT d1 ANSWER
,dept ==============================
,sex D1 DEPT SEX SAL #R F1 FD FS
,INT(SUM(salary)) AS sal -- ---- --- ------ -- -- -- --
,SMALLINT(COUNT(*)) AS #r A A00 F 52750 1 0 0 0
,GROUPING(d1) AS f1 A A00 M 75750 2 0 0 0
,GROUPING(dept) AS fd A A00 - 128500 3 0 0 1
,GROUPING(sex) AS fs A - F 52750 1 0 1 0
FROM employee_view A - M 75750 2 0 1 0
GROUP BY CUBE(d1, dept, sex) A - - 128500 3 0 1 1
ORDER BY d1 B B01 M 41250 1 0 0 0
,dept B B01 - 41250 1 0 0 1
,sex; B - M 41250 1 0 1 0
B - - 41250 1 0 1 1
C C01 F 90470 3 0 0 0
C C01 - 90470 3 0 0 1
C - F 90470 3 0 1 0
C - - 90470 3 0 1 1
D D11 F 73430 3 0 0 0
D D11 M 148670 6 0 0 0
D D11 - 222100 9 0 0 1
D - F 73430 3 0 1 0
D - M 148670 6 0 1 0
D - - 222100 9 0 1 1
- A00 F 52750 1 1 0 0
- A00 M 75750 2 1 0 0
- A00 - 128500 3 1 0 1
- B01 M 41250 1 1 0 0
- B01 - 41250 1 1 0 1
- C01 F 90470 3 1 0 0
- C01 - 90470 3 1 0 1
- D11 F 73430 3 1 0 0
- D11 M 148670 6 1 0 0
- D11 - 222100 9 1 0 1
- - F 216650 7 1 1 0
- - M 265670 9 1 1 0
- - - 482320 16 1 1 1
Figure 562, CUBE example
SELECT d1 ANSWER
,dept ==============================
,sex D1 DEPT SEX SAL #R F1 FD FS
,INT(SUM(salary)) AS sal -- ---- --- ------ -- -- -- --
,SMALLINT(COUNT(*)) AS #r A A00 F 52750 1 0 0 0
,GROUPING(d1) AS f1 A A00 M 75750 2 0 0 0
,GROUPING(dept) AS fd etc... (same as prior query)
,GROUPING(sex) AS fs
FROM employee_view
GROUP BY GROUPING SETS ((d1, dept, sex)
,(d1,dept)
,(d1,sex)
,(dept,sex)
,(d1)
,(dept)
,(sex)
,())
ORDER BY d1
,dept
,sex;
Figure 563, CUBE expressed using multiple GROUPING SETS
SELECT d1 ANSWER
,dept ==============================
,sex D1 DEPT SEX SAL #R F1 FD FS
,INT(SUM(salary)) AS sal ------------------------------
,SMALLINT(COUNT(*)) AS #r A A00 F 52750 1 0 0 0
,GROUPING(d1) AS f1 A A00 M 75750 2 0 0 0
,GROUPING(dept) AS fd B B01 M 41250 1 0 0 0
,GROUPING(sex) AS fs C C01 F 90470 3 0 0 0
FROM employee_VIEW D D11 F 73430 3 0 0 0
GROUP BY CUBE((d1, dept, sex)) D D11 M 148670 6 0 0 0
ORDER BY d1 - - - 482320 16 1 1 1
,dept
,sex;
Figure 564, CUBE on compound fields
GROUP BY CUBE((A,B,C)) => GROUP BY GROUING SETS((A,B,C) => GROUP BY A
,()) ,B
,C
UNION ALL
GROUP BY()
Figure 565, CUBE on compound field, explanation
SELECT d1 AS d1 ANSWER
,dept AS dpt ==================
,sex AS sx D1 DPT SX SAL R
,INT(SUM(salary)) AS sal -- --- -- ------ -
,SMALLINT(COUNT(*)) AS r A A00 F 52750 1
FROM employee_VIEW A A00 M 75750 2
GROUP BY d1 B B01 M 41250 1
,dept C C01 F 90470 3
,sex D D11 F 73430 3
ORDER BY 1,2,3; D D11 M 148670 6
Figure 566, Basic GROUP BY example
DESIRED SUB-TOTALS EQUIVILENT TO
================== =====================================
D1, DEPT, and SEX. GROUP BY GROUPING SETS ((d1,dept,sex)
D1 and DEPT. ,(d1,dept)
D1 and SEX. ,(d1,sex)
D1. ,(d1)
SEX. ,(sex)
Grand total. EQUIVILENT TO ,())
=======================
GROUP BY ROLLUP(d1,dept)
,ROLLUP(sex)
Figure 567, Sub-totals that we want to get
SELECT *
FROM (SELECT d1 AS d1
,dept AS dpt
,sex AS sx
,INT(SUM(salary)) AS sal
,SMALLINT(COUNT(*)) AS #r
,SMALLINT(GROUPING(d1)) AS g1
,SMALLINT(GROUPING(dept)) AS gd
,SMALLINT(GROUPING(sex)) AS gs
FROM EMPLOYEE_VIEW ANSWER
GROUP BY CUBE(d1,dept,sex) ============================
)AS xxx D1 DPT SX SAL #R G1 GD GS
WHERE (g1,gd,gs) = (0,0,0) -- --- -- ------ -- -- -- --
OR (g1,gd,gs) = (0,0,1) A A00 F 52750 1 0 0 0
OR (g1,gd,gs) = (0,1,0) A A00 M 75750 2 0 0 0
OR (g1,gd,gs) = (0,1,1) A A00 - 128500 3 0 0 1
OR (g1,gd,gs) = (1,1,0) A - F 52750 1 0 1 0
OR (g1,gd,gs) = (1,1,1) A - M 75750 2 0 1 0
ORDER BY 1,2,3; A - - 128500 3 0 1 1
B B01 M 41250 1 0 0 0
B B01 - 41250 1 0 0 1
B - M 41250 1 0 1 0
B - - 41250 1 0 1 1
C C01 F 90470 3 0 0 0
C C01 - 90470 3 0 0 1
C - F 90470 3 0 1 0
C - - 90470 3 0 1 1
D D11 F 73430 3 0 0 0
D D11 M 148670 6 0 0 0
D D11 - 222100 9 0 0 1
D - F 73430 3 0 1 0
D - M 148670 6 0 1 0
D - - 222100 9 0 1 1
- - F 216650 7 1 1 0
- - M 265670 9 1 1 0
- - - 482320 16 1 1 1
Figure 568, Get lots of sub-totals, using CUBE
(G1,GD,GS) = (0,0,0) <== D1, DEPT, SEX
(G1,GD,GS) = (0,0,1) <== D1, DEPT
(G1,GD,GS) = (0,1,0) <== D1, SEX
(G1,GD,GS) = (0,1,1) <== D1,
(G1,GD,GS) = (1,1,0) <== SEX,
(G1,GD,GS) = (1,1,1) <== grand total
Figure 569, Predicates used - explanation
SELECT d1 ANSWER
,dept =====================
,sex D1 DEPT SEX SAL #R
,INT(SUM(salary)) AS sal -- ---- --- ------ --
,SMALLINT(COUNT(*)) AS #r A A00 F 52750 1
FROM employee_view A A00 M 75750 2
GROUP BY ROLLUP(d1,dept) A A00 - 128500 3
,ROLLUP(sex) A - F 52750 1
ORDER BY 1,2,3; A - M 75750 2
A - - 128500 3
B B01 M 41250 1
B B01 - 41250 1
B - M 41250 1
B - - 41250 1
C C01 F 90470 3
C C01 - 90470 3
C - F 90470 3
C - - 90470 3
D D11 F 73430 3
D D11 M 148670 6
D D11 - 222100 9
D - F 73430 3
D - M 148670 6
D - - 222100 9
- - F 216650 7
- - M 265670 9
- - - 482320 16
Figure 570, Get lots of sub-totals, using ROLLUP
SELECT dept, job
,COUNT(*)
FROM staff
GROUP BY dept, job
ORDER BY dept, job;
Figure 571, GROUP BY with ORDER BY
WITH staff2 (dept, avgsal) AS ANSWER
(SELECT dept =================
,AVG(salary) ID NAME DEPT
FROM staff --- -------- ----
GROUP BY dept 160 Molinare 10
HAVING AVG(salary) > 18000 210 Lu 10
) 240 Daniels 10
SELECT a.id 260 Jones 10
,a.name
,a.dept
FROM staff a
,staff2 b
WHERE a.dept = b.dept
ORDER BY a.id;
Figure 572, GROUP BY on one side of join - using common table expression
SELECT a.id ANSWER
,a.name =================
,a.dept ID NAME DEPT
FROM staff a --- -------- ----
,(SELECT dept AS dept 160 Molinare 10
,AVG(salary) AS avgsal 210 Lu 10
FROM staff 240 Daniels 10
GROUP BY dept 260 Jones 10
HAVING AVG(salary) > 18000
)AS b
WHERE a.dept = b.dept
ORDER BY a.id;
Figure 573, GROUP BY on one side of join - using full-select
SELECT COUNT(*) AS c1 ANSWER
FROM staff ======
WHERE id < 1; 0
SELECT COUNT(*) AS c1 ANSWER
FROM staff ======
WHERE id < 1 no row
GROUP BY id;
Figure 574, COUNT and No Rows
CREATE VIEW staff_v1 AS STAFF_V1 STAFF_V2
SELECT id, name +-----------+ +---------+
FROM staff |ID|NAME | |ID|JOB |
WHERE ID BETWEEN 10 AND 30; |--|--------| |--|------|
|10|Sanders | |20|Sales |
CREATE VIEW staff_v2 AS |20|Pernal | |30|Clerk |
SELECT id, job |30|Marenghi| |30|Mgr |
FROM staff +-----------+ |40|Sales |
WHERE id BETWEEN 20 AND 50 |50|Mgr |
UNION ALL +---------+
SELECT id, 'Clerk' AS job
FROM staff
WHERE id = 30;
Figure 575, Sample Views used in Join Examples
Figure 576, Join Syntax #1
SELECT v1.id JOIN ANSWER
,v1.name =================
,v2.job ID NAME JOB
FROM staff_v1 v1 -- -------- -----
,staff_v2 v2 20 Pernal Sales
WHERE v1.id = v2.id 30 Marenghi Clerk
ORDER BY v1.id 30 Marenghi Mgr
,v2.job;
Figure 577, Sample two-table join
SELECT v1.id JOIN ANSWER
,v2.job =================
,v3.name ID JOB NAME
FROM staff_v1 v1 -- ----- --------
,staff_v2 v2 30 Clerk Marenghi
,staff_v1 v3 30 Mgr Marenghi
WHERE v1.id = v2.id
AND v2.id = v3.id
AND v3.name LIKE 'M%'
ORDER BY v1.name
,v2.job;
Figure 578, Sample three-table join
Figure 579, Join Syntax #2
SELECT v1.id JOIN ANSWER
,v1.name =================
,v2.job ID NAME JOB
FROM staff_v1 v1 -- -------- -----
INNER JOIN 20 Pernal Sales
staff_v2 v2 30 Marenghi Clerk
ON v1.id = v2.id 30 Marenghi Mgr
ORDER BY v1.id
,v2.job;
Figure 580, Sample two-table inner join
SELECT v1.id STAFF_V1 STAFF_V2
,v2.job +-----------+ +---------+
,v3.name |ID|NAME | |ID|JOB |
FROM staff_v1 v1 |--|--------| |--|------|
JOIN |10|Sanders | |20|Sales |
staff_v2 v2 |20|Pernal | |30|Clerk |
ON v1.id = v2.id |30|Marenghi| |30|Mgr |
JOIN +-----------+ |40|Sales |
staff_v1 v3 |50|Mgr |
ON v2.id = v3.id JOIN ANSWER +---------+
WHERE v3.name LIKE 'M%' =================
ORDER BY v1.name ID JOB NAME
,v2.job; -- ----- --------
30 Clerk Marenghi
30 Mgr Marenghi
Figure 581, Sample three-table inner join
SELECT * ANSWER
FROM staff_v1 v1 ====================
LEFT OUTER JOIN ID NAME ID JOB
staff_v2 v2 -- -------- -- -----
ON 1 = 1 10 Sanders - -
AND v1.id = v2.id 20 Pernal 20 Sales
ORDER BY v1.id 30 Marenghi 30 Clerk
,v2.job; 30 Marenghi 30 Mgr
Figure 582, Sample Views used in Join Examples
SELECT * ANSWER
FROM staff_v1 v1 ====================
LEFT OUTER JOIN ID NAME ID JOB
staff_v2 v2 -- -------- -- -----
ON 1 = 1 20 Pernal 20 Sales
WHERE v1.id = v2.id 30 Marenghi 30 Clerk
ORDER BY v1.id 30 Marenghi 30 Mgr
,v2.job;
Figure 583, Sample Views used in Join Examples
STAFF_V1 STAFF_V2 INNER-JOIN ANSWER
+-----------+ +---------+ ====================
|ID|NAME | |ID|JOB | Join on ID ID NAME ID JOB
|--|--------| |--|------| ==========> -- -------- -- -----
|10|Sanders | |20|Sales | 20 Pernal 20 Sales
|20|Pernal | |30|Clerk | 30 Marenghi 30 Clerk
|30|Marenghi| |30|Mgr | 30 Marenghi 30 Mgr
+-----------+ |40|Sales |
|50|Mgr |
+---------+
Figure 584, Example of Inner Join
SELECT * ANSWER
FROM staff_v1 v1 ====================
,staff_v2 v2 ID NAME ID JOB
WHERE v1.id = v2.id -- -------- -- -----
ORDER BY v1.id 20 Pernal 20 Sales
,v2.job; 30 Marenghi 30 Clerk
30 Marenghi 30 Mgr
Figure 585, Inner Join SQL (1 of 2)
SELECT * ANSWER
FROM staff_v1 v1 ====================
INNER JOIN ID NAME ID JOB
staff_v2 v2 -- -------- -- -----
ON v1.id = v2.id 20 Pernal 20 Sales
ORDER BY v1.id 30 Marenghi 30 Clerk
,v2.job; 30 Marenghi 30 Mgr
Figure 586, Inner Join SQL (2 of 2)
SELECT * ANSWER
FROM staff_v1 v1 ====================
INNER JOIN ID NAME ID JOB
staff_v2 v2 -- -------- -- -----
ON v1.id = v2.id 20 Pernal 20 Sales
AND v2.job <> 'Mgr' 30 Marenghi 30 Clerk
ORDER BY v1.id
,v2.job;
Figure 587, Inner join, using ON check
SELECT * ANSWER
FROM staff_v1 v1 ====================
INNER JOIN ID NAME ID JOB
staff_v2 v2 -- -------- -- -----
ON v1.id = v2.id 20 Pernal 20 Sales
WHERE v2.job <> 'Mgr' 30 Marenghi 30 Clerk
ORDER BY v1.id
,v2.job;
Figure 588, Inner join, using WHERE check
STAFF_V1 STAFF_V2 LEFT-OUTER-JOIN ANSWER
+-----------+ +---------+ ======================
|ID|NAME | |ID|JOB | ID NAME ID JOB
|--|--------| |--|------| =========> -- -------- -- -----
|10|Sanders | |20|Sales | 10 Sanders - -
|20|Pernal | |30|Clerk | 20 Pernal 20 Sales
|30|Marenghi| |30|Mgr | 30 Marenghi 30 Clerk
+-----------+ |40|Sales | 30 Marenghi 30 Mgr
|50|Mgr |
+---------+
Figure 589, Example of Left Outer Join
SELECT *
FROM staff_v1 v1
LEFT OUTER JOIN
staff_v2 v2
ON v1.id = v2.id
ORDER BY 1,4;
Figure 590, Left Outer Join SQL (1 of 2)
SELECT v1.* <== This join gets all
,v2.* rows in STAFF_V1
FROM staff_v1 v1 that match rows
,staff_v2 v2 in STAFF_V2.
WHERE v1.id = v2.id
UNION
SELECT v1.* <== This query gets
,CAST(NULL AS SMALLINT) AS id all the rows in
,CAST(NULL AS CHAR(5)) AS job STAFF_V1 with no
FROM staff_v1 v1 matching rows
WHERE v1.id NOT IN in STAFF_V2.
(SELECT id FROM staff_v2)
ORDER BY 1,4;
Figure 591, Left Outer Join SQL (2 of 2)
SELECT * ANSWER
FROM staff_v1 v1 ====================
LEFT OUTER JOIN ID NAME ID JOB
staff_v2 v2 -- -------- -- -----
ON v1.id = v2.id 10 Sanders - -
AND v2.job <> 'Mgr' 20 Pernal 20 Sales
ORDER BY v1.id 30 Marenghi 30 Clerk
,v2.job;
Figure 592, ON check on table being joined to
SELECT * ANSWER
FROM staff_v1 v1 ====================
LEFT OUTER JOIN ID NAME ID JOB
staff_v2 v2 -- -------- -- -----
ON v1.id = v2.id 20 Pernal 20 Sales
WHERE v2.job <> 'Mgr' 30 Marenghi 30 Clerk
ORDER BY v1.id
,v2.job;
Figure 593, WHERE check on table being joined to (1 of 2)
SELECT * ANSWER
FROM staff_v1 v1 ====================
LEFT OUTER JOIN ID NAME ID JOB
staff_v2 v2 -- -------- -- -----
ON v1.id = v2.id 10 Sanders - -
WHERE (v2.job <> 'Mgr' 20 Pernal 20 Sales
OR v2.job IS NULL) 30 Marenghi 30 Clerk
ORDER BY v1.id
,v2.job;
Figure 594, WHERE check on table being joined to (2 of 2)
SELECT * ANSWER
FROM staff_v1 v1 ====================
LEFT OUTER JOIN ID NAME ID JOB
staff_v2 v2 -- -------- -- -----
ON v1.id = v2.id 10 Sanders - -
AND v1.name > 'N' 20 Pernal 20 Sales
ORDER BY v1.id 30 Marenghi - -
,v2.job;
Figure 595, ON check on table being joined from
SELECT * ANSWER
FROM staff_v1 v1 ====================
LEFT OUTER JOIN ID NAME ID JOB
staff_v2 v2 -- -------- -- -----
ON v1.id = v2.id 10 Sanders - -
WHERE v1.name > 'N' 20 Pernal 20 Sales
ORDER BY v1.id
,v2.job;
Figure 596, WHERE check on table being joined from
STAFF_V1 STAFF_V2 RIGHT-OUTER-JOIN ANSWER
+-----------+ +---------+ =======================
|ID|NAME | |ID|JOB | ID NAME ID JOB
|--|--------| |--|------| =========> -- -------- -- -----
|10|Sanders | |20|Sales | 20 Pernal 20 Sales
|20|Pernal | |30|Clerk | 30 Marenghi 30 Clerk
|30|Marenghi| |30|Mgr | 30 Marenghi 30 Mgr
+-----------+ |40|Sales | - - 40 Sales
|50|Mgr | - - 50 Mgr
+---------+
Figure 597, Example of Right Outer Join
SELECT * ANSWER
FROM staff_v1 v1 ====================
RIGHT OUTER JOIN ID NAME ID JOB
staff_v2 v2 -- -------- -- -----
ON v1.id = v2.id 20 Pernal 20 Sales
ORDER BY v2.id 30 Marenghi 30 Clerk
,v2.job; 30 Marenghi 30 Mgr
- - 40 Sales
- - 50 Mgr
Figure 598, Right Outer Join SQL (1 of 2)
SELECT v1.* ANSWER
,v2.* ====================
FROM staff_v1 v1 ID NAME ID JOB
,staff_v2 v2 -- -------- -- -----
WHERE v1.id = v2.id 20 Pernal 20 Sales
UNION 30 Marenghi 30 Clerk
SELECT CAST(NULL AS SMALLINT) AS id 30 Marenghi 30 Mgr
,CAST(NULL AS VARCHAR(9)) AS name - - 40 Sales
,v2.* - - 50 Mgr
FROM staff_v2 v2
WHERE v2.id NOT IN
(SELECT id FROM staff_v1)
ORDER BY 3,4;
Figure 599, Right Outer Join SQL (2 of 2)
STAFF_V1 STAFF_V2 FULL-OUTER-JOIN ANSWER
+-----------+ +---------+ ======================
|ID|NAME | |ID|JOB | ID NAME ID JOB
|--|--------| |--|------| =========> -- -------- -- -----
|10|Sanders | |20|Sales | 10 Sanders - -
|20|Pernal | |30|Clerk | 20 Pernal 20 Sales
|30|Marenghi| |30|Mgr | 30 Marenghi 30 Clerk
+-----------+ |40|Sales | 30 Marenghi 30 Mgr
|50|Mgr | - - 40 Sales
+---------+ - - 50 Mgr
Figure 600, Example of Full Outer Join
SELECT * ANSWER
FROM staff_v1 v1 ====================
FULL OUTER JOIN ID NAME ID JOB
staff_v2 v2 -- -------- -- -----
ON v1.id = v2.id 10 Sanders - -
ORDER BY v1.id 20 Pernal 20 Sales
,v2.id 30 Marenghi 30 Clerk
,v2.job; 30 Marenghi 30 Mgr
- - 40 Sales
- - 50 Mgr
Figure 601, Full Outer Join SQL
SELECT v1.* ANSWER
,v2.* ====================
FROM staff_v1 v1 ID NAME ID JOB
,staff_v2 v2 -- -------- -- -----
WHERE v1.id = v2.id 10 Sanders - -
UNION 20 Pernal 20 Sales
SELECT v1.* 30 Marenghi 30 Clerk
,CAST(NULL AS SMALLINT) AS id 30 Marenghi 30 Mgr
,CAST(NULL AS CHAR(5)) AS job - - 40 Sales
FROM staff_v1 v1 - - 50 Mgr
WHERE v1.id NOT IN
(SELECT id FROM staff_v2)
UNION
SELECT CAST(NULL AS SMALLINT) AS id
,CAST(NULL AS VARCHAR(9)) AS name
,v2.*
FROM staff_v2 v2
WHERE v2.id NOT IN
(SELECT id FROM staff_v1)
ORDER BY 1,3,4;
Figure 602, Full Outer Join SQL
SELECT * ANSWER
FROM staff_v1 v1 ====================
FULL OUTER JOIN ID NAME ID JOB
staff_v2 v2 -- -------- -- -----
ON v1.id = v2.id 10 Sanders - -
ORDER BY v1.id 20 Pernal 20 Sales
,v2.id 30 Marenghi 30 Clerk
,v2.job; 30 Marenghi 30 Mgr
- - 40 Sales
- - 50 Mgr
Figure 603, Full Outer Join, match on keys
SELECT * ANSWER
FROM staff_v1 v1 ====================
FULL OUTER JOIN ID NAME ID JOB
staff_v2 v2 -- -------- -- -----
ON v1.id = v2.id 10 Sanders - -
AND v1.id > 20 20 Pernal - -
ORDER BY v1.id 30 Marenghi 30 Clerk
,v2.id 30 Marenghi 30 Mgr
,v2.job; - - 20 Sales
- - 40 Sales
- - 50 Mgr
Figure 604, Full Outer Join, match on keys > 20
SELECT * ANSWER
FROM staff_v1 v1 ====================
FULL OUTER JOIN ID NAME ID JOB
staff_v2 v2 -- -------- -- -----
ON v1.id = v2.id 10 Sanders - -
AND +1 = -1 20 Pernal - -
ORDER BY v1.id 30 Marenghi - -
,v2.id - - 20 Sales
,v2.job; - - 30 Clerk
- - 30 Mgr
- - 40 Sales
- - 50 Mgr
Figure 605, Full Outer Join, match on keys (no rows match)
SELECT * ANSWER
FROM staff_v1 v1 ====================
FULL OUTER JOIN ID NAME ID JOB
staff_v2 v2 -- -------- -- -----
ON +1 = -1 10 Sanders - -
ORDER BY v1.id 20 Pernal - -
,v2.id 30 Marenghi - -
,v2.job; - - 20 Sales
- - 30 Clerk
- - 30 Mgr
- - 40 Sales
- - 50 Mgr
Figure 606, Full Outer Join, don't match on keys (no rows match)
SELECT * ANSWER
FROM staff_v1 v1 ====================
FULL OUTER JOIN ID NAME ID JOB
staff_v2 v2 -- -------- -- -----
ON +1 <> -1 10 Sanders 20 Sales
ORDER BY v1.id 10 Sanders 30 Clerk
,v2.id 10 Sanders 30 Mgr
,v2.job; 10 Sanders 40 Sales
10 Sanders 50 Mgr
20 Pernal 20 Sales
STAFF_V1 STAFF_V2 20 Pernal 30 Clerk
+-----------+ +---------+ 20 Pernal 30 Mgr
|ID|NAME | |ID|JOB | 20 Pernal 40 Sales
|--|--------| |--|------| 20 Pernal 50 Mgr
|10|Sanders | |20|Sales | 30 Marenghi 20 Sales
|20|Pernal | |30|Clerk | 30 Marenghi 30 Clerk
|30|Marenghi| |30|Mgr | 30 Marenghi 30 Mgr
+-----------+ |40|Sales | 30 Marenghi 40 Sales
|50|Mgr | 30 Marenghi 50 Mgr
+---------+
Figure 607, Full Outer Join, don't match on keys (all rows match)
SELECT * ANSWER
FROM staff_v1 v1 ====================
FULL JOIN ID NAME ID JOB
staff_v2 v2 -- -------- -- -----
ON v1.id = v2.id 20 Pernal 20 Sales
WHERE v1.id = v2.id 30 Marenghi 30 Clerk
ORDER BY 1,3,4; 30 Marenghi 30 Mgr
Figure 608, Full Outer Join, turned into an inner join by WHERE
STAFF_V1 STAFF_V2
+-----------+ +---------+ ANSWER
|ID|NAME | |ID|JOB | OUTER-JOIN CRITERIA ============
|--|--------| |--|------| ==================> ???, DEPENDS
|10|Sanders | |20|Sales | V1.ID = V2.ID
|20|Pernal | |30|Clerk | V1.ID < 30
|30|Marenghi| |30|Mgr |
+-----------+ |40|Sales |
|50|Mgr |
+---------+
Figure 609, Outer join V1.ID < 30, sample data
SELECT * ANSWER
FROM staff_v1 v1 ====================
FULL JOIN ID NAME ID JOB
staff_v2 v2 -- -------- -- -----
ON v1.id = v2.id 10 Sanders - -
WHERE v1.id < 30 20 Pernal 20 Sales
ORDER BY 1,3,4;
Figure 610, Outer join V1.ID < 30, check applied in WHERE (after join)
SELECT * ANSWER
FROM staff_v1 v1 ====================
FULL JOIN ID NAME ID JOB
staff_v2 v2 -- -------- -- -----
ON v1.id = v2.id 10 Sanders - -
AND v1.id < 30 20 Pernal 20 Sales
ORDER BY 1,3,4; 30 Marenghi - -
- - 30 Clerk
- - 30 Mgr
- - 40 Sales
- - 50 Mgr
Figure 611, Outer join V1.ID < 30, check applied in ON (during join)
SELECT * ANSWER
FROM (SELECT * ====================
FROM staff_v1 ID NAME ID JOB
WHERE id < 30) AS v1 -- -------- -- -----
FULL OUTER JOIN 10 Sanders - -
staff_v2 v2 20 Pernal 20 Sales
ON v1.id = v2.id - - 30 Clerk
ORDER BY 1,3,4; - - 30 Mgr
- - 40 Sales
- - 50 Mgr
Figure 612, Outer join V1.ID < 30, check applied in WHERE (before join)
SELECT * ANSWER
FROM staff_v1 v1 ====================
FULL OUTER JOIN ID NAME ID JOB
staff_v2 v2 -- -------- -- -----
ON v1.id = v2.id 10 Sanders - -
WHERE v1.id < 30 20 Pernal 20 Sales
OR v1.id IS NULL - - 40 Sales
ORDER BY 1,3,4; - - 50 Mgr
Figure 613, Outer join V1.ID < 30, (gives wrong answer - see text)
SELECT * ANSWER
FROM staff_v1 v1 ====================
FULL OUTER JOIN ID NAME ID JOB
staff_v2 v2 -- -------- -- -----
ON v1.id = v2.id 10 Sanders - -
WHERE v1.id < 30 20 Pernal 20 Sales
OR v1.id = v2.id 30 Marenghi 30 Clerk
OR v1.id IS NULL 30 Marenghi 30 Mgr
ORDER BY 1,3,4; - - 40 Sales
- - 50 Mgr
Figure 614, Outer join V1.ID < 30, (gives wrong answer - see text)
STAFF_V1 STAFF_V2 CARTESIAN-PRODUCT
+-----------+ +---------+ ====================
|ID|NAME | |ID|JOB | ID NAME ID JOB
|--|--------| |--|------| =========> -- -------- -- -----
|10|Sanders | |20|Sales | 10 Sanders 20 Sales
|20|Pernal | |30|Clerk | 10 Sanders 30 Clerk
|30|Marenghi| |30|Mgr | 10 Sanders 30 Mgr
+-----------+ |40|Sales | 10 Sanders 40 Sales
|50|Mgr | 10 Sanders 50 Mgr
+---------+ 20 Pernal 20 Sales
20 Pernal 30 Clerk
20 Pernal 30 Mgr
20 Pernal 40 Sales
20 Pernal 50 Mgr
30 Marenghi 20 Sales
30 Marenghi 30 Clerk
30 Marenghi 30 Mgr
30 Marenghi 40 Sales
30 Marenghi 50 Mgr
Figure 615, Example of Cartesian Product
SELECT *
FROM staff_v1 v1
,staff_v2 v2
ORDER BY v1.id
,v2.id
,v2.job;
Figure 616, Cartesian Product SQL (1 of 2)
SELECT *
FROM staff_v1 v1
INNER JOIN
staff_v2 v2
ON 'A' <> 'B'
ORDER BY v1.id
,v2.id
,v2.job;
Figure 617, Cartesian Product SQL (2 of 2)
SELECT v2a.id ANSWER
,v2a.job ===========
,v2b.id ID JOB ID
FROM staff_v2 v2a -- ----- --
,staff_v2 v2b 20 Sales 20
WHERE v2a.job = v2b.job 20 Sales 40
AND v2a.id < 40 30 Clerk 30
ORDER BY v2a.id 30 Mgr 30
,v2b.id; 30 Mgr 50
Figure 618, Partial Cartesian Product SQL
SELECT v2.job ANSWER
,COUNT(*) AS #rows ===========
FROM staff_v1 v1 JOB #ROWS
,staff_v2 v2 ----- -----
GROUP BY v2.job Clerk 3
ORDER BY #rows Mgr 6
,v2.job; Sales 6
Figure 619, Partial Cartesian Product SQL, with GROUP BY
SELECT COALESCE(v1.id,v2.id) AS id ANSWER
,COALESCE(v1.name,'?') AS name =================
,v2.job ID NAME JOB
FROM staff_v1 v1 -- -------- -----
FULL OUTER JOIN 10 Sanders -
staff_v2 v2 20 Pernal Sales
ON v1.id = v2.id 30 Marenghi Clerk
ORDER BY v1.id 30 Marenghi Mgr
,v2.job; 40 ? Sales
50 ? Mgr
Figure 620, Use of COALESCE function in outer join
STAFF_V1 STAFF_V2 ANSWER
+-----------+ +---------+ NON-MATCHING ===================
|ID|NAME | |ID|JOB | OUTER-JOIN ID NAME ID JOB
|--|--------| |--|------| ===========> -- ------- -- -----
|10|Sanders | |20|Sales | 10 Sanders - -
|20|Pernal | |30|Clerk | - - 40 Sales
|30|Marenghi| |30|Mgr | - - 50 Mgr
+-----------+ |40|Sales |
|50|Mgr |
+---------+
Figure 621, Example of outer join, only getting the non-matching rows
SELECT v1.* <== Get all the rows
,CAST(NULL AS SMALLINT) AS id in STAFF_V1 that
,CAST(NULL AS CHAR(5)) AS job have no matching
FROM staff_v1 v1 row in STAFF_V2.
WHERE v1.id NOT IN
(SELECT id FROM staff_v2)
UNION
SELECT CAST(NULL AS SMALLINT) AS id <== Get all the rows
,CAST(NULL AS VARCHAR(9)) AS name in STAFF_V2 that
,v2.* have no matching
FROM staff_v2 v2 row in STAFF_V1.
WHERE v2.id NOT IN
(SELECT id FROM staff_v1)
ORDER BY 1,3,4;
Figure 622, Outer Join SQL, getting only non-matching rows
SELECT *
FROM (SELECT v1.* ,'V1' AS flag FROM staff_v1 v1) AS v1
FULL OUTER JOIN
(SELECT v2.* ,'V2' AS flag FROM staff_v2 v2) AS v2
ON v1.id = v2.id
WHERE v1.flag IS NULL ANSWER
OR v2.flag IS NULL =============================
ORDER BY v1.id ID NAME FLAG ID JOB FLAG
,v2.id -- ------- ---- -- ----- ----
,v2.job; 10 Sanders V1 - - -
- - - 40 Sales V2
- - - 50 Mgr V2
Figure 623, Outer Join SQL, getting only non-matching rows
WITH
v1 AS (SELECT v1.* ,'V1' AS flag FROM staff_v1 v1)
,v2 AS (SELECT v2.* ,'V2' AS flag FROM staff_v2 v2)
SELECT *
FROM v1 v1 ANSWER
FULL OUTER JOIN =============================
v2 v2 ID NAME FLAG ID JOB FLAG
ON v1.id = v2.id -- ------- ---- -- ----- ----
WHERE v1.flag IS NULL 10 Sanders V1 - - -
OR v2.flag IS NULL - - - 40 Sales V2
ORDER BY v1.id, v2.id, v2.job; - - - 50 Mgr V2
Figure 624, Outer Join SQL, getting only non-matching rows
SELECT * STAFF_V1 STAFF_V2
FROM staff_v1 v1 +-----------+ +---------+
FULL OUTER JOIN |ID|NAME | |ID|JOB |
staff_v2 v2 |--|--------| |--|------|
ON v1.id = v2.id |10|Sanders | |20|Sales |
WHERE v1.id IS NULL |20|Pernal | |30|Clerk |
OR v2.id IS NULL |30|Marenghi| |30|Mgr |
ORDER BY v1.id +-----------+ |40|Sales |
,v2.id |50|Mgr |
,v2.job; +---------+
Figure 625, Outer Join SQL, getting only non-matching rows
STAFF_V1 STAFF_V2 ANSWER
+-----------+ +---------+ LEFT OUTER JOIN ===================
|ID|NAME | |ID|JOB | ==============> ID NAME ID JOB
|--|--------| |--|------| V1.ID = V2.ID -- ------- -- -----
|10|Sanders | |20|Sales | V1.ID <> 30 10 Sanders - -
|20|Pernal | |30|Clerk | 20 Pernal 20 Sales
|30|Marenghi| |30|Mgr |
+-----------+ |40|Sales |
|50|Mgr |
+---------+
Figure 626, Left outer join example
SELECT v1.id ANSWER
,v1.name =================
,v2.job ID NAME JOB
FROM staff_v1 v1 -- -------- -----
LEFT OUTER JOIN 10 Sanders -
staff_v2 v2 20 Pernal Sales
ON v1.id = v2.id
WHERE v1.id <> 30
ORDER BY v1.id ;
Figure 627, Outer Join done in FROM phrase of SQL
SELECT v1.id ANSWER
,v1.name =================
,(SELECT v2.job ID NAME JB
FROM staff_v2 v2 -- -------- -----
WHERE v1.id = v2.id) AS jb 10 Sanders -
FROM staff_v1 v1 20 Pernal Sales
WHERE v1.id <> 30
ORDER BY v1.id;
Figure 628, Outer Join done in SELECT phrase of SQL
SELECT v1.id ANSWER
,v1.name =================
,(SELECT v2.job ID NAME JB
FROM staff_v2 v2 -- -------- -----
WHERE v1.id = v2.id) AS jb 10 Sanders -
FROM staff_v1 v1 20 Pernal Sales
ORDER BY v1.id;
Figure 629, Outer Join done in SELECT phrase of SQL - gets error
SELECT v1.id ANSWER
,v1.name =================
,(SELECT MAX(v2.job) ID NAME JB
FROM staff_v2 v2 -- -------- -----
WHERE v1.id = v2.id) AS jb 10 Sanders -
FROM staff_v1 v1 20 Pernal Sales
ORDER BY v1.id; 30 Marenghi Mgr
Figure 630, Outer Join done in SELECT phrase of SQL - fixed
SELECT v1.id ANSWER
,v1.name =================
,MAX(v2.job) AS jb ID NAME JB
FROM staff_v1 v1 -- -------- -----
LEFT OUTER JOIN 10 Sanders -
staff_v2 v2 20 Pernal Sales
ON v1.id = v2.id 30 Marenghi Mgr
GROUP BY v1.id
,v1.name
ORDER BY v1.id ;
Figure 631, Same as prior query - using join and GROUP BY
SELECT v2.id ANSWER
,CASE ===========
WHEN v2.job <> 'Mgr' ID J2
THEN v2.job -- --------
ELSE (SELECT v1.name 20 Sales
FROM staff_v1 v1 30 Clerk
WHERE v1.id = v2.id) 30 Marenghi
END AS j2 40 Sales
FROM staff_v2 v2 50 -
ORDER BY v2.id
,j2;
Figure 632, Sample Views used in Join Examples
SELECT v2.id ANSWER
,v2.job ====================
,(SELECT v1.name ID JOB NAME N2
FROM staff_v1 v1 -- ----- -------- --
WHERE v2.id = v1.id) 20 Sales Pernal 6
,(SELECT LENGTH(v1.name) AS n2 30 Clerk Marenghi 8
FROM staff_v1 v1 30 Mgr Marenghi 8
WHERE v2.id = v1.id) 40 Sales - -
FROM staff_v2 v2 50 Mgr - -
ORDER BY v2.id
,v2.job;
Figure 633, Outer Join done in SELECT, 2 columns
SELECT v2.id ANSWER
,v2.job ====================
,v1.name ID JOB NAME N2
,LENGTH(v1.name) AS n2 -- ----- -------- --
FROM staff_v2 v2 20 Sales Pernal 6
LEFT OUTER JOIN 30 Clerk Marenghi 8
staff_v1 v1 30 Mgr Marenghi 8
ON v2.id = v1.id 40 Sales - -
ORDER BY v2.id 50 Mgr - -
,v2.job;
Figure 634, Outer Join done in FROM, 2 columns
SELECT v1.id ANSWER
,v1.name ==================
,(SELECT SUM(x1.id) ID NAME SUM_ID
FROM staff_v1 x1 -- -------- ------
WHERE x1.id <= v1.id 10 Sanders 10
)AS sum_id 20 Pernal 30
FROM staff_v1 v1 30 Marenghi 60
ORDER BY v1.id
,v2.job;
Figure 635, Running total, using JOIN in SELECT
SELECT v1.id ANSWER
,v1.name ==================
,SUM(id) OVER(ORDER BY id) AS sum_id ID NAME SUM_ID
FROM staff_v1 v1 -- -------- ------
ORDER BY v1.id; 10 Sanders 10
20 Pernal 30
30 Marenghi 60
Figure 636, Running total, using OLAP function
STAFF_V1 STAFF_V2 ANSWER
+-----------+ +---------+ =================
|ID|NAME | |ID|JOB | OUTER-JOIN CRITERIA ID NAME JOB
|--|--------| |--|------| ==================> -- -------- -----
|10|Sanders | |20|Sales | V1.ID = V2.ID 10 Sanders -
|20|Pernal | |30|Clerk | V2.JOB LIKE 'S%' 20 Pernal Sales
|30|Marenghi| |30|Mgr | 30 Marenghi -
+-----------+ |40|Sales |
|50|Mgr |
+---------+
Figure 637, Outer join, with WHERE filter
SELECT v1.id ANSWER (WRONG)
,v1.name =================
,v2.job ID NAME JOB
FROM staff_v1 v1 -- -------- -----
LEFT OUTER JOIN 20 Pernal Sales
staff_v2 v2
ON v1.id = v2.id
WHERE v2.job LIKE 'S%'
ORDER BY v1.id
,v2.job;
Figure 638, Outer Join, WHERE done after - wrong
SELECT v1.id ANSWER
,v1.name =================
,v2.job ID NAME JOB
FROM staff_v1 v1 -- -------- -----
LEFT OUTER JOIN 10 Sanders -
(SELECT * 20 Pernal Sales
FROM staff_v2 30 Marenghi -
WHERE job LIKE 'S%'
)AS v2
ON v1.id = v2.id
ORDER BY v1.id
,v2.job;
Figure 639, Outer Join, WHERE done before - correct
SELECT v1.id ANSWER
,v1.name =================
,(SELECT v2.job ID NAME JOB
FROM staff_v2 v2 -- -------- -----
WHERE v1.id = v2.id 10 Sanders -
AND v2.job LIKE 'S%') 20 Pernal Sales
FROM staff_v1 v1 30 Marenghi -
ORDER BY v1.id
,job;
Figure 640, Outer Join, WHERE done independently - correct
SELECT eee.empno ANSWER
,aaa.projno ==========================
,aaa.actno EMPNO PROJNO ACTNO FORMAT
,ppp.photo_format AS format ------ ------ ----- ------
FROM employee eee 000010 MA2110 10 -
LEFT OUTER JOIN 000070 - - -
emp_act aaa 000130 - - bitmap
ON eee.empno = aaa.empno 000150 MA2112 60 bitmap
AND aaa.emptime = 1 000150 MA2112 180 bitmap
AND aaa.projno LIKE 'M%1%' 000160 MA2113 60 -
LEFT OUTER JOIN
emp_photo ppp
ON eee.empno = ppp.empno
AND ppp.photo_format LIKE 'b%'
WHERE eee.lastname LIKE '%A%'
AND eee.empno < '000170'
AND eee.empno <> '000030'
ORDER BY eee.empno;
Figure 641, Join from Employee to Activity and Photo
SELECT eee.empno ANSWER
,aaa.projno ==========================
,aaa.actno EMPNO PROJNO ACTNO FORMAT
,ppp.photo_format AS format ------ ------ ----- ------
FROM employee eee 000010 MA2110 10 -
LEFT OUTER JOIN 000070 - - -
emp_act aaa 000130 - - -
ON eee.empno = aaa.empno 000150 MA2112 60 bitmap
AND aaa.emptime = 1 000150 MA2112 180 bitmap
AND aaa.projno LIKE 'M%1%' 000160 MA2113 60 -
LEFT OUTER JOIN
emp_photo ppp
ON aaa.empno = ppp.empno
AND ppp.photo_format LIKE 'b%'
WHERE eee.lastname LIKE '%A%'
AND eee.empno < '000170'
AND eee.empno <> '000030'
ORDER BY eee.empno;
Figure 642, Join from Employee to Activity, then from Activity to Photo
SELECT ddd.deptno AS dp#
,eee.empno
,aaa.projno
,ppp.projname
FROM (SELECT *
FROM department
WHERE deptname LIKE '%A%'
AND deptname NOT LIKE '%U%'
AND deptno < 'E'
)AS ddd
INNER JOIN
employee eee
ON ddd.deptno = eee.workdept
AND eee.lastname LIKE '%A%'
LEFT OUTER JOIN
emp_act aaa
ON aaa.empno = eee.empno
AND aaa.emptime <= 0.5
INNER JOIN
project ppp
ON aaa.projno = ppp.projno
AND ppp.projname LIKE '%Q%'
ORDER BY ddd.deptno
,eee.empno ANSWER
,aaa.projno; ================================
DP# EMPNO PROJNO PROJNAME
--- ------ ------ --------------
C01 000030 IF1000 QUERY SERVICES
C01 000130 IF1000 QUERY SERVICES
Figure 643, Complex join - wrong
SELECT ddd.deptno AS dp#
,eee.empno
,xxx.projno
,xxx.projname
FROM (SELECT *
FROM department
WHERE deptname LIKE '%A%'
AND deptname NOT LIKE '%U%'
AND deptno < 'E'
)AS ddd
INNER JOIN
employee eee
ON ddd.deptno = eee.workdept
AND eee.lastname LIKE '%A%'
LEFT OUTER JOIN
(SELECT aaa.empno
,aaa.emptime
,aaa.projno
,ppp.projname
FROM emp_act aaa
INNER JOIN
project ppp
ON aaa.projno = ppp.projno
AND ppp.projname LIKE '%Q%'
)AS xxx
ON xxx.empno = eee.empno
AND xxx.emptime <= 0.5
ORDER BY ddd.deptno
,eee.empno ANSWER
,xxx.projno; ================================
DP# EMPNO PROJNO PROJNAME
--- ------ ------ --------------
C01 000030 IF1000 QUERY SERVICES
C01 000130 IF1000 QUERY SERVICES
D21 000070 - -
D21 000240 - -
Figure 644, Complex join - right
CREATE TABLE table1 TABLE1 TABLE2
(t1a CHAR(1) NOT NULL +-------+ +-----------+
,t1b CHAR(2) NOT NULL |T1A|T1B| |T2A|T2B|T2C|
,PRIMARY KEY(t1a)); |---|---| |---|---|---|
COMMIT; |A |AA | |A |A |A |
|B |BB | |B |A | - |
CREATE TABLE table2 |C |CC | +-----------+
(t2a CHAR(1) NOT NULL +-------+ "-" = null
,t2b CHAR(1) NOT NULL
,t2c CHAR(1));
INSERT INTO table1 VALUES ('A','AA'),('B','BB'),('C','CC');
INSERT INTO table2 VALUES ('A','A','A'),('B','A',NULL);
Figure 645, Sample tables used in sub-query examples
Figure 646, Sub-query syntax diagram
SELECT * ANSWER
FROM table1 =======
WHERE t1a = T1A T1B
(SELECT t2a --- --
FROM table2 A AA
WHERE t2a = 'A');
SUB-Q TABLE1 TABLE2
RESLT +-------+ +-----------+
+---+ |T1A|T1B| |T2A|T2B|T2C|
|T2A| |---|---| |---|---|---|
|---| |A |AA | |A |A |A |
|A | |B |BB | |B |A | - |
+---+ |C |CC | +-----------+
+-------+ "-" = null
Figure 647, No keyword sub-query, works
SELECT * ANSWER
FROM table1 =======
WHERE t1a =
(SELECT t2a
FROM table2);
SUB-Q TABLE1 TABLE2
RESLT +-------+ +-----------+
+---+ |T1A|T1B| |T2A|T2B|T2C|
|T2A| |---|---| |---|---|---|
|---| |A |AA | |A |A |A |
|A | |B |BB | |B |A | - |
|B | |C |CC | +-----------+
+---+ +-------+ "-" = null
Figure 648, No keyword sub-query, fails
SELECT * ANSWER SUB-Q TABLE1 TABLE2
FROM table1 ======= RESLT +-------+ +-----------+
WHERE t1a > ANY T1A T1B +---+ |T1A|T1B| |T2A|T2B|T2C|
(SELECT t2a --- -- |T2A| |---|---| |---|---|---|
FROM table2); B BB |---| |A |AA | |A |A |A |
C CC |A | |B |BB | |B |A | - |
|B | |C |CC | +-----------+
+---+ +-------+ "-" = null
Figure 649, ANY sub-query
SUB-QUERY CHECK EQUIVALENT COLUMN FUNCTION
================ ============================
> ANY(sub-qurey) > MINIMUM(sub-query results)
< ANY(sub-query) < MAXIMUM(sub-query results)
> ALL(sub-query) > MAXIMUM(sub-query results)
< ALL(sub-query) < MINIMUM(sub-query results)
Figure 650, ANY and ALL vs. column functions
SELECT * ANSWER SUB-Q
FROM table1 ======= RESLT
WHERE t1a = ALL T1A T1B +---+
(SELECT t2b --- -- |T2B|
FROM table2 A AA |---|
WHERE t2b >= 'A'); |A |
|A |
+---+
Figure 651, ALL sub-query, with non-empty sub-query result
SELECT * ANSWER SUB-Q
FROM table1 ======= RESLT
WHERE t1a = ALL T1A T1B +---+
(SELECT t2b --- -- |T2B|
FROM table2 A AA |---|
WHERE t2b >= 'X'); B BB +---+
C CC
Figure 652, ALL sub-query, with empty sub-query result
SELECT * ANSWER
FROM table1 ======
WHERE t1a = ALL 0 rows
(SELECT t2b
FROM table2 SQ-#1 SQ-#2 TABLE1 TABLE2
WHERE t2b >= 'X') RESLT RESLT +-------+ +-----------+
AND 0 <> +---+ +---+ |T1A|T1B| |T2A|T2B|T2C|
(SELECT COUNT(*) |T2B| |(*)| |---|---| |---|---|---|
FROM table2 |---| |---| |A |AA | |A |A |A |
WHERE t2b >= 'X'); +---+ |0 | |B |BB | |B |A | - |
+---+ |C |CC | +-----------+
+-------+ "-" = null
Figure 653, ALL sub-query, with extra check for empty set
SELECT * ANSWER TABLE1 TABLE2
FROM table1 ======= +-------+ +-----------+
WHERE EXISTS T1A T1B |T1A|T1B| |T2A|T2B|T2C|
(SELECT * --- -- |---|---| |---|---|---|
FROM table2); A AA |A |AA | |A |A |A |
B BB |B |BB | |B |A | - |
C CC |C |CC | +-----------+
+-------+ "-" = null
Figure 654, EXISTS sub-query, always returns a match
SELECT * ANSWER
FROM table1 ======
WHERE EXISTS 0 rows
(SELECT *
FROM table2
WHERE t2b >= 'X');
Figure 655, EXISTS sub-query, always returns a non-match
SELECT * ANSWER TABLE1 TABLE2
FROM table1 ======= +-------+ +-----------+
WHERE EXISTS T1A T1B |T1A|T1B| |T2A|T2B|T2C|
(SELECT COUNT(*) --- -- |---|---| |---|---|---|
FROM table2 A AA |A |AA | |A |A |A |
WHERE t2b = 'X'); B BB |B |BB | |B |A | - |
C CC |C |CC | +-----------+
+-------+ "-" = null
Figure 656, EXISTS sub-query, always returns a match
SELECT * ANSWERS TABLE1 TABLE2
FROM table1 ======= +-------+ +-----------+
WHERE NOT EXISTS T1A T1B |T1A|T1B| |T2A|T2B|T2C|
(SELECT * --- --- |---|---| |---|---|---|
FROM table2 A AA |A |AA | |A |A |A |
WHERE t2c >= 'A' |B |BB | |B |A | - |
AND t2c <> t1a); |C |CC | +-----------+
+-------+ "-" = null
SELECT *
FROM table1
WHERE t1a = ALL
(SELECT t2c
FROM table2
WHERE t2c >= 'A');
Figure 657, NOT EXISTS vs. ALL, ignore nulls, find match
SELECT * ANSWERS TABLE1 TABLE2
FROM table1 ======= +-------+ +-----------+
WHERE NOT EXISTS T1A T1B |T1A|T1B| |T2A|T2B|T2C|
(SELECT * --- --- |---|---| |---|---|---|
FROM table2 A AA |A |AA | |A |A |A |
WHERE t2c >= 'X' B BB |B |BB | |B |A | - |
AND t2c <> t1a); C CC |C |CC | +-----------+
+-------+ "-" = null
SELECT *
FROM table1
WHERE t1a = ALL
(SELECT t2c
FROM table2
WHERE t2c >= 'X');
Figure 658, NOT EXISTS vs. ALL, ignore nulls, no match
SELECT * ANSWER TABLE1 TABLE2
FROM table1 ======= +-------+ +-----------+
WHERE NOT EXISTS T1A T1B |T1A|T1B| |T2A|T2B|T2C|
(SELECT * --- --- |---|---| |---|---|---|
FROM table2 A AA |A |AA | |A |A |A |
WHERE t2c <> t1a); |B |BB | |B |A | - |
|C |CC | +-----------+
+-------+ "-" = null
SELECT * ANSWER
FROM table1 =======
WHERE t1a = ALL no rows
(SELECT t2c
FROM table2);
Figure 659, NOT EXISTS vs. ALL, process nulls
SELECT * SELECT * SELECT *
FROM table2 FROM table2 FROM table2
WHERE t2c <> 'A'; WHERE t2c <> 'B'; WHERE t2c <> 'C';
ANSWER ANSWER ANSWER
=========== =========== ===========
T2A T2B T2C T2A T2B T2C T2A T2B T2C
--- --- --- --- --- --- --- --- ---
no rows A A A A A A
Figure 660, List of values in T2C <> T1A value
SELECT * ANSWER TABLE1 TABLE2
FROM table1 ======= +-------+ +-----------+
WHERE NOT EXISTS no rows |T1A|T1B| |T2A|T2B|T2C|
(SELECT * |---|---| |---|---|---|
FROM table2 |A |AA | |A |A |A |
WHERE t2c <> t1a |B |BB | |B |A | - |
OR t2c IS NULL); |C |CC | +-----------+
+-------+ "-" = null
Figure 661, NOT EXISTS - same as ALL
SELECT * ANSWER TABLE1 TABLE2
FROM table1 ======= +-------+ +-----------+
WHERE t1a IN T1A T1B |T1A|T1B| |T2A|T2B|T2C|
(SELECT t2a --- -- |---|---| |---|---|---|
FROM table2); A AA |A |AA | |A |A |A |
B BB |B |BB | |B |A | - |
|C |CC | +-----------+
+-------+ "-" = null
Figure 662, IN sub-query example, two matches
SELECT * ANSWER
FROM table1 ======
WHERE t1a IN 0 rows
(SELECT t2a
FROM table2
WHERE t2a >= 'X');
Figure 663, IN sub-query example, no matches
SELECT * ANSWERS TABLE2
FROM table2 =========== +-----------+
WHERE t2c IN T2A T2B T2C |T2A|T2B|T2C|
(SELECT t2c --- --- --- |---|---|---|
FROM table2); A A A |A |A |A |
|B |A | - |
SELECT * +-----------+
FROM table2 "-" = null
WHERE t2c = ANY
(SELECT t2c
FROM table2);
Figure 664, IN and = ANY sub-query examples, with nulls
SELECT * ANSWER TABLE1 TABLE2
FROM table1 ====== +-------+ +-----------+
WHERE t1a NOT IN 0 rows |T1A|T1B| |T2A|T2B|T2C|
(SELECT t2c |---|---| |---|---|---|
FROM table2); |A |AA | |A |A |A |
|B |BB | |B |A | - |
|C |CC | +-----------+
+-------+ "-" = null
Figure 665, NOT IN sub-query example, no matches
SELECT * ANSWER TABLE1 TABLE2
FROM table1 ======= +-------+ +-----------+
WHERE t1a NOT IN T1A T1B |T1A|T1B| |T2A|T2B|T2C|
(SELECT t2c --- -- |---|---| |---|---|---|
FROM table2 B BB |A |AA | |A |A |A |
WHERE t2c IS NOT NULL); C CC |B |BB | |B |A | - |
|C |CC | +-----------+
+-------+ "-" = null
Figure 666, NOT IN sub-query example, matches
SELECT * ANSWER TABLE1 TABLE2
FROM table1 ======= +-------+ +-----------+
WHERE NOT EXISTS T1A T1B |T1A|T1B| |T2A|T2B|T2C|
(SELECT * --- -- |---|---| |---|---|---|
FROM table2 B BB |A |AA | |A |A |A |
WHERE t1a = t2c); C CC |B |BB | |B |A | - |
|C |CC | +-----------+
+-------+ "-" = null
Figure 667, NOT EXISTS sub-query example, matches
SELECT * ANSWER TABLE1 TABLE2
FROM table1 ======= +-------+ +-----------+
WHERE t1a IN T1A T1B |T1A|T1B| |T2A|T2B|T2C|
(SELECT t2a --- -- |---|---| |---|---|---|
FROM table2); A AA |A |AA | |A |A |A |
B BB |B |BB | |B |A | - |
|C |CC | +-----------+
+-------+ "-" = null
Figure 668, Uncorrelated sub-query
SELECT * ANSWER TABLE1 TABLE2
FROM table1 ======= +-------+ +-----------+
WHERE t1a IN T1A T1B |T1A|T1B| |T2A|T2B|T2C|
(SELECT t2a --- -- |---|---| |---|---|---|
FROM table2 A AA |A |AA | |A |A |A |
WHERE t1a = t2a); B BB |B |BB | |B |A | - |
|C |CC | +-----------+
+-------+ "-" = null
Figure 669, Correlated sub-query
SELECT * ANSWER TABLE2
FROM table2 aa =========== +-----------+
WHERE EXISTS T2A T2B T2C |T2A|T2B|T2C|
(SELECT * --- --- --- |---|---|---|
FROM table2 bb A A A |A |A |A |
WHERE aa.t2a = bb.t2b); |B |A | - |
+-----------+
"-" = null
Figure 670,Correlated sub-query, with correlation names
SELECT * ANSWER TABLE1 TABLE2
FROM table1 ====== +-------+ +-----------+
WHERE (t1a,t1b) IN 0 rows |T1A|T1B| |T2A|T2B|T2C|
(SELECT t2a, t2b |---|---| |---|---|---|
FROM table2); |A |AA | |A |A |A |
|B |BB | |B |A | - |
|C |CC | +-----------+
+-------+ "-" = null
SELECT * ANSWER
FROM table1 ======
WHERE EXISTS 0 rows
(SELECT *
FROM table2
WHERE t1a = t2a
AND t1b = t2b);
Figure 671, Multi-field sub-queries, equal checks
SELECT * ANSWER TABLE1 TABLE2
FROM table1 ======= +-------+ +-----------+
WHERE EXISTS T1A T1B |T1A|T1B| |T2A|T2B|T2C|
(SELECT * --- -- |---|---| |---|---|---|
FROM table2 A AA |A |AA | |A |A |A |
WHERE t1a = t2a B BB |B |BB | |B |A | - |
AND t1b >= t2b); |C |CC | +-----------+
+-------+ "-" = null
Figure 672, Multi-field sub-query, with non-equal check
SELECT empno ANSWER
,lastname =========================
,salary EMPNO LASTNAME SALARY
FROM employee ------ --------- --------
WHERE salary > 000010 HAAS 52750.00
(SELECT MAX(salary) 000110 LUCCHESSI 46500.00
FROM employee
WHERE empno NOT IN
(SELECT empno
FROM emp_act
WHERE projno LIKE 'MA%'))
ORDER BY 1;
Figure 673, Nested Sub-Queries
SELECT COUNT(*) AS #rows ANSWER
,MAX(deptno) AS maxdpt =============
FROM department #ROWS MAXDEPT
WHERE deptname LIKE 'Z%' ----- -------
ORDER BY 1; 0 null
Figure 674, Getting a null value from a not null field
SELECT * TABLE1 TABLE2
FROM table1 t1 +-------+ +-----------+
WHERE 0 = |T1A|T1B| |T2A|T2B|T2C|
(SELECT COUNT(*) |---|---| |---|---|---|
FROM table2 t2 |A |AA | |A |A |A |
WHERE t1.t1a = t2.t2c); |B |BB | |B |A | - |
|C |CC | +-----------+
SELECT * +-------+ "-" = null
FROM table1 t1
WHERE NOT EXISTS
(SELECT * ANSWER
FROM table2 t2 =======
WHERE t1.t1a = t2.t2c); T1A T1B
--- ---
SELECT * B BB
FROM table1 C CC
WHERE t1a NOT IN
(SELECT t2c
FROM table2
WHERE t2c IS NOT NULL);
Figure 675, Sub-queries, true if none match
SELECT t1.* ANSWER
FROM table1 t1 =======
LEFT OUTER JOIN T1A T1B
table2 t2 --- ---
ON t1.t1a = t2.t2c B BB
WHERE t2.t2c IS NULL; C CC
Figure 676, Outer join, true if none match
SELECT * TABLE1 TABLE2
FROM table1 t1 +-------+ +-----------+
WHERE EXISTS |T1A|T1B| |T2A|T2B|T2C|
(SELECT * |---|---| |---|---|---|
FROM table2 t2 |A |AA | |A |A |A |
WHERE t1.t1a = t2.t2c); |B |BB | |B |A | - |
|C |CC | +-----------+
SELECT * +-------+ "-" = null
FROM table1 t1
WHERE 1 <=
(SELECT COUNT(*) ANSWER
FROM table2 t2 =======
WHERE t1.t1a = t2.t2c); T1A T1B
--- ---
SELECT * A AA
FROM table1
WHERE t1a = ANY
(SELECT t2c
FROM table2);
SELECT *
FROM table1
WHERE t1a = SOME
(SELECT t2c
FROM table2);
SELECT *
FROM table1
WHERE t1a IN
(SELECT t2c
FROM table2);
Figure 677, Sub-queries, true if any match
WITH t2 AS TABLE1 TABLE2
(SELECT DISTINCT t2c +-------+ +-----------+
FROM table2 |T1A|T1B| |T2A|T2B|T2C|
) |---|---| |---|---|---|
SELECT t1.* |A |AA | |A |A |A |
FROM table1 t1 |B |BB | |B |A | - |
,t2 |C |CC | +-----------+
WHERE t1.t1a = t2.t2c; +-------+ "-" = null
SELECT t1.*
FROM table1 t1 ANSWER
,(SELECT DISTINCT t2c =======
FROM table2 T1A T1B
)AS t2 --- ---
WHERE t1.t1a = t2.t2c; A AA
SELECT t1.*
FROM table1 t1
INNER JOIN
(SELECT DISTINCT t2c
FROM table2
)AS t2
ON t1.t1a = t2.t2c;
Figure 678, Joins, true if any match
SELECT * TABLE1 TABLE2
FROM table1 t1 +-------+ +-----------+
WHERE 10 = |T1A|T1B| |T2A|T2B|T2C|
(SELECT COUNT(*) |---|---| |---|---|---|
FROM table2 t2 |A |AA | |A |A |A |
WHERE t1.t1a = t2.t2b); |B |BB | |B |A | - |
|C |CC | +-----------+
SELECT * +-------+ "-" = null
FROM table1
WHERE EXISTS
(SELECT t2b ANSWER
FROM table2 ======
WHERE t1a = t2b 0 rows
GROUP BY t2b
HAVING COUNT(*) = 10);
SELECT *
FROM table1
WHERE t1a IN
(SELECT t2b
FROM table2
GROUP BY t2b
HAVING COUNT(*) = 10);
Figure 679, Sub-queries, true if ten match (1 of 2)
SELECT * ANSWER
FROM table1 ======
WHERE (t1a,10) IN 0 rows
(SELECT t2b, COUNT(*)
FROM table2
GROUP BY t2b);
Figure 680, Sub-queries, true if ten match (2 of 2)
WITH t2 AS TABLE1 TABLE2
(SELECT t2b +-------+ +-----------+
FROM table2 |T1A|T1B| |T2A|T2B|T2C|
GROUP BY t2b |---|---| |---|---|---|
HAVING COUNT(*) = 10 |A |AA | |A |A |A |
) |B |BB | |B |A | - |
SELECT t1.* |C |CC | +-----------+
FROM table1 t1 +-------+ "-" = null
,t2
WHERE t1.t1a = t2.t2b;
ANSWER
SELECT t1.* ======
FROM table1 t1 0 rows
,(SELECT t2b
FROM table2
GROUP BY t2b
HAVING COUNT(*) = 10
)AS t2
WHERE t1.t1a = t2.t2b;
SELECT t1.*
FROM table1 t1
INNER JOIN
(SELECT t2b
FROM table2
GROUP BY t2b
HAVING COUNT(*) = 10
)AS t2
ON t1.t1a = t2.t2b;
Figure 681, Joins, true if ten match
SELECT * TABLE1 TABLE2
FROM table1 +-------+ +-----------+
WHERE t1a = ALL |T1A|T1B| |T2A|T2B|T2C|
(SELECT t2b |---|---| |---|---|---|
FROM table2); |A |AA | |A |A |A |
|B |BB | |B |A | - |
SELECT * |C |CC | +-----------+
FROM table1 +-------+ "-" = null
WHERE NOT EXISTS
(SELECT * ANSWER
FROM table2 =======
WHERE t1a <> t2b); T1A T1B
--- ---
A AA
Figure 682, Sub-queries, true if all match, find rows
SELECT * ANSWER
FROM table1 =======
WHERE t1a = ALL T1A T1B
(SELECT t2b --- ---
FROM table2 A AA
WHERE t2b >= 'X'); B BB
C CC
SELECT *
FROM table1
WHERE NOT EXISTS
(SELECT *
FROM table2
WHERE t1a <> t2b
AND t2b >= 'X');
Figure 683, Sub-queries, true if all match, empty set
SELECT * TABLE1 TABLE2
FROM table1 +-------+ +-----------+
WHERE t1a = ALL |T1A|T1B| |T2A|T2B|T2C|
(SELECT t2b |---|---| |---|---|---|
FROM table2 |A |AA | |A |A |A |
WHERE t2b >= 'X') |B |BB | |B |A | - |
AND 0 <> |C |CC | +-----------+
(SELECT COUNT(*) +-------+ "-" = null
FROM table2
WHERE t2b >= 'X'); ANSWER
======
SELECT * 0 rows
FROM table1
WHERE t1a IN
(SELECT MAX(t2b)
FROM table2
WHERE t2b >= 'X'
HAVING COUNT(DISTINCT t2b) = 1);
Figure 684, Sub-queries, true if all match, and at least one value found
R1 R1 R1 R1 R1 R1
UNION UNION INTERSECT INTERSECT EXCEPT EXCEPT
R2 ALL R2 ALL R2 ALL
R1 R2 R2 R2 R2
-- -- ----- ----- --------- ----- ------ ------
A A A A A A E A
A A B A B A C
A B C A C B C
B B D A B E
B B E A C
C C B
C D B
C B
E B
B
C
C
C
C
D
E
Figure 685, Examples of Union, Except, and Intersect
Figure 686, Union, Except, and Intersect syntax
CREATE VIEW R1 (R1)
AS VALUES ('A'),('A'),('A'),('B'),('B'),('C'),('C'),('C'),('E');
CREATE VIEW R2 (R2)
AS VALUES ('A'),('A'),('B'),('B'),('B'),('C'),('D'); ANSWER
======
SELECT R1 R1 R2
FROM R1 -- --
ORDER BY R1; A A
A A
SELECT R2 A B
FROM R2 B B
ORDER BY R2; B B
C C
C D
C
E
Figure 687, Query sample views
SELECT R1 R1 R2 UNION UNION ALL
FROM R1 -- -- ===== =========
UNION A A A A
SELECT R2 A A B A
FROM R2 A B C A
ORDER BY 1; B B D A
B B E A
C C B
SELECT R1 C D B
FROM R1 C B
UNION ALL E B
SELECT R2 B
FROM R2 C
ORDER BY 1; C
C
C
D
E
Figure 688, Union and Union All SQL
SELECT R1 R1 R2 INTERSECT INTERSECT ALL
FROM R1 -- -- ========= =============
INTERSECT A A A A
SELECT R2 A A B A
FROM R2 A B C B
ORDER BY 1; B B B
B B C
SELECT R1 C C
FROM R1 C D
INTERSECT ALL C
SELECT R2 E
FROM R2
ORDER BY 1;
Figure 689, Intersect and Intersect All SQL
SELECT R1 R1 R1
FROM R1 EXCEPT EXCEPT ALL
EXCEPT R1 R2 R2 R2
SELECT R2 -- -- ===== ==========
FROM R2 A A E A
ORDER BY 1; A A C
A B C
SELECT R1 B B E
FROM R1 B B
EXCEPT ALL C C
SELECT R2 C D
FROM R2 C
ORDER BY 1; E
Figure 690, Except and Except All SQL (R1 on top)
SELECT R2 R2 R2
FROM R2 EXCEPT EXCEPT ALL
EXCEPT R1 R2 R1 R1
SELECT R1 -- -- ===== ==========
FROM R1 A A D B
ORDER BY 1; A A D
A B
SELECT R2 B B
FROM R2 B B
EXCEPT ALL C C
SELECT R1 C D
FROM R1 C
ORDER BY 1; E
Figure 691, Except and Except All SQL (R2 on top)
SELECT R1 (SELECT R1 SELECT R1 R1 R2
FROM R1 FROM R1 FROM R1 -- --
UNION UNION UNION A A
SELECT R2 SELECT R2 (SELECT R2 A A
FROM R2 FROM R2 FROM R2 A B
EXCEPT )EXCEPT EXCEPT B B
SELECT R2 SELECT R2 SELECT R2 B B
FROM R2 FROM R2 FROM R2 C C
ORDER BY 1; ORDER BY 1; )ORDER BY 1; C D
C
E
ANSWER ANSWER ANSWER
====== ====== ======
E E A
B
C
E
Figure 692, Use of parenthesis in Union
CREATE TABLE sales_data_2002
(sales_date DATE NOT NULL
,daily_seq# INTEGER NOT NULL
,cust_id INTEGER NOT NULL
,amount DEC(10,2) NOT NULL
,invoice# INTEGER NOT NULL
,sales_rep CHAR(10) NOT NULL
,CONSTRAINT C CHECK (YEAR(sales_date) = 2002)
,PRIMARY KEY (sales_date, daily_seq#));
CREATE TABLE sales_data_2003
(sales_date DATE NOT NULL
,daily_seq# INTEGER NOT NULL
,cust_id INTEGER NOT NULL
,amount DEC(10,2) NOT NULL
,invoice# INTEGER NOT NULL
,sales_rep CHAR(10) NOT NULL
,CONSTRAINT C CHECK (YEAR(sales_date) = 2003)
,PRIMARY KEY (sales_date, daily_seq#));
CREATE VIEW sales_data AS
SELECT *
FROM sales_data_2002
UNION ALL
SELECT *
FROM sales_data_2003;
Figure 693, Define view to combine yearly tables
INSERT INTO sales_data VALUES ('2002-11-22',1,123,100.10,996,'SUE')
,('2002-11-22',2,123,100.10,997,'JOHN')
,('2003-01-01',1,123,100.10,998,'FRED')
,('2003-01-01',2,123,100.10,999,'FRED');
UPDATE sales_data
SET amount = amount / 2
WHERE sales_rep = 'JOHN';
DELETE
FROM sales_data
WHERE sales_date = '2003-01-01'
AND daily_seq# = 2;
Figure 694, Insert, update, and delete using view
SALES_DATE DAILY_SEQ# CUST_ID AMOUNT INVOICE# SALES_REP
---------- ---------- ------- ------ -------- ---------
01/01/2003 1 123 100.10 998 FRED
11/22/2002 1 123 100.10 996 SUE
11/22/2002 2 123 50.05 997 JOHN
Figure 695, View contents after insert, update, delete
CREATE TABLE staff_summary AS
(SELECT dept
,COUNT(*) AS count_rows
,SUM(id) AS sum_id
FROM staff
GROUP BY dept)
DATA INITIALLY DEFERRED REFRESH IMMEDIATE;
Figure 696, Sample materialized query table DDL
ORIGINAL QUERY OPTIMIZED QUERY
============== =================================
SELECT dept SELECT Q1.dept AS "dept"
,AVG(id) ,Q1.sum_id / Q1.count_rows
FROM staff FROM staff_summary AS Q1
GROUP BY dept
Figure 697, Original and optimized queries
Figure 698, Materialized query table DDL, syntax diagram
CREATE TABLE emp_summary AS
(SELECT workdept AS dept
,sex AS sex
,COUNT_BIG(*) AS num_rows
,COUNT(salary) AS num_salary
,SUM(salary) AS sum_salary
,GROUPING(workdept) AS fd
,GROUPING(sex) AS fs
FROM employee
WHERE job = 'MANAGER'
AND lastname LIKE '%S%'
GROUP BY CUBE(workdept, sex)
)DATA INITIALLY DEFERRED REFRESH IMMEDIATE
ENABLE QUERY OPTIMIZATION
MAINTAINED BY SYSTEM;
Figure 699, Typical materialized query table definition
MATERIALIZED QUERY TABLE ALLOWABLE ACTIONS ON TABLE
========================== =====================================
REFRESH MAINTAINED BY REFRESH TABLE INSERT/UPDATE/DELETE
========= ============= ============= ====================
DEFERRED SYSTEM yes no
USER no yes
IMMEDIATE SYSTEM yes no
Figure 700, Materialized query table options vs. allowable actions
CREATE TABLE staff_names AS
(SELECT dept
,COUNT(*) AS count_rows
,SUM(salary) AS sum_salary
,AVG(salary) AS avg_salary
,MAX(salary) AS max_salary
,MIN(salary) AS min_salary
,STDDEV(salary) AS std_salary
,VARIANCE(salary) AS var_salary
,CURRENT TIMESTAMP AS last_change
FROM staff
WHERE TRANSLATE(name) LIKE '%A%'
AND salary > 10000
GROUP BY dept
HAVING COUNT(*) = 1
)DATA INITIALLY DEFERRED REFRESH DEFERRED;
Figure 701, Refresh deferred materialized query table DDL
Figure 702, Refresh age command, syntax
SET CURRENT REFRESH AGE 0;
SET CURRENT REFRESH AGE = ANY;
SET CURRENT REFRESH AGE = 99999999999999;
Figure 703, Set refresh age command
SELECT CURRENT REFRESH AGE AS age_ts
,CURRENT TIMESTAMP AS current_ts
FROM sysibm.sysdummy1;
Figure 704, Selecting refresh age
SELECT CHAR(tabschema,10) AS schema
,CHAR(tabname,20) AS table
,type
,refresh
,refresh_time
,card AS #rows
,DATE(create_time) AS create_dt
,DATE(stats_time) AS stats_dt
FROM syscat.tables
WHERE type = 'S'
ORDER BY 1,2;
Figure 705, List all materialized query tables
CREATE TABLE emp_summary AS
(SELECT emp.workdept
,COUNT(*) AS num_rows
,COUNT(emp.salary) AS num_salary
,SUM(emp.salary) AS sum_salary
,COUNT(emp.comm) AS num_comm
,SUM(emp.comm) AS sum_comm
FROM employee emp
GROUP BY emp.workdept
)DATA INITIALLY DEFERRED REFRESH IMMEDIATE;
Figure 706, Refresh immediate materialized query table DDL
SELECT emp.workdept
,DEC(SUM(emp.salary),8,2) AS sum_sal
,DEC(AVG(emp.salary),7,2) AS avg_sal
,SMALLINT(COUNT(emp.comm)) AS #comms
,SMALLINT(COUNT(*)) AS #emps
FROM employee emp
WHERE emp.workdept > 'C'
GROUP BY emp.workdept
HAVING COUNT(*) <> 5
AND SUM(emp.salary) > 50000
ORDER BY sum_sal DESC;
Figure 707, Query that uses materialized query table (1 of 3)
SELECT emp.workdept
,COUNT(*) AS #rows
FROM employee emp
WHERE emp.workdept IN
(SELECT deptno
FROM department
WHERE deptname LIKE '%S%')
GROUP BY emp.workdept
HAVING SUM(salary) > 50000;
Figure 708, Query that uses materialized query table (2 of 3)
SELECT #emps
,DEC(SUM(sum_sal),9,2) AS sal_sal
,SMALLINT(COUNT(*)) AS #depts
FROM (SELECT emp.workdept
,DEC(SUM(emp.salary),8,2) AS sum_sal
,MAX(emp.salary) AS max_sal
,SMALLINT(COUNT(*)) AS #emps
FROM employee emp
GROUP BY emp.workdept
)AS XXX
GROUP BY #emps
HAVING COUNT(*) > 1
ORDER BY #emps
FETCH FIRST 3 ROWS ONLY
OPTIMIZE FOR 3 ROWS;
Figure 709, Query that uses materialized query table (3 of 3)
CREATE TABLE staff_all
(id SMALLINT NOT NULL
,name VARCHAR(9) NOT NULL
,job CHAR(5)
,salary DECIMAL(7,2)
,PRIMARY KEY(id));
Figure 710, Create source table
CREATE TABLE staff_all_dup AS
(SELECT *
FROM staff_all)
DATA INITIALLY DEFERRED REFRESH IMMEDIATE;
Figure 711, Create duplicate data table
CREATE TABLE staff_all_dup_some AS
(SELECT *
FROM staff_all
WHERE id < 30)
DATA INITIALLY DEFERRED REFRESH IMMEDIATE;
Figure 712, Create table - duplicate certain rows only
CREATE TABLE staff_to_fire
(id SMALLINT NOT NULL
,name VARCHAR(9) NOT NULL
,dept SMALLINT
,PRIMARY KEY(id));
Figure 713, Create source table
CREATE TABLE staff_combo AS
(SELECT aaa.id AS id1
,aaa.job AS job
,fff.id as id2
,fff.dept AS dept
FROM staff_all aaa
,staff_to_fire fff
WHERE aaa.id = fff.id)
DATA INITIALLY DEFERRED REFRESH IMMEDIATE;
Figure 714, Materialized query table on join
SELECT emp.workdept
,DEC(SUM(emp.salary),8,2) AS sum_sal
,MAX(emp.salary) AS max_sal
FROM employee emp
GROUP BY emp.workdept;
Figure 715, Query that doesn't use materialized query table (1 of 2)
SELECT emp.workdept
,DEC(SUM(emp.salary),8,2) AS sum_sal
,COUNT(DISTINCT salary) AS #salaries
FROM employee emp
GROUP BY emp.workdept;
Figure 716, Query that doesn't use materialized query table (2 of 2)
REFRESH TABLE emp_summary;
COMMIT;
SET INTEGRITY FOR emp_summary iMMEDIATE CHECKED;
COMMIT;
Figure 717, Materialized query table refresh commands
CREATE TABLE dept_emp_summary AS
(SELECT emp.workdept
,dpt.deptname
,COUNT(*) AS num_rows
,COUNT(emp.salary) AS num_salary
,SUM(emp.salary) AS sum_salary
,COUNT(emp.comm) AS num_comm
,SUM(emp.comm) AS sum_comm
FROM employee emp
,department dpt
WHERE dpt.deptno = emp.workdept
GROUP BY emp.workdept
,dpt.deptname
)DATA INITIALLY DEFERRED REFRESH IMMEDIATE;
Figure 718, Multi-table materialized query table DDL
SELECT d.deptname
,d.deptno
,DEC(AVG(e.salary),7,2) AS avg_sal
,SMALLINT(COUNT(*)) AS #emps
FROM department d
,employee e
WHERE e.workdept = d.deptno
AND d.deptname LIKE '%S%'
GROUP BY d.deptname
,d.deptno
HAVING SUM(e.comm) > 4000
ORDER BY avg_sal DESC;
Figure 719, Query that uses materialized query table
SELECT Q2.$C0 AS "deptname"
,Q2.$C1 AS "deptno"
,Q2.$C2 AS "avg_sal"
,Q2.$C3 AS "#emps"
FROM (SELECT Q1.deptname AS $C0
,Q1.workdept AS $C1
,DEC((Q1.sum_salary / Q1.num_salary),7,2) AS $C2
,SMALLINT(Q1.num_rows) AS $C3
FROM dept_emp_summary AS Q1
WHERE (Q1.deptname LIKE '%S%')
AND (4000 < Q1.sum_comm)
)AS Q2
ORDER BY Q2.$C2 DESC;
Figure 720, DB2 generated query to use materialized query table
CREATE TABLE dpt_emp_act_sumry AS
(SELECT emp.workdept
,dpt.deptname
,emp.empno
,emp.firstnme
,SUM(act.emptime) AS sum_time
,COUNT(act.emptime) AS num_time
,COUNT(*) AS num_rows
FROM department dpt
,employee emp
,emp_act act
WHERE dpt.deptno = emp.workdept
AND emp.empno = act.empno
GROUP BY emp.workdept
,dpt.deptname
,emp.empno
,emp.firstnme
)DATA INITIALLY DEFERRED REFRESH IMMEDIATE;
Figure 721, Three-table materialized query table DDL
SELECT d.deptno
,d.deptname
,DEC(AVG(a.emptime),5,2) AS avg_time
FROM department d
,employee e
,emp_act a
WHERE d.deptno = e.workdept
AND e.empno = a.empno
AND d.deptname LIKE '%S%'
AND e.firstnme LIKE '%S%'
GROUP BY d.deptno
,d.deptname
ORDER BY 3 DESC;
Figure 722, Query that uses materialized query table
SELECT Q4.$C0 AS "deptno"
,Q4.$C1 AS "deptname"
,Q4.$C2 AS "avg_time"
FROM (SELECT Q3.$C3 AS $C0
,Q3.$C2 AS $C1
,DEC((Q3.$C1 / Q3.$C0),5,2) AS $C2
FROM (SELECT SUM(Q2.$C2) AS $C0
,SUM(Q2.$C3) AS $C1
,Q2.$C0 AS $C2
,Q2.$C1 AS $C3
FROM (SELECT Q1.deptname AS $C0
,Q1.workdept AS $C1
,Q1.num_time AS $C2
,Q1.sum_time AS $C3
FROM dpt_emp_act_sumry AS Q1
WHERE (Q1.firstnme LIKE '%S%')
AND (Q1.DEPTNAME LIKE '%S%')
)AS Q2
GROUP BY Q2.$C1
,Q2.$C0
)AS Q3
)AS Q4
ORDER BY Q4.$C2 DESC;
Figure 723, DB2 generated query to use materialized query table
CREATE INDEX dpt_emp_act_sumx1
ON dpt_emp_act_sumry
(workdept
,deptname
,empno
,firstnme);
CREATE INDEX dpt_emp_act_sumx2
ON dpt_emp_act_sumry
(num_rows);
Figure 724, Indexes for DPT_EMP_ACT_SUMRY materialized query table
SELECT d.deptno
,d.deptname
,e.empno
,e.firstnme
,INT(AVG(a.emptime)) AS avg_time
FROM department d
,employee e
,emp_act a
WHERE d.deptno = e.workdept
AND e.empno = a.empno
AND d.deptno LIKE 'D%'
GROUP BY d.deptno
,d.deptname
,e.empno
,e.firstnme
ORDER BY 1,2,3,4;
Figure 725, Sample query that use WORKDEPT index
SELECT d.deptno
,d.deptname
,e.empno
,e.firstnme
,COUNT(*) AS #acts
FROM department d
,employee e
,emp_act a
WHERE d.deptno = e.workdept
AND e.empno = a.empno
GROUP BY d.deptno
,d.deptname
,e.empno
,e.firstnme
HAVING COUNT(*) > 4
ORDER BY 1,2,3,4;
Figure 726, Sample query that uses NUM_ROWS index
CREATE TABLE emp_sum AS
(SELECT workdept
,job
,SUM(salary) AS sum_sal
,COUNT(*) AS #emps
,GROUPING(workdept) AS grp_dpt
,GROUPING(job) AS grp_job
FROM employee
GROUP BY CUBE(workdept
,job))
DATA INITIALLY DEFERRED REFRESH DEFERRED
ORGANIZE BY DIMENSIONS (workdept, job)
IN tsempsum;
Figure 727, Materialized query table organized by dimensions
CREATE TABLE emp_sumry AS
(SELECT workdept AS dept
,COUNT(*) AS #rows
,COUNT(salary) AS #sal
,SUM(salary) AS sum_sal
FROM employee emp
GROUP BY emp.workdept
)DATA INITIALLY DEFERRED REFRESH DEFERRED;
Figure 728, Sample materialized query table
CREATE TABLE emp_sumry_s
(dept
,num_rows
,num_sal
,sum_sal
,GLOBALTRANSID
,GLOBALTRANSTIME
)FOR emp_sumry PROPAGATE IMMEDIATE;
Figure 729, Staging table for the above materialized query table
SET INTEGRITY FOR emp_sumry_s STAGING IMMEDIATE UNCHECKED;
REFRESH TABLE emp_sumry;
<< make changes to the source table (i.e. employee) >>
REFRESH TABLE emp_sumry INCREMENTAL;
Figure 730, Enabling and the using a staging table
Figure 731, Identity Column syntax
CREATE TABLE invoice_data
(invoice# INTEGER NOT NULL
GENERATED ALWAYS AS IDENTITY
(START WITH 1
,INCREMENT BY 1
,NO MAXVALUE
,NO CYCLE
,ORDER)
,sale_date DATE NOT NULL
,customer_id CHAR(20) NOT NULL
,product_id INTEGER NOT NULL
,quantity INTEGER NOT NULL
,price DECIMAL(18,2) NOT NULL
,PRIMARY KEY (invoice#));
Figure 732, Identity column, sample table
CREATE TABLE test_data KEY# FIELD - VALUES ASSIGNED
(key# SMALLINT NOT NULL ============================
GENERATED ALWAYS AS IDENTITY 1 2 3 4 5 6 7 8 9 10 11 etc.
,dat1 SMALLINT NOT NULL
,ts1 TIMESTAMP NOT NULL
,PRIMARY KEY(key#));
Figure 733, Identity column, ascending sequence
CREATE TABLE test_data KEY# FIELD - VALUES ASSIGNED
(key# SMALLINT NOT NULL ============================
GENERATED ALWAYS AS IDENTITY 6 3 0 -3 -6 -9 -12 -15 etc.
(START WITH 6
,INCREMENT BY -3
,NO CYCLE
,NO CACHE
,ORDER)
,dat1 SMALLINT NOT NULL
,ts1 TIMESTAMP NOT NULL
,PRIMARY KEY(key#));
Figure 734, Identity column, descending sequence
CREATE TABLE test_data KEY# VALUES ASSIGNED
(key# SMALLINT NOT NULL ============================
GENERATED ALWAYS AS IDENTITY 123 123 123 123 123 123 etc.
(START WITH 123
,MAXVALUE 124
,INCREMENT BY 0
,NO CYCLE
,NO ORDER)
,dat1 SMALLINT NOT NULL
,ts1 TIMESTAMP NOT NULL);
Figure 735, Identity column, dumb sequence
CREATE TABLE test_data KEY# VALUES ASSIGNED
(key# SMALLINT NOT NULL ============================
GENERATED ALWAYS AS IDENTITY 1 3 5 2 4 6 2 4 6 2 4 6 etc.
(START WITH 1
,INCREMENT BY 2
,MAXVALUE 6
,MINVALUE 2
,CYCLE
,NO CACHE
,ORDER)
,dat1 SMALLINT NOT NULL
,ts1 TIMESTAMP NOT NULL);
Figure 736, Identity column, odd values, then even, then stuck
CREATE TABLE invoice_data
(invoice# INTEGER NOT NULL
GENERATED ALWAYS AS IDENTITY
(START WITH 100
,INCREMENT BY 1
,NO CYCLE
,ORDER)
,sale_date DATE NOT NULL
,customer_id CHAR(20) NOT NULL
,product_id INTEGER NOT NULL
,quantity INTEGER NOT NULL
,price DECIMAL(18,2) NOT NULL
,PRIMARY KEY (invoice#));
Figure 737, Identity column, definition
INSERT INTO invoice_data
VALUES (DEFAULT,'2001-11-22','ABC',123,100,10);
SELECT invoice# ANSWER
FROM FINAL TABLE ========
(INSERT INTO invoice_data INVOICE#
(sale_date,customer_id,product_id,quantity,price) --------
VALUES ('2002-11-22','DEF',123,100,10) 101
,('2003-11-22','GHI',123,100,10)); 102
Figure 738, Invoice table, sample inserts
INVOICE# SALE_DATE CUSTOMER_ID PRODUCT_ID QUANTITY PRICE
-------- ---------- ----------- --- ------ -------- -----
100 2001-11-22 ABC 123 100 10.00
101 2002-11-22 DEF 123 100 10.00
102 2003-11-22 GHI 123 100 10.00
Figure 739, Invoice table, after inserts
ALTER TABLE invoice_data
ALTER COLUMN invoice#
RESTART WITH 1000
SET INCREMENT BY 2;
Figure 740, Invoice table, restart identity column value
INSERT INTO invoice_data
VALUES (DEFAULT,'2004-11-24','XXX',123,100,10)
,(DEFAULT,'2004-11-25','YYY',123,100,10);
Figure 741, Invoice table, more sample inserts
INVOICE# SALE_DATE CUSTOMER_ID PRODUCT_ID QUANTITY PRICE
-------- ---------- ----------- ---------- -------- -----
100 2001-11-22 ABC 123 100 10.00
101 2002-11-22 DEF 123 100 10.00
102 2003-11-22 GHI 123 100 10.00
1000 2004-11-24 XXX 123 100 10.00
1002 2004-11-25 YYY 123 100 10.00
Figure 742, Invoice table, after second inserts
Figure 743, Identity Column alter syntax
CREATE TABLE customers
(cust# INTEGER NOT NULL
GENERATED ALWAYS AS IDENTITY (NO CACHE)
,cname CHAR(10) NOT NULL
,ctype CHAR(03) NOT NULL
,PRIMARY KEY (cust#));
COMMIT;
SELECT cust# ANSWER
FROM FINAL TABLE ======
(INSERT INTO customers CUST#
VALUES (DEFAULT,'FRED','XXX')); -----
ROLLBACK; 1
SELECT cust# ANSWER
FROM FINAL TABLE ======
(INSERT INTO customers CUST#
VALUES (DEFAULT,'FRED','XXX')); -----
COMMIT; 2
Figure 744, Gaps in Values, example
SELECT MIN(cust#) AS minc ANSWER
,MAX(cust#) AS maxc ==============
,COUNT(*) AS rows MINC MAXC ROWS
FROM FINAL TABLE ---- ---- ----
(INSERT INTO customers 3 5 3
VALUES (DEFAULT,'FRED','xxx')
,(DEFAULT,'DAVE','yyy')
,(DEFAULT,'JOHN','zzz'));
Figure 745, Selecting identity column values inserted
CREATE TABLE invoice_table
(invoice# INTEGER NOT NULL
GENERATED ALWAYS AS IDENTITY
,sale_date DATE NOT NULL
,customer_id CHAR(20) NOT NULL
,product_id INTEGER NOT NULL
,quantity INTEGER NOT NULL
,price DECIMAL(18,2) NOT NULL
,PRIMARY KEY (invoice#));
COMMIT;
INSERT INTO invoice_table
VALUES (DEFAULT,'2000-11-22','ABC',123,100,10);
WITH temp (id) AS <<< ANSWER
(VALUES (IDENTITY_VAL_LOCAL())) ======
SELECT * ID
FROM temp; ----
1
COMMIT;
WITH temp (id) AS <<< ANSWER
(VALUES (IDENTITY_VAL_LOCAL())) ======
SELECT * ID
FROM temp; ----
1
Figure 746, IDENTITY_VAL_LOCAL function examples
INSERT INTO invoice_table
VALUES (DEFAULT,'2000-11-23','ABC',123,100,10);
INSERT INTO invoice_table
VALUES (DEFAULT,'2000-11-24','ABC',123,100,10)
,(DEFAULT,'2000-11-25','ABC',123,100,10); ANSWER
==================
SELECT invoice# AS inv# INV# SALE_DATE ID
,sale_date ---- ---------- --
,IDENTITY_VAL_LOCAL() AS id 1 11/22/2000 2
FROM invoice_table 2 11/23/2000 2
ORDER BY 1; 3 11/24/2000 2
COMMIT; 4 11/25/2000 2
Figure 747, IDENTITY_VAL_LOCAL function examples
SELECT invoice# AS inv# ANSWER
,sale_date ==================
,IDENTITY_VAL_LOCAL() AS id INV# SALE_DATE ID
FROM invoice_table ---- ---------- --
WHERE id = IDENTITY_VAL_LOCAL(); 2 11/23/2000 2
Figure 748, IDENTITY_VAL_LOCAL usage in predicate
CREATE SEQUENCE fred SEQ# VALUES ASSIGNED
AS DECIMAL(31) ====================
START WITH 100 100 102 104 106 etc.
INCREMENT BY 2
NO MINVALUE
NO MAXVALUE
NO CYCLE
CACHE 20
ORDER;
Figure 749, Create sequence
ALTER SEQUENCE fred SEQ# VALUES ASSIGNED
RESTART WITH -55 ====================
INCREMENT BY -5 -55 -60 -65 -70 etc.
MINVALUE -1000
MAXVALUE +1000
NO CACHE
NO ORDER
CYCLE;
Figure 750, Alter sequence attributes
CREATE SEQUENCE biggest_sale_to_date SEQ# VALUES ASSIGNED
AS INTEGER ====================
START WITH 345678 345678, 345678, etc.
INCREMENT BY 0;
Figure 751, Sequence that doesn't change
CREATE SEQUENCE fred; ANSWER
COMMIT; ======
SEQ#
WITH temp1 (n1) AS ----
(VALUES 1 1
UNION ALL 2
SELECT n1 + 1 3
FROM temp1 4
WHERE n1 < 5 5
)
SELECT NEXTVAL FOR fred AS seq#
FROM temp1;
Figure 752, Selecting the NEXTVAL
CREATE SEQUENCE fred; ANSWERS
COMMIT; =======
WITH temp1 (prv) AS ===> PRV
(VALUES (PREVVAL FOR fred)) ---
SELECT *
FROM temp1;
WITH temp1 (nxt) AS ===> NXT
(VALUES (NEXTVAL FOR fred)) ---
SELECT * 1
FROM temp1;
WITH temp1 (prv) AS ===> PRV
(VALUES (PREVVAL FOR fred)) ---
SELECT * 1
FROM temp1;
WITH temp1 (n1) AS ===> NXT PRV
(VALUES 1 --- ---
UNION ALL 2 1
SELECT n1 + 1 3 1
FROM temp1 4 1
WHERE n1 < 5 5 1
) 6 1
SELECT NEXTVAL FOR fred AS nxt
,PREVVAL FOR fred AS prv
FROM temp1;
Figure 753, Use of NEXTVAL and PREVVAL expressions
CREATE SEQUENCE fred; ANSWERS
COMMIT; =======
WITH temp1 AS ===> ID NXT
(SELECT id -- ---
,NEXTVAL FOR fred AS nxt 50 5
FROM staff
WHERE id < 100
)
SELECT *
FROM temp1
WHERE id = 50 + (nxt * 0);
WITH temp1 (nxt, prv) AS ===> NXT PRV
(VALUES (NEXTVAL FOR fred --- ---
,PREVVAL FOR fred)) 10 9
SELECT *
FROM temp1;
Figure 754, NEXTVAL values used but not retrieved
CREATE SEQUENCE cust#
START WITH 1
INCREMENT BY 1
NO MAXVALUE
NO CYCLE
ORDER;
CREATE TABLE us_customer
(cust# INTEGER NOT NULL
,cname CHAR(10) NOT NULL
,frst_sale DATE NOT NULL
,#sales INTEGER NOT NULL
,PRIMARY KEY (cust#));
CREATE TRIGGER us_cust_ins
NO CASCADE BEFORE INSERT ON us_customer
REFERENCING NEW AS nnn
FOR EACH ROW MODE DB2SQL
SET nnn.cust# = NEXTVAL FOR cust#;
CREATE TABLE intl_customer
(cust# INTEGER NOT NULL
,cname CHAR(10) NOT NULL
,frst_sale DATE NOT NULL
,#sales INTEGER NOT NULL
,PRIMARY KEY (cust#));
CREATE TRIGGER intl_cust_ins
NO CASCADE BEFORE INSERT ON intl_customer
REFERENCING NEW AS nnn
FOR EACH ROW MODE DB2SQL
SET nnn.cust# = NEXTVAL FOR cust#;
Figure 755, Create tables that use a common sequence
SELECT cust# ANSWERS
,cname ===========
FROM FINAL TABLE CUST# CNAME
(INSERT INTO us_customer (cname, frst_sale, #sales) ----- -----
VALUES ('FRED','2002-10-22',1) 1 FRED
,('JOHN','2002-10-23',1)); 2 JOHN
SELECT cust#
,cname
FROM FINAL TABLE CUST# CNAME
(INSERT INTO intl_customer (cname, frst_sale, #sales) ----- -----
VALUES ('SUE','2002-11-12',2) 3 SUE
,('DEB','2002-11-13',2)); 4 DEB
Figure 756, Insert into tables with common sequence
WITH temp (prev) AS ANSWER
(VALUES (PREVVAL FOR cust#)) ======
SELECT * PREV
FROM temp; ----
4
Figure 757, Get previous value - select
VALUES PREVVAL FOR CUST# INTO :host-var
Figure 758, Get previous value - into host-variable
CREATE SEQUENCE delete_rows
START WITH 1
INCREMENT BY 1
NO MAXVALUE
NO CYCLE
ORDER;
CREATE SEQUENCE delete_stmts
START WITH 1
INCREMENT BY 1
NO MAXVALUE
NO CYCLE
ORDER;
CREATE TABLE customer
(cust# INTEGER NOT NULL
,cname CHAR(10) NOT NULL
,frst_sale DATE NOT NULL
,#sales INTEGER NOT NULL
,PRIMARY KEY (cust#));
CREATE TRIGGER cust_del_rows
AFTER DELETE ON customer
FOR EACH ROW MODE DB2SQL
WITH temp1 (n1) AS (VALUES(1))
SELECT NEXTVAL FOR delete_rows
FROM temp1;
CREATE TRIGGER cust_del_stmts
AFTER DELETE ON customer
FOR EACH STATEMENT MODE DB2SQL
WITH temp1 (n1) AS (VALUES(1))
SELECT NEXTVAL FOR delete_stmts
FROM temp1;
Figure 759, Count deletes done to table
CREATE TABLE sales_invoice
(invoice# INTEGER NOT NULL
,sale_date DATE NOT NULL
,customer_id CHAR(20) NOT NULL
,product_id INTEGER NOT NULL
,quantity INTEGER NOT NULL
,price DECIMAL(18,2) NOT NULL
,PRIMARY KEY (invoice#));
Figure 760, Sample table, roll your own sequence#
CREATE TRIGGER sales_insert
NO CASCADE BEFORE
INSERT ON sales_invoice
REFERENCING NEW AS nnn
FOR EACH ROW
MODE DB2SQL
SET nnn.invoice# =
(SELECT COALESCE(MAX(invoice#),0) + 1
FROM sales_invoice);
Figure 761, Sample trigger, roll your own sequence#
INSERT INTO sales_invoice VALUES (0,'2001-06-22','ABC',123,10,1);
INSERT INTO sales_invoice VALUES (0,'2001-06-23','DEF',453,10,1);
COMMIT;
INSERT INTO sales_invoice VALUES (0,'2001-06-24','XXX',888,10,1);
ROLLBACK;
INSERT INTO sales_invoice VALUES (0,'2001-06-25','YYY',999,10,1);
COMMIT;
ANSWER
==============================================================
INVOICE# SALE_DATE CUSTOMER_ID PRODUCT_ID QUANTITY PRICE
-------- ---------- ----------- ---------- -------- -----
1 06/22/2001 ABC 123 10 1.00
2 06/23/2001 DEF 453 10 1.00
3 06/25/2001 YYY 999 10 1.00
Figure 762, Sample inserts, roll your own sequence#
CREATE TABLE control_table
(table_name CHAR(18) NOT NULL
,table_nmbr INTEGER NOT NULL
,PRIMARY KEY (table_name));
Figure 763, Control Table, DDL
INSERT INTO control_table VALUES ('invoice_table',0);
INSERT INTO control_table VALUES ('2nd_data_tble',0);
INSERT INTO control_table VALUES ('3rd_data_tble',0);
Figure 764, Control Table, sample inserts
CREATE TABLE invoice_table
(unqval CHAR(13) FOR BIT DATA NOT NULL
,invoice# INTEGER
,sale_date DATE NOT NULL
,customer_id CHAR(20) NOT NULL
,product_id INTEGER NOT NULL
,quantity INTEGER NOT NULL
,price DECIMAL(18,2) NOT NULL
,PRIMARY KEY(unqval));
Figure 765, Sample Data Table, DDL
CREATE TRIGGER invoice1
NO CASCADE BEFORE INSERT ON invoice_table
REFERENCING NEW AS nnn
FOR EACH ROW MODE DB2SQL
SET nnn.unqval = GENERATE_UNIQUE()
,nnn.invoice# = NULL;
Figure 766, Before trigger
CREATE TRIGGER invoice2
AFTER INSERT ON invoice_table
REFERENCING NEW AS nnn
FOR EACH ROW MODE DB2SQL
BEGIN ATOMIC
UPDATE control_table
SET table_nmbr = table_nmbr + 1
WHERE table_name = 'invoice_table';
UPDATE invoice_table
SET invoice# =
(SELECT table_nmbr
FROM control_table
WHERE table_name = 'invoice_table')
WHERE unqval = nnn.unqval
AND invoice# IS NULL;
END
Figure 767, After trigger
CREATE TRIGGER invoice3
NO CASCADE BEFORE UPDATE OF invoice# ON invoice_table
REFERENCING OLD AS ooo
NEW AS nnn
FOR EACH ROW MODE DB2SQL
WHEN (ooo.invoice# <> nnn.invoice#)
SIGNAL SQLSTATE '71001' ('no updates allowed - you twit');
Figure 768, Update trigger
SELECT id
,salary
FROM (SELECT s.*
,ROW_NUMBER() OVER(ORDER BY salary DESC) AS sorder
FROM staff s
WHERE id < 200 ANSWER
)AS xxx =============
WHERE sorder BETWEEN 2 AND 3 ID SALARY
ORDER BY id; --- --------
50 20659.80
140 21150.00
Figure 769, Nested Table Expression
WITH xxx (id, salary, sorder) AS
(SELECT ID
,salary
,ROW_NUMBER() OVER(ORDER BY salary DESC) AS sorder
FROM staff
WHERE id < 200
) ANSWER
SELECT id =============
,salary ID SALARY
FROM xxx --- --------
WHERE sorder BETWEEN 2 AND 3 50 20659.80
ORDER BY id; 140 21150.00
Figure 770, Common Table Expression
WITH ANSWER
rows_wanted AS ================================
(SELECT * ID NAME SALARY SUM_SAL PCT
FROM staff -- ------- -------- -------- ---
WHERE id < 100 70 Rothman 16502.83 34504.58 47
AND UCASE(name) LIKE '%T%' 90 Koonitz 18001.75 34504.58 52
),
sum_salary AS
(SELECT SUM(salary) AS sum_sal
FROM rows_wanted)
SELECT id
,name
,salary
,sum_sal
,INT((salary * 100) / sum_sal) AS pct
FROM rows_wanted
,sum_salary
ORDER BY id;
Figure 771, Common Table Expression
DECLARE GLOBAL TEMPORARY TABLE session.fred
(dept SMALLINT NOT NULL
,avg_salary DEC(7,2) NOT NULL
,num_emps SMALLINT NOT NULL)
ON COMMIT PRESERVE ROWS;
COMMIT;
INSERT INTO session.fred
SELECT dept
,AVG(salary)
,COUNT(*) ANSWER#1
FROM staff ========
WHERE id > 200 CNT
GROUP BY dept; ---
COMMIT; 4
SELECT COUNT(*) AS cnt
FROM session.fred; ANSWER#2
==========================
DELETE FROM session.fred DEPT AVG_SALARY NUM_EMPS
WHERE dept > 80; ---- ---------- --------
10 20168.08 3
SELECT * 51 15161.43 3
FROM session.fred; 66 17215.24 5
Figure 772, Declared Global Temporary Table
WITH staff_dept AS ANSWER
(SELECT dept AS dept# ==========================
,MAX(salary) AS max_sal ID DEPT SALARY MAX_SAL
FROM staff --- ---- -------- --------
WHERE dept < 50 10 20 18357.50 18357.50
GROUP BY dept 190 20 14252.75 18357.50
) 200 42 11508.60 18352.80
SELECT id 220 51 17654.50 -
,dept
,salary
,max_sal
FROM staff
LEFT OUTER JOIN
staff_dept
ON dept = dept#
WHERE name LIKE 'S%'
ORDER BY id;
Figure 773, Identical query (1 of 3) - using Common Table Expression
SELECT id ANSWER
,dept ==========================
,salary ID DEPT SALARY MAX_SAL
,max_sal --- ---- -------- --------
FROM staff 10 20 18357.50 18357.50
LEFT OUTER JOIN 190 20 14252.75 18357.50
(SELECT dept AS dept# 200 42 11508.60 18352.80
,MAX(salary) AS max_sal 220 51 17654.50 -
FROM staff
WHERE dept < 50
GROUP BY dept
)AS STAFF_dept
ON dept = dept#
WHERE name LIKE 'S%'
ORDER BY id;
Figure 774, Identical query (2 of 3) - using full-select in FROM
SELECT id ANSWER
,dept ==========================
,salary ID DEPT SALARY MAX_SAL
,(SELECT MAX(salary) --- ---- -------- --------
FROM staff s2 10 20 18357.50 18357.50
WHERE s1.dept = s2.dept 190 20 14252.75 18357.50
AND s2.dept < 50 200 42 11508.60 18352.80
GROUP BY dept) 220 51 17654.50 -
AS max_sal
FROM staff s1
WHERE name LIKE 'S%'
ORDER BY id;
Figure 775, Identical query (3 of 3) - using full-select in SELECT
Figure 776, Common Table Expression Syntax
WITH temp1 AS ANSWER
(SELECT MAX(name) AS max_name ==================
,MAX(dept) AS max_dept MAX_NAME MAX_DEPT
FROM staff --------- --------
) Yamaguchi 84
SELECT *
FROM temp1;
Figure 777, Common Table Expression, using named fields
WITH temp1 (max_name,max_dept) AS ANSWER
(SELECT MAX(name) ==================
,MAX(dept) MAX_NAME MAX_DEPT
FROM staff --------- --------
) Yamaguchi 84
SELECT *
FROM temp1;
Figure 778, Common Table Expression, using unnamed fields
WITH ANSWER
temp1 AS ==========
(SELECT dept MAX_AVG
,AVG(salary) AS avg_sal ----------
FROM staff 20865.8625
GROUP BY dept),
temp2 AS
(SELECT MAX(avg_sal) AS max_avg
FROM temp1)
SELECT *
FROM temp2;
Figure 779, Query with two common table expressions
SELECT * ANSWER
FROM (SELECT MAX(avg_sal) AS max_avg ==========
FROM (SELECT dept MAX_AVG
,AVG(salary) AS avg_sal ----------
FROM staff 20865.8625
GROUP BY dept
)AS temp1
)AS temp2;
Figure 780, Same as prior example, but using nested table expressions
WITH temp1 AS ANSWER
(SELECT id ==========================
,name ID DEPT SALARY MAX_SAL
,dept --- ---- -------- --------
,salary 10 20 18357.50 18357.50
FROM staff 190 20 14252.75 18357.50
WHERE id < 300 200 42 11508.60 11508.60
AND dept <> 55 220 51 17654.50 17654.50
AND name LIKE 'S%'
AND dept NOT IN
(SELECT deptnumb
FROM org
WHERE division = 'SOUTHERN'
OR location = 'HARTFORD')
)
,temp2 AS
(SELECT dept
,MAX(salary) AS max_sal
FROM temp1
GROUP BY dept
)
SELECT t1.id
,t1.dept
,t1.salary
,t2.max_sal
FROM temp1 t1
,temp2 t2
WHERE t1.dept = t2.dept
ORDER BY t1.id;
Figure 781, Deriving second temporary table from first
INSERT INTO staff
WITH temp1 (max1) AS
(SELECT MAX(id) + 1
FROM staff
)
SELECT max1,'A',1,'B',2,3,4
FROM temp1;
Figure 782, Insert using common table expression
INSERT INTO staff
SELECT MAX(id) + 1
,'A',1,'B',2,3,4
FROM staff;
Figure 783, Equivalent insert (to above) without common table expression
SELECT division
,DEC(AVG(dept_avg),7,2) AS div_dept
,COUNT(*) AS #dpts
,SUM(#emps) AS #emps
FROM (SELECT division
,dept
,AVG(salary) AS dept_avg
,COUNT(*) AS #emps
FROM staff ANSWER
,org ==============================
WHERE dept = deptnumb DIVISION DIV_DEPT #DPTS #EMPS
GROUP BY division --------- -------- ----- -----
,dept Corporate 20865.86 1 4
)AS xxx Eastern 15670.32 3 13
GROUP BY division; Midwest 15905.21 2 9
Western 16875.99 2 9
Figure 784, Nested column function usage
SELECT id ANSWER
FROM (SELECT * ======
FROM (SELECT id, years, salary ID
FROM (SELECT * ---
FROM (SELECT * 170
FROM staff 180
WHERE dept < 77 230
)AS t1
WHERE id < 300
)AS t2
WHERE job LIKE 'C%'
)AS t3
WHERE salary < 18000
)AS t4
WHERE years < 5;
Figure 785, Nested full-selects
SELECT a.id ANSWER
,a.dept =========================
,a.salary ID DEPT SALARY AVG_DEPT
,DEC(b.avgsal,7,2) AS avg_dept -- ---- -------- --------
FROM staff a 10 20 18357.50 16071.52
LEFT OUTER JOIN 20 20 18171.25 16071.52
(SELECT dept AS dept 30 38 17506.75 -
,AVG(salary) AS avgsal
FROM staff
GROUP BY dept
HAVING AVG(salary) > 16000
)AS b
ON a.dept = b.dept
WHERE a.id < 40
ORDER BY a.id;
Figure 786, Join full-select to real table
SELECT a.id ANSWER
,a.dept =========================
,a.salary ID DEPT SALARY DEPTSAL
,b.deptsal -- ---- -------- --------
FROM staff a 10 20 18357.50 64286.10
,TABLE 20 20 18171.25 64286.10
(SELECT b.dept 30 38 17506.75 77285.55
,SUM(b.salary) AS deptsal
FROM staff b
WHERE b.dept = a.dept
GROUP BY b.dept
)AS b
WHERE a.id < 40
ORDER BY a.id;
Figure 787, Full-select with external table reference
SELECT a.id ANSWER
,a.dept =========================
,a.salary ID DEPT SALARY DEPTSAL
,b.deptsal -- ---- -------- --------
FROM staff a 10 20 18357.50 64286.10
,(SELECT b.dept 20 20 18171.25 64286.10
,SUM(b.salary) AS deptsal 30 38 17506.75 77285.55
FROM staff b
GROUP BY b.dept
)AS b
WHERE a.id < 40
AND b.dept = a.dept
ORDER BY a.id;
Figure 788, Full-select without external table reference
SELECT id ANSWER
,salary ====================
,(SELECT MAX(salary) ID SALARY MAXSAL
FROM staff -- -------- --------
) AS maxsal 10 18357.50 22959.20
FROM staff a 20 18171.25 22959.20
WHERE id < 60 30 17506.75 22959.20
ORDER BY id; 40 18006.00 22959.20
50 20659.80 22959.20
Figure 789, Use an uncorrelated Full-Select in a SELECT list
SELECT id ANSWER
,salary ====================
,(SELECT MAX(salary) ID SALARY MAXSAL
FROM staff b -- -------- --------
WHERE a.dept = b.dept 10 18357.50 18357.50
) AS maxsal 20 18171.25 18357.50
FROM staff a 30 17506.75 18006.00
WHERE id < 60 40 18006.00 18006.00
ORDER BY id; 50 20659.80 20659.80
Figure 790, Use a correlated Full-Select in a SELECT list
SELECT id ANSWER
,dept ==================================
,salary ID DEPT SALARY 4 5
,(SELECT MAX(salary) -- ---- -------- -------- --------
FROM staff b 10 20 18357.50 18357.50 22959.20
WHERE b.dept = a.dept) 20 20 18171.25 18357.50 22959.20
,(SELECT MAX(salary) 30 38 17506.75 18006.00 22959.20
FROM staff) 40 38 18006.00 18006.00 22959.20
FROM staff a 50 15 20659.80 20659.80 22959.20
WHERE id < 60
ORDER BY id;
Figure 791, Use correlated and uncorrelated Full-Selects in a SELECT list
INSERT INTO staff
SELECT id + 1
,(SELECT MIN(name)
FROM staff)
,(SELECT dept
FROM staff s2
WHERE s2.id = s1.id - 100)
,'A',1,2,3
FROM staff s1
WHERE id =
(SELECT MAX(id)
FROM staff);
Figure 792, Full-select in INSERT
UPDATE staff a ANSWER: SALARY
SET salary = ======= =================
(SELECT AVG(salary)+ 2000 ID DEPT BEFORE AFTER
FROM staff) -- ---- -------- --------
WHERE id < 60; 10 20 18357.50 18675.64
20 20 18171.25 18675.64
30 38 17506.75 18675.64
40 38 18006.00 18675.64
50 15 20659.80 18675.64
Figure 793, Use uncorrelated Full-Select to give workers company AVG salary (+$2000)
UPDATE staff a ANSWER: SALARY
SET salary = ======= =================
(SELECT AVG(salary) + 2000 ID DEPT BEFORE AFTER
FROM staff b -- ---- -------- --------
WHERE a.dept = b.dept ) 10 20 18357.50 18071.52
WHERE id < 60; 20 20 18171.25 18071.52
30 38 17506.75 17457.11
40 38 18006.00 17457.11
50 15 20659.80 17482.33
Figure 794, Use correlated Full-Select to give workers department AVG salary (+$2000)
Figure 795, Declared Global Temporary Table syntax
DECLARE GLOBAL TEMPORARY TABLE session.fred
(dept SMALLINT NOT NULL
,avg_salary DEC(7,2) NOT NULL
,num_emps SMALLINT NOT NULL)
ON COMMIT DELETE ROWS;
Figure 796, Declare Global Temporary Table - define columns
DECLARE GLOBAL TEMPORARY TABLE session.fred
LIKE staff INCLUDING COLUMN DEFAULTS
WITH REPLACE
ON COMMIT PRESERVE ROWS;
Figure 797, Declare Global Temporary Table - like another table
DECLARE GLOBAL TEMPORARY TABLE session.fred AS
(SELECT dept
,MAX(id) AS max_id
,SUM(salary) AS sum_sal
FROM staff
WHERE name <> 'IDIOT'
GROUP BY dept)
DEFINITION ONLY
WITH REPLACE;
Figure 798, Declare Global Temporary Table - like query output
DECLARE GLOBAL TEMPORARY TABLE session.fred
LIKE staff INCLUDING COLUMN DEFAULTS
WITH REPLACE ON COMMIT DELETE ROWS;
CREATE UNIQUE INDEX session.fredx ON Session.fred (id);
INSERT INTO session.fred
SELECT *
FROM staff
WHERE id < 200;
ANSWER
SELECT COUNT(*) ======
FROM session.fred; 19
COMMIT;
ANSWER
SELECT COUNT(*) ======
FROM session.fred; 0
Figure 799, Temporary table with index
DECLARE GLOBAL TEMPORARY TABLE session.fred
(dept SMALLINT NOT NULL
,avg_salary DEC(7,2) NOT NULL
,num_emps SMALLINT NOT NULL)
ON COMMIT DELETE ROWS;
INSERT INTO session.fred
SELECT dept
,AVG(salary)
,COUNT(*)
FROM staff
GROUP BY dept;
ANSWER
SELECT COUNT(*) ======
FROM session.fred; 8
DROP TABLE session.fred;
DECLARE GLOBAL TEMPORARY TABLE session.fred
(dept SMALLINT NOT NULL)
ON COMMIT DELETE ROWS;
ANSWER
SELECT COUNT(*) ======
FROM session.fred; 0
Figure 800, Dropping a temporary table
CREATE USER TEMPORARY TABLESPACE FRED
MANAGED BY DATABASE
USING (FILE 'C:\DB2\TEMPFRED\FRED1' 1000
,FILE 'C:\DB2\TEMPFRED\FRED2' 1000
,FILE 'C:\DB2\TEMPFRED\FRED3' 1000);
GRANT USE OF TABLESPACE FRED TO PUBLIC;
Figure 801, Create USER TEMPORARY tablespace
HIERARCHY AAA
+---------------+ |
|PKEY |CKEY |NUM| +-----+-----+
|-----|-----|---| | | |
|AAA |BBB | 1| BBB CCC DDD
|AAA |CCC | 5| | |
|AAA |DDD | 20| +-+ +-+--+
|CCC |EEE | 33| | | |
|DDD |EEE | 44| EEE FFF
|DDD |FFF | 5| |
|FFF |GGG | 5| |
+---------------+ GGG
Figure 802, Sample Table description - Recursion
WITH parent (pkey, ckey) AS ANSWER
(SELECT pkey, ckey ========= PROCESSING
FROM hierarchy PKEY CKEY SEQUENCE
WHERE pkey = 'AAA' ---- ---- ==========
UNION ALL AAA BBB < 1st pass
SELECT C.pkey, C.ckey AAA CCC ""
FROM hierarchy C AAA DDD ""
,parent P CCC EEE < 2nd pass
WHERE P.ckey = C.pkey DDD EEE < 3rd pass
) DDD FFF ""
SELECT pkey, ckey FFF GGG < 4th pass
FROM parent;
Figure 803, SQL that does Recursion
Figure 804, Recursive processing sequence
CREATE TABLE hierarchy
(pkey CHAR(03) NOT NULL
,ckey CHAR(03) NOT NULL
,num SMALLINT NOT NULL
,PRIMARY KEY(pkey, ckey)
,CONSTRAINT dt1 CHECK (pkey <> ckey)
,CONSTRAINT dt2 CHECK (num > 0));
COMMIT;
CREATE UNIQUE INDEX hier_x1 ON hierarchy
(ckey, pkey);
COMMIT;
INSERT INTO hierarchy VALUES
('AAA','BBB', 1),
('AAA','CCC', 5),
('AAA','DDD',20),
('CCC','EEE',33),
('DDD','EEE',44),
('DDD','FFF', 5),
('FFF','GGG', 5);
COMMIT;
Figure 805, Sample Table DDL - Recursion
WITH parent (ckey) AS ANSWER HIERARCHY
(SELECT ckey ====== +---------------+
FROM hierarchy CKEY |PKEY |CKEY |NUM|
WHERE pkey = 'AAA' ---- |-----|-----|---|
UNION ALL BBB |AAA |BBB | 1|
SELECT C.ckey CCC |AAA |CCC | 5|
FROM hierarchy C DDD |AAA |DDD | 20|
,parent P EEE |CCC |EEE | 33|
WHERE P.ckey = C.pkey EEE |DDD |EEE | 44|
) FFF |DDD |FFF | 5|
SELECT ckey GGG |FFF |GGG | 5|
FROM parent; +---------------+
Figure 806, List of children of AAA
WITH parent (ckey) AS ANSWER HIERARCHY
(SELECT DISTINCT pkey ====== +---------------+
FROM hierarchy CKEY |PKEY |CKEY |NUM|
WHERE pkey = 'AAA' ---- |-----|-----|---|
UNION ALL AAA |AAA |BBB | 1|
SELECT C.ckey BBB |AAA |CCC | 5|
FROM hierarchy C CCC |AAA |DDD | 20|
,parent P DDD |CCC |EEE | 33|
WHERE P.ckey = C.pkey EEE |DDD |EEE | 44|
) EEE |DDD |FFF | 5|
SELECT ckey FFF |FFF |GGG | 5|
FROM parent; GGG +---------------+
Figure 807, List all children of AAA
WITH parent (ckey) AS ANSWER HIERARCHY
(SELECT DISTINCT pkey ====== +---------------+
FROM hierarchy CKEY |PKEY |CKEY |NUM|
WHERE pkey = 'AAA' ---- |-----|-----|---|
UNION ALL AAA |AAA |BBB | 1|
SELECT C.ckey BBB |AAA |CCC | 5|
FROM hierarchy C CCC |AAA |DDD | 20|
,parent P DDD |CCC |EEE | 33|
WHERE P.ckey = C.pkey EEE |DDD |EEE | 44|
) FFF |DDD |FFF | 5|
SELECT DISTINCT ckey GGG |FFF |GGG | 5|
FROM parent; +---------------+
Figure 808, List distinct children of AAA
WITH parent (ckey) AS ANSWER HIERARCHY
(SELECT DISTINCT pkey ====== +---------------+
FROM hierarchy CKEY |PKEY |CKEY |NUM|
WHERE pkey = 'AAA' ---- |-----|-----|---|
UNION ALL AAA |AAA |BBB | 1|
SELECT C.ckey BBB |AAA |CCC | 5|
FROM hierarchy C CCC |AAA |DDD | 20|
,parent P DDD |CCC |EEE | 33|
WHERE P.ckey = C.pkey EEE |DDD |EEE | 44|
), FFF |DDD |FFF | 5|
distinct_parent (ckey) AS GGG |FFF |GGG | 5|
(SELECT DISTINCT ckey +---------------+
FROM parent
)
SELECT ckey
FROM distinct_parent;
Figure 809, List distinct children of AAA
WITH parent (ckey, lvl) AS ANSWER AAA
(SELECT DISTINCT pkey, 0 ======== |
FROM hierarchy CKEY LVL +-----+-----+
WHERE pkey = 'AAA' ---- --- | | |
UNION ALL AAA 0 BBB CCC DDD
SELECT C.ckey, P.lvl +1 BBB 1 | |
FROM hierarchy C CCC 1 +-+ +-+--+
,parent P DDD 1 | | |
WHERE P.ckey = C.pkey EEE 2 EEE FFF
) EEE 2 |
SELECT ckey, lvl FFF 2 |
FROM parent; GGG 3 GGG
Figure 810, Show item level in hierarchy
WITH parent (ckey, lvl) AS ANSWER HIERARCHY
(SELECT DISTINCT pkey, 0 ======== +---------------+
FROM hierarchy CKEY LVL |PKEY |CKEY |NUM|
WHERE pkey = 'AAA' ---- --- |-----|-----|---|
UNION ALL AAA 0 |AAA |BBB | 1|
SELECT C.ckey, P.lvl +1 BBB 1 |AAA |CCC | 5|
FROM hierarchy C CCC 1 |AAA |DDD | 20|
,parent P DDD 1 |CCC |EEE | 33|
WHERE P.ckey = C.pkey EEE 2 |DDD |EEE | 44|
) EEE 2 |DDD |FFF | 5|
SELECT ckey, lvl FFF 2 |FFF |GGG | 5|
FROM parent +---------------+
WHERE lvl < 3;
Figure 811, Select rows where LEVEL < 3
WITH parent (ckey, lvl) AS ANSWER AAA
(SELECT DISTINCT pkey, 0 ======== |
FROM hierarchy CKEY LVL +-----+-----+
WHERE pkey = 'AAA' ---- --- | | |
UNION ALL AAA 0 BBB CCC DDD
SELECT C.ckey, P.lvl +1 BBB 1 | |
FROM hierarchy C CCC 1 +-+ +-+--+
,parent P DDD 1 | | |
WHERE P.ckey = C.pkey EEE 2 EEE FFF
AND P.lvl+1 < 3 EEE 2 |
) FFF 2 |
SELECT ckey, lvl GGG
FROM parent;
Figure 812, Select rows where LEVEL < 3
WITH parent (ckey, lvl) AS ANSWER HIERARCHY
(SELECT DISTINCT pkey, 0 ======== +---------------+
FROM hierarchy CKEY LVL |PKEY |CKEY |NUM|
WHERE pkey = 'AAA' ---- --- |-----|-----|---|
UNION ALL EEE 2 |AAA |BBB | 1|
SELECT C.ckey, P.lvl +1 EEE 2 |AAA |CCC | 5|
FROM hierarchy C FFF 2 |AAA |DDD | 20|
,parent P |CCC |EEE | 33|
WHERE P.ckey = C.pkey |DDD |EEE | 44|
AND P.lvl+1 < 3 |DDD |FFF | 5|
) |FFF |GGG | 5|
SELECT ckey, lvl +---------------+
FROM parent
WHERE lvl = 2;
Figure 813, Select rows where LEVEL = 2
WITH children (kkey, lvl) AS ANSWER AAA
(SELECT ckey, 1 ======== |
FROM hierarchy KKEY LVL +-----+-----+
WHERE pkey = 'DDD' ---- --- | | |
UNION ALL AAA -1 BBB CCC DDD
SELECT H.ckey, C.lvl + 1 EEE 1 | |
FROM hierarchy H FFF 1 +-+ +-+--+
,children C GGG 2 | | |
WHERE H.pkey = C.kkey EEE FFF
) |
,parents (kkey, lvl) AS |
(SELECT pkey, -1 GGG
FROM hierarchy
WHERE ckey = 'DDD'
UNION ALL
SELECT H.pkey, P.lvl - 1
FROM hierarchy H
,parents P
WHERE H.ckey = P.kkey
)
SELECT kkey ,lvl
FROM children
UNION ALL
SELECT kkey ,lvl
FROM parents;
Figure 814, Find all children and parents of DDD
WITH temp1 (n1) AS ANSWER
(SELECT id ======
FROM staff N1
WHERE id = 10 --
UNION ALL warn
SELECT n1 +10 10
FROM temp1 20
WHERE n1 < 50 30
) 40
SELECT * 50
FROM temp1;
Figure 815, Recursion - with warning message
WITH temp1 (n1) AS ANSWER
(SELECT INT(id) ======
FROM staff N1
WHERE id = 10 --
UNION ALL 10
SELECT n1 +10 20
FROM temp1 30
WHERE n1 < 50 40
) 50
SELECT *
FROM temp1;
Figure 816, Recursion - without warning message
DIVERGENT CONVERGENT RECURSIVE BALANCED UNBALANCED
========= ========== ========= ======== ==========
AAA AAA AAA<--+ AAA AAA
| | | | | |
+-+-+ +-+-+ +-+-+ | +-+-+ +-+-+
| | | | | | | | | | |
BBB CCC BBB CCC BBB CCC>+ BBB CCC BBB CCC
| | | | | | |
+-+-+ +-+-+-+ +-+-+ | +---+ +-+-+
| | | | | | | | | | |
DDD EEE DDD EEE DDD EEE DDD EEE FFF DDD EEE
Figure 817, Hierarchy Flavours
OBJECTS_RELATES AAA
+---------------------+ |
|KEYO |PKEY |NUM|PRICE| +-----+-----+
|-----|-----|---|-----| | | |
|AAA | | | $10| BBB CCC DDD
|BBB |AAA | 1| $21| |
|CCC |AAA | 5| $23| +--+--+
|DDD |AAA | 20| $25| | |
|EEE |DDD | 44| $33| EEE FFF
|FFF |DDD | 5| $34| |
|GGG |FFF | 5| $44| |
+---------------------+ GGG
Figure 818, Divergent Hierarchy - Table and Layout
OBJECTS RELATIONSHIPS AAA
+-----------+ +---------------+ |
|KEYO |PRICE| |PKEY |CKEY |NUM| +-----+-----+
|-----|-----| |-----|-----|---| | | |
|AAA | $10| |AAA |BBB | 1| BBB CCC DDD
|BBB | $21| |AAA |CCC | 5| | |
|CCC | $23| |AAA |DDD | 20| +-+ +-+--+
|DDD | $25| |CCC |EEE | 33| | | |
|EEE | $33| |DDD |EEE | 44| EEE FFF
|FFF | $34| |DDD |FFF | 5| |
|GGG | $44| |FFF |GGG | 5| |
+-----------+ +---------------+ GGG
Figure 819, Convergent Hierarchy - Tables and Layout
OBJECTS RELATIONSHIPS AAA <------+
+-----------+ +---------------+ | |
|KEYO |PRICE| |PKEY |CKEY |NUM| +-----+-----+ |
|-----|-----| |-----|-----|---| | | | |
|AAA | $10| |AAA |BBB | 1| BBB CCC DDD>-+
|BBB | $21| |AAA |CCC | 5| | |
|CCC | $23| |AAA |DDD | 20| +-+ +-+--+
|DDD | $25| |CCC |EEE | 33| | | |
|EEE | $33| |DDD |AAA | 99| EEE FFF
|FFF | $34| |DDD |FFF | 5| |
|GGG | $44| |DDD |EEE | 44| |
+-----------+ |FFF |GGG | 5| GGG
+---------------+
Figure 820, Recursive Hierarchy - Tables and Layout
AAA << Balanced hierarchy AAA
| Unbalanced hierarchy >> |
+-----+-----+ +---+----+
| | | | | |
BBB CCC DDD | CCC DDD
| | | | | |
| | +-+-+ | +-+ +-+-+
| | | | | | | |
EEE FFF GGG HHH FFF GGG HHH
|
|
III
Figure 821, Balanced and Unbalanced Hierarchies
TROUBLE AAA <------+
+---------+ | |
|PKEY|CKEY| +-----+-----+ |
|----|----| | | | |
|AAA |BBB | BBB CCC DDD>-+
|AAA |CCC | | |
|AAA |DDD | +-+ +-+--+
|CCC |EEE | | | |
|DDD |AAA | <=== This row EEE FFF
|DDD |FFF | points back to |
|DDD |EEE | the hierarchy |
|FFF |GGG | parent. GGG
+---------+
Figure 822, Recursive Hierarchy - Sample Table and Layout
CREATE TABLE trouble
(pkey CHAR(03) NOT NULL
,ckey CHAR(03) NOT NULL);
CREATE UNIQUE INDEX tble_x1 ON trouble (pkey, ckey);
CREATE UNIQUE INDEX tble_x2 ON trouble (ckey, pkey);
INSERT INTO trouble VALUES
('AAA','BBB'),
('AAA','CCC'),
('AAA','DDD'),
('CCC','EEE'),
('DDD','AAA'),
('DDD','EEE'),
('DDD','FFF'),
('FFF','GGG');
Figure 823, Sample Table DDL - Recursive Hierarchy
WITH parent (pkey, ckey, lvl) AS ANSWER TROUBLE
(SELECT DISTINCT ============= +---------+
pkey PKEY CKEY LVL |PKEY|CKEY|
,pkey ---- ---- --- |----|----|
,0 AAA AAA 0 |AAA |BBB |
FROM trouble AAA BBB 1 |AAA |CCC |
WHERE pkey = 'AAA' AAA CCC 1 |AAA |DDD |
UNION ALL AAA DDD 1 |CCC |EEE |
SELECT C.pkey CCC EEE 2 |DDD |AAA |
,C.ckey DDD AAA 2 |DDD |FFF |
,P.lvl + 1 DDD EEE 2 |DDD |EEE |
FROM trouble C DDD FFF 2 |FFF |GGG |
,parent P AAA BBB 3 +---------+
WHERE P.ckey = C.pkey AAA CCC 3
AND P.lvl + 1 < 4 AAA DDD 3
) FFF GGG 3
SELECT *
FROM parent;
Figure 824, Stop Recursive SQL after "n" levels
CREATE FUNCTION LOCATE_BLOCK(searchstr VARCHAR(30000)
,lookinstr VARCHAR(30000))
RETURNS INTEGER
BEGIN ATOMIC
DECLARE lookinlen, searchlen INT;
DECLARE locatevar, returnvar INT DEFAULT 0;
DECLARE beginlook INT DEFAULT 1;
SET lookinlen = LENGTH(lookinstr);
SET searchlen = LENGTH(searchstr);
WHILE locatevar = 0 AND
beginlook <= lookinlen DO
SET locatevar = LOCATE(searchstr,SUBSTR(lookinstr
,beginlook
,searchlen));
SET beginlook = beginlook + searchlen;
SET returnvar = returnvar + 1;
END WHILE;
IF locatevar = 0 THEN
SET returnvar = 0;
END IF;
RETURN returnvar;
END
Figure 825, LOCATE_BLOCK user defined function
SELECT id ANSWER
,name =================
,LOCATE('th',name) AS l1 ID NAME L1 L2
,LOCATE_BLOCK('th',name) AS l2 --- ------- -- --
FROM staff 70 Rothman 3 2
WHERE LOCATE('th',name) > 1; 220 Smith 4 0
Figure 826, LOCATE_BLOCK function example
WITH parent (pkey, ckey, lvl, path, loop) AS
(SELECT DISTINCT
pkey
,pkey ANSWER
,0 ===============================
,VARCHAR(pkey,20) PKEY CKEY LVL PATH LOOP
,0 ---- ---- --- ------------ ----
FROM trouble AAA AAA 0 AAA 0
WHERE pkey = 'AAA' AAA BBB 1 AAABBB 0
UNION ALL AAA CCC 1 AAACCC 0
SELECT C.pkey AAA DDD 1 AAADDD 0
,C.ckey CCC EEE 2 AAACCCEEE 0
,P.lvl + 1 DDD AAA 2 AAADDDAAA 1
,P.path || C.ckey DDD EEE 2 AAADDDEEE 0
,LOCATE_BLOCK(C.ckey,P.path) DDD FFF 2 AAADDDFFF 0
FROM trouble C AAA BBB 3 AAADDDAAABBB 0
,parent P AAA CCC 3 AAADDDAAACCC 0
WHERE P.ckey = C.pkey AAA DDD 3 AAADDDAAADDD 2
AND P.lvl + 1 < 4 FFF GGG 3 AAADDDFFFGGG 0
)
SELECT *
FROM parent; TROUBLE
+---------+ AAA <------+
|PKEY|CKEY| | |
|----|----| +-----+-----+ |
|AAA |BBB | | | | |
|AAA |CCC | BBB CCC DDD>-+
|AAA |DDD | | |
|CCC |EEE | +-+ +-+--+
This row ===> |DDD |AAA | | | |
points back to |DDD |FFF | EEE FFF
the hierarchy |DDD |EEE | |
parent. |FFF |GGG | |
+---------+ GGG
Figure 827, Show path, and rows in loop
WITH parent (pkey, ckey, lvl, path) AS ANSWER
(SELECT DISTINCT ==========================
pkey PKEY CKEY LVL PATH
,pkey ---- ----- -- ------------
,0 AAA AAA 0 AAA
,VARCHAR(pkey,20) AAA BBB 1 AAABBB
FROM trouble AAA CCC 1 AAACCC
WHERE pkey = 'AAA' AAA DDD 1 AAADDD
UNION ALL CCC EEE 2 AAACCCEEE
SELECT C.pkey DDD EEE 2 AAADDDEEE
,C.ckey DDD FFF 2 AAADDDFFF
,P.lvl + 1 FFF GGG 3 AAADDDFFFGGG
,P.path || C.ckey
FROM trouble C
,parent P
WHERE P.ckey = C.pkey
AND LOCATE_BLOCK(C.ckey,P.path) = 0
)
SELECT *
FROM parent;
Figure 828, Use LOCATE_BLOCK function to stop recursion
WITH parent (pkey, ckey, lvl, path, loop) AS
(SELECT DISTINCT
pkey
,pkey
,0
,VARCHAR(pkey,20) ANSWER
,0 ===============================
FROM trouble PKEY CKEY LVL PATH LOOP
WHERE pkey = 'AAA' ---- ---- --- ------------ ----
UNION ALL AAA AAA 0 AAA 0
SELECT C.pkey AAA BBB 1 AAABBB 0
,C.ckey AAA CCC 1 AAACCC 0
,P.lvl + 1 AAA DDD 1 AAADDD 0
,P.path || C.ckey CCC EEE 2 AAACCCEEE 0
,LOCATE_BLOCK(C.ckey,P.path) DDD AAA 2 AAADDDAAA 1
FROM trouble C DDD EEE 2 AAADDDEEE 0
,parent P DDD FFF 2 AAADDDFFF 0
WHERE P.ckey = C.pkey FFF GGG 3 AAADDDFFFGGG 0
AND P.loop = 0
)
SELECT *
FROM parent;
Figure 829, Use LOCATE_BLOCK function to stop recursion
WITH parent (pkey, ckey, lvl, path, loop) AS ANSWER
(SELECT DISTINCT =========
pkey PKEY CKEY
,pkey ---- ----
,0 DDD AAA
,VARCHAR(pkey,20)
,0
FROM trouble
WHERE pkey = 'AAA'
UNION ALL
SELECT C.pkey
,C.ckey TROUBLE
,P.lvl + 1 +---------+
,P.path || C.ckey |PKEY|CKEY|
,LOCATE_BLOCK(C.ckey,P.path) |----|----|
FROM trouble C |AAA |BBB |
,parent P |AAA |CCC |
WHERE P.ckey = C.pkey |AAA |DDD |
AND P.loop = 0 |CCC |EEE |
) This row ===> |DDD |AAA |
SELECT pkey points back to |DDD |FFF |
,ckey the hierarchy |DDD |EEE |
FROM parent parent. |FFF |GGG |
WHERE loop > 0; +---------+
Figure 830,List rows that point back to a parent
DECLARE GLOBAL TEMPORARY TABLE SESSION.del_list
(pkey CHAR(03) NOT NULL
,ckey CHAR(03) NOT NULL)
ON COMMIT PRESERVE ROWS;
INSERT INTO SESSION.del_list
WITH parent (pkey, ckey, lvl, path, loop) AS
(SELECT DISTINCT
pkey
,pkey
,0 TROUBLE
,VARCHAR(pkey,20) +---------+
,0 |PKEY|CKEY|
FROM trouble |----|----|
WHERE pkey = 'AAA' |AAA |BBB |
UNION ALL |AAA |CCC |
SELECT C.pkey |AAA |DDD |
,C.ckey |CCC |EEE |
,P.lvl + 1 This row ===> |DDD |AAA |
,P.path || C.ckey points back to |DDD |FFF |
,LOCATE_BLOCK(C.ckey,P.path) the hierarchy |DDD |EEE |
FROM trouble C parent. |FFF |GGG |
,parent P +---------+
WHERE P.ckey = C.pkey
AND P.loop = 0 AAA <------+
) | |
SELECT pkey +-----+-----+ |
,ckey | | | |
FROM parent BBB CCC DDD>-+
WHERE loop > 0; | |
+-+ +-+--+
DELETE | | |
FROM trouble EEE FFF
WHERE (pkey,ckey) IN |
(SELECT pkey, ckey |
FROM SESSION.del_list); GGG
Figure 831, Delete rows that loop back to a parent
CREATE TRIGGER TBL_INS TROUBLE
NO CASCADE BEFORE INSERT ON trouble +---------+
REFERENCING NEW AS NNN This trigger |PKEY|CKEY|
FOR EACH ROW MODE DB2SQL would reject |----|----|
WITH temp (pkey, ckey) AS insertion of |AAA |BBB |
(VALUES (NNN.pkey this row. |AAA |CCC |
,NNN.ckey) | |AAA |DDD |
UNION ALL | |CCC |EEE |
SELECT TTT.pkey +---> |DDD |AAA |
,CASE |DDD |FFF |
WHEN TTT.ckey = TBL.pkey |DDD |EEE |
THEN RAISE_ERROR('70001','LOOP FOUND') |FFF |GGG |
ELSE TBL.ckey +---------+
END
FROM trouble TBL
,temp TTT
WHERE TTT.ckey = TBL.pkey
)
SELECT *
FROM temp;
Figure 832, INSERT trigger
CREATE TRIGGER TBL_UPD
NO CASCADE BEFORE UPDATE OF pkey, ckey ON trouble
REFERENCING NEW AS NNN
FOR EACH ROW MODE DB2SQL
WITH temp (pkey, ckey) AS
(VALUES (NNN.pkey
,NNN.ckey)
UNION ALL
SELECT TTT.pkey
,CASE
WHEN TTT.ckey = TBL.pkey
THEN RAISE_ERROR('70001','LOOP FOUND')
ELSE TBL.ckey
END
FROM trouble TBL
,temp TTT
WHERE TTT.ckey = TBL.pkey
)
SELECT *
FROM temp;
Figure 833, UPDATE trigger
INSERT INTO trouble VALUES('GGG','AAA');
UPDATE trouble SET ckey = 'AAA' WHERE pkey = 'FFF';
UPDATE trouble SET pkey = 'GGG' WHERE ckey = 'DDD';
Figure 834, Invalid DML statements
HIERARCHY#1 EXPLODED#1
AAA +--------------------+ +-------------+
| |KEYY|PKEY|DATA | |PKEY|CKEY|LVL|
BBB |----|----|----------| |----|----|---|
| |AAA |AAA |SOME DATA | |AAA |AAA | 0|
+-----+ |BBB |AAA |MORE DATA | |AAA |BBB | 1|
| | |CCC |BBB |MORE JUNK | |AAA |CCC | 2|
CCC EEE |DDD |CCC |MORE JUNK | |AAA |DDD | 3|
| |EEE |BBB |JUNK DATA | |AAA |EEE | 2|
DDD +--------------------+ |BBB |BBB | 0|
|BBB |CCC | 1|
|BBB |DDD | 2|
|BBB |EEE | 1|
|CCC |CCC | 0|
|CCC |DDD | 1|
|DDD |DDD | 0|
|EEE |EEE | 0|
+-------------+
Figure 835, Data Hierarchy, with normalized and exploded representations
CREATE TABLE hierarchy#1
(keyy CHAR(3) NOT NULL
,pkey CHAR(3) NOT NULL
,data VARCHAR(10)
,CONSTRAINT hierarchy11 PRIMARY KEY(keyy)
,CONSTRAINT hierarchy12 FOREIGN KEY(pkey)
REFERENCES hierarchy#1 (keyy) ON DELETE CASCADE);
CREATE TRIGGER HIR#1_UPD
NO CASCADE BEFORE UPDATE OF pkey ON hierarchy#1
REFERENCING NEW AS NNN
OLD AS OOO
FOR EACH ROW MODE DB2SQL
WHEN (NNN.pkey <> OOO.pkey)
SIGNAL SQLSTATE '70001' ('CAN NOT UPDATE pkey');
Figure 836, Hierarchy table that does not allow updates to PKEY
CREATE TABLE exploded#1
(pkey CHAR(4) NOT NULL
,ckey CHAR(4) NOT NULL
,lvl SMALLINT NOT NULL
,PRIMARY KEY(pkey,ckey));
Figure 837, Exploded table CREATE statement
CREATE TRIGGER EXP#1_DEL
AFTER DELETE ON hierarchy#1
REFERENCING OLD AS OOO
FOR EACH ROW MODE DB2SQL
DELETE
FROM exploded#1
WHERE ckey = OOO.keyy;
Figure 838, Trigger to maintain exploded table after delete in hierarchy table
CREATE TRIGGER EXP#1_INS HIERARCHY#1 EXPLODED#1
AFTER INSERT ON hierarchy#1 +--------------+ +-------------+
REFERENCING NEW AS NNN |KEYY|PKEY|DATA| |PKEY|CKEY|LVL|
FOR EACH ROW MODE DB2SQL |----|----|----| |----|----|---|
INSERT |AAA |AAA |S...| |AAA |AAA | 0|
INTO exploded#1 |BBB |AAA |M...| |AAA |BBB | 1|
WITH temp(pkey, ckey, lvl) AS |CCC |BBB |M...| |AAA |CCC | 2|
(VALUES (NNN.keyy |DDD |CCC |M...| |AAA |DDD | 3|
,NNN.keyy |EEE |BBB |J...| |AAA |EEE | 2|
,0) +--------------+ |BBB |BBB | 0|
UNION ALL |BBB |CCC | 1|
SELECT N.pkey |BBB |DDD | 2|
,NNN.keyy |BBB |EEE | 1|
,T.lvl +1 |CCC |CCC | 0|
FROM temp T |CCC |DDD | 1|
,hierarchy#1 N |DDD |DDD | 0|
WHERE N.keyy = T.pkey |EEE |EEE | 0|
AND N.keyy <> N.pkey +-------------+
)
SELECT *
FROM temp;
Figure 839, Trigger to maintain exploded table after insert in hierarchy table
SELECT *
FROM exploded#1
WHERE pkey = :host-var
ORDER BY pkey
,ckey
,lvl;
Figure 840, Querying the exploded table
CREATE TABLE hierarchy#2
(keyy CHAR(3) NOT NULL
,pkey CHAR(3) NOT NULL
,data VARCHAR(10)
,CONSTRAINT NO_loopS21 PRIMARY KEY(keyy)
,CONSTRAINT NO_loopS22 FOREIGN KEY(pkey)
REFERENCES hierarchy#2 (keyy) ON DELETE CASCADE
ON UPDATE RESTRICT);
Figure 841, Hierarchy table that allows updates to PKEY
CREATE TRIGGER HIR#2_UPD HIERARCHY#2
NO CASCADE BEFORE UPDATE OF pkey ON hierarchy#2 +--------------+
REFERENCING NEW AS NNN |KEYY|PKEY|DATA|
OLD AS OOO |----|----|----|
FOR EACH ROW MODE DB2SQL |AAA |AAA |S...|
WHEN (NNN.pkey <> OOO.pkey |BBB |AAA |M...|
AND NNN.pkey <> NNN.keyy) |CCC |BBB |M...|
WITH temp (keyy, pkey) AS |DDD |CCC |M...|
(VALUES (NNN.keyy |EEE |BBB |J...|
,NNN.pkey) +--------------+
UNION ALL
SELECT LP2.keyy
,CASE
WHEN LP2.keyy = NNN.keyy
THEN RAISE_ERROR('70001','LOOP FOUND')
ELSE LP2.pkey
END
FROM hierarchy#2 LP2
,temp TMP
WHERE TMP.pkey = LP2.keyy
AND TMP.keyy <> TMP.pkey
)
SELECT *
FROM temp;
Figure 842, Trigger to check for recursive data structures before update of PKEY
CREATE TABLE exploded#2
(pkey CHAR(4) NOT NULL
,ckey CHAR(4) NOT NULL
,lvl SMALLINT NOT NULL
,PRIMARY KEY(pkey,ckey));
Figure 843, Exploded table CREATE statement
CREATE TRIGGER EXP#2_DEL
AFTER DELETE ON hierarchy#2
REFERENCING OLD AS OOO
FOR EACH ROW MODE DB2SQL
DELETE
FROM exploded#2
WHERE ckey = OOO.keyy;
Figure 844, Trigger to maintain exploded table after delete in hierarchy table
CREATE TRIGGER EXP#2_INS HIERARCHY#2 EXPLODED#2
AFTER INSERT ON hierarchy#2 +--------------+ +-------------+
REFERENCING NEW AS NNN |KEYY|PKEY|DATA| |PKEY|CKEY|LVL|
FOR EACH ROW MODE DB2SQL |----|----|----| |----|----|---|
INSERT |AAA |AAA |S...| |AAA |AAA | 0|
INTO exploded#2 |BBB |AAA |M...| |AAA |BBB | 1|
WITH temp(pkey, ckey, lvl) AS |CCC |BBB |M...| |AAA |CCC | 2|
(SELECT NNN.keyy |DDD |CCC |M...| |AAA |DDD | 3|
,NNN.keyy |EEE |BBB |J...| |AAA |EEE | 2|
,0 +--------------+ |BBB |BBB | 0|
FROM hierarchy#2 |BBB |CCC | 1|
WHERE keyy = NNN.keyy |BBB |DDD | 2|
UNION ALL |BBB |EEE | 1|
SELECT N.pkey |CCC |CCC | 0|
,NNN.keyy |CCC |DDD | 1|
,T.lvl +1 |DDD |DDD | 0|
FROM temp T |EEE |EEE | 0|
,hierarchy#2 N +-------------+
WHERE N.keyy = T.pkey
AND N.keyy <> N.pkey
)
SELECT *
FROM temp;
Figure 845, Trigger to maintain exploded table after insert in hierarchy table
CREATE TRIGGER EXP#2_UPD
AFTER UPDATE OF pkey ON hierarchy#2
REFERENCING OLD AS OOO
NEW AS NNN
FOR EACH ROW MODE DB2SQL
BEGIN ATOMIC
DELETE
FROM exploded#2
WHERE ckey IN
(SELECT ckey
FROM exploded#2
WHERE pkey = OOO.keyy);
INSERT
INTO exploded#2
WITH temp1(ckey) AS
(VALUES (NNN.keyy)
UNION ALL
SELECT N.keyy
FROM temp1 T
,hierarchy#2 N
WHERE N.pkey = T.ckey
AND N.pkey <> N.keyy
)
Figure 846, Trigger to run after update of PKEY in hierarchy table (part 1 of 2)
,temp2(pkey, ckey, lvl) AS
(SELECT ckey
,ckey
,0
FROM temp1
UNION ALL
SELECT N.pkey
,T.ckey
,T.lvl +1
FROM temp2 T
,hierarchy#2 N
WHERE N.keyy = T.pkey
AND N.keyy <> N.pkey
)
SELECT *
FROM temp2;
END
Figure 847, Trigger to run after update of PKEY in hierarchy table (part 2 of 2)
SELECT *
FROM exploded#2
WHERE pkey = :host-var
ORDER BY pkey
,ckey
,lvl;
Figure 848, Querying the exploded table
Figure 849, Create Trigger syntax
CREATE TABLE cust_balance
(cust# INTEGER NOT NULL
GENERATED ALWAYS AS IDENTITY
,status CHAR(2) NOT NULL
,balance DECIMAL(18,2) NOT NULL
,num_trans INTEGER NOT NULL
,cur_ts TIMESTAMP NOT NULL
,PRIMARY KEY (cust#));
CREATE TABLE cust_history
(cust# INTEGER NOT NULL
,trans# INTEGER NOT NULL
,balance DECIMAL(18,2) NOT NULL
,bgn_ts TIMESTAMP NOT NULL
,end_ts TIMESTAMP NOT NULL
,PRIMARY KEY (cust#, bgn_ts));
CREATE TABLE cust_trans
(min_cust# INTEGER
,max_cust# INTEGER
,rows_tot INTEGER NOT NULL
,change_val DECIMAL(18,2)
,change_type CHAR(1) NOT NULL
,cur_ts TIMESTAMP NOT NULL
,PRIMARY KEY (cur_ts));
Figure 850, Sample Tables
CREATE TRIGGER cust_bal_ins1
NO CASCADE BEFORE INSERT
ON cust_balance
REFERENCING NEW AS nnn
FOR EACH ROW
MODE DB2SQL
SET nnn.cur_ts = CURRENT TIMESTAMP
,nnn.num_trans = 1;
Figure 851, Before insert trigger - set values
CREATE TRIGGER cust_bal_upd1
NO CASCADE BEFORE UPDATE
ON cust_balance
REFERENCING NEW AS nnn
OLD AS ooo
FOR EACH ROW
MODE DB2SQL
SET nnn.cur_ts = CURRENT TIMESTAMP
,nnn.num_trans = ooo.num_trans + 1;
Figure 852, Before update trigger - set values
CREATE TRIGGER cust_bal_upd2
NO CASCADE BEFORE UPDATE OF balance
ON cust_balance
REFERENCING NEW AS nnn
OLD AS ooo
FOR EACH ROW
MODE DB2SQL
WHEN (ooo.balance - nnn.balance > 1000)
SIGNAL SQLSTATE VALUE '71001'
SET MESSAGE_TEXT = 'Cannot withdraw > 1000';
Figure 853, Before Trigger - flag error
CREATE TRIGGER cust_his_ins1
AFTER INSERT
ON cust_balance
REFERENCING NEW AS nnn
FOR EACH ROW
MODE DB2SQL
INSERT INTO cust_history VALUES
(nnn.cust#
,nnn.num_trans
,nnn.balance
,nnn.cur_ts
,'9999-12-31-24.00.00');
Figure 854, After Trigger - record insert
CREATE TRIGGER cust_his_upd1
AFTER UPDATE
ON cust_balance
REFERENCING OLD AS ooo
NEW AS nnn
FOR EACH ROW
MODE DB2SQL
BEGIN ATOMIC
UPDATE cust_history
SET end_ts = CURRENT TIMESTAMP
WHERE cust# = ooo.cust#
AND bgn_ts = ooo.cur_ts;
INSERT INTO cust_history VALUES
(nnn.cust#
,nnn.num_trans
,nnn.balance
,nnn.cur_ts
,'9999-12-31-24.00.00');
END
Figure 855, After Trigger - record update
CREATE TRIGGER cust_his_del1
AFTER DELETE
ON cust_balance
REFERENCING OLD AS ooo
FOR EACH ROW
MODE DB2SQL
UPDATE cust_history
SET end_ts = CURRENT TIMESTAMP
WHERE cust# = ooo.cust#
AND bgn_ts = ooo.cur_ts;
Figure 856, After Trigger - record delete
CREATE TRIGGER trans_his_ins1
AFTER INSERT
ON cust_balance
REFERENCING NEW_TABLE AS newtab
FOR EACH STATEMENT
MODE DB2SQL
INSERT INTO cust_trans
SELECT MIN(cust#)
,MAX(cust#)
,COUNT(*)
,SUM(balance)
,'I'
,CURRENT TIMESTAMP
FROM newtab;
Figure 857, After Trigger - record insert
CREATE TRIGGER trans_his_upd1
AFTER UPDATE
ON cust_balance
REFERENCING OLD_TABLE AS oldtab
NEW_TABLE AS newtab
FOR EACH STATEMENT
MODE DB2SQL
INSERT INTO cust_trans
SELECT MIN(nt.cust#)
,MAX(nt.cust#)
,COUNT(*)
,SUM(nt.balance - ot.balance)
,'U'
,CURRENT TIMESTAMP
FROM oldtab ot
,newtab nt
WHERE ot.cust# = nt.cust#;
Figure 858, After Trigger - record update
CREATE TRIGGER trans_his_del1
AFTER DELETE
ON cust_balance
REFERENCING OLD_TABLE AS oldtab
FOR EACH STATEMENT
MODE DB2SQL
INSERT INTO cust_trans
SELECT MIN(cust#)
,MAX(cust#)
,COUNT(*)
,SUM(balance)
,'D'
,CURRENT TIMESTAMP
FROM oldtab;
Figure 859, After Trigger - record delete
INSERT INTO cust_balance (status, balance) VALUES ('C',123.45);
INSERT INTO cust_balance (status, balance) VALUES ('C',000.00);
INSERT INTO cust_balance (status, balance) VALUES ('D', -1.00);
UPDATE cust_balance
SET balance = balance + 123
WHERE cust# <= 2;
UPDATE cust_balance
SET balance = balance * -1
WHERE cust# = -1;
UPDATE cust_balance
SET balance = balance - 123
WHERE cust# = 1;
DELETE
FROM cust_balance
WHERE cust# = 3;
Figure 860, Sample DML statements
Figure 861, Customer-balance table rows
Figure 862, Customer-history table rows
Figure 863, Customer-transaction table rows
Enforcing field uniqueness.
Enforcing field value ranges.
Generating key values.
Maintaining summary columns.
Enforcing relationships between and within tables.
Data access authorization.
Recovery and backup.
CREATE TABLE customer_balance
(cust_id INTEGER
,cust_name VARCHAR(20)
,cust_sex CHAR(1)
,num_sales SMALLINT
,total_sales DECIMAL(12,2)
,master_cust_id INTEGER
,cust_insert_ts TIMESTAMP
,cust_update_ts TIMESTAMP);
CREATE TABLE us_sales
(invoice# INTEGER
,cust_id INTEGER
,sale_value DECIMAL(18,2)
,sale_insert_ts TIMESTAMP
,sale_update_ts TIMESTAMP);
Figure 864, Sample Views used in Join Examples
CUST_ID will be a unique positive integer value, always ascending, never reused, and automatically generated by DB2. This field cannot be updated by a user.
CUST_NAME has the customer name. It can be anything, but not blank.
CUST_SEX must be either "M" or "F".
NUM_SALES will have a count of the sales (for the customer), as recorded in the related US-sales table. The value will be automatically maintained by DB2. It cannot be updated directly by a user.
TOTAL_SALES will have the sum sales (in US dollars) for the customer. The value will be automatically updated by DB2. It cannot be updated directly by a user.
MASTER_CUST_ID will have, if there exists, the customer-ID of the customer that this customer is a dependent of. If there is no master customer, the value is null. If the master customer is deleted, this row will also be deleted (if possible).
CUST_INSERT_TS has the timestamp when the row was inserted. The value is automatically generated by DB2. Any attempt to change will induce an error.
CUST_UPDATE_TS has the timestamp when the row was last updated by a user (note: not by a trigger as a result of a change to the US-sales table). The value is automatically generated by DB2. Any attempt to change will induce an error.
A row can only be deleted when there are no corresponding rows in the US-sales table (i.e. for the same customer).
INVOICE#: will be a unique ascending integer value. The uniqueness will apply to the US-sales table, plus any international sales tables (i.e. to more than one table).
CUST_ID is the customer ID, as recorded in the customer-balance table. No row can be inserted into the US-sales table except that there is a corresponding row in the customer-balance table. Once inserted, this value cannot be updated.
SALE_VALUE is the value of the sale, in US dollars. When a row is inserted, this value is added to the related total-sales value in the customer-balance table. If the value is subsequently updated, the total-sales value is maintained in sync.
SALE_INSERT_TS has the timestamp when the row was inserted. The value is automatically generated by DB2. Any attempt to change will induce an error.
SALE_UPDATE_TS has the timestamp when the row was last updated. The value is automatically generated by DB2. Any attempt to change will induce an error.
Deleting a row from the US-sales table it has no impact on the customer-balance table (i.e. the total-sales is not decremented). But a row can only be deleted from the latter when there are no more related rows in the US-sales table.
Unique indexes.
Secondary non-unique indexes (needed for performance).
Primary and foreign key definitions.
User-defined distinct data types.
Nulls-allowed and not-null columns.
Column value constraint rules.
Before and after triggers.
CREATE DISTINCT TYPE us_dollars AS decimal(18,2) WITH COMPARISONS;
Figure 865, Create US-dollars data type
CREATE TABLE customer_balance
(cust_id INTEGER NOT NULL
GENERATED ALWAYS AS IDENTITY
(START WITH 1
,INCREMENT BY 1
,NO CYCLE
,NO CACHE)
,cust_name VARCHAR(20) NOT NULL
,cust_sex CHAR(1) NOT NULL
,num_sales SMALLINT NOT NULL
,total_sales us_dollars NOT NULL
,master_cust_id INTEGER
,cust_insert_ts TIMESTAMP NOT NULL
,cust_update_ts TIMESTAMP NOT NULL
,PRIMARY KEY (cust_id)
,CONSTRAINT c1 CHECK (cust_name <> '')
,CONSTRAINT c2 CHECK (cust_sex = 'F'
OR cust_sex = 'M')
,CONSTRAINT c3 FOREIGN KEY (master_cust_id)
REFERENCES customer_balance (cust_id)
ON DELETE CASCADE);
Figure 866, Customer-Balance table DDL
The customer-ID is defined as an identity column (see page 263), which means that the value is automatically generated by DB2 using the rules given. The field cannot be updated by the user.
The customer-ID is defined as the primary key, which automatically generates a unique index on the field, and also enables us to reference the field using a referential integrity rule. Being a primary key prevents updates, but we had already prevented them because the field is an identity column.
The total-sales column is uses the type us-dollars.
Constraints C1 and C2 enforce two data validation rules.
Constraint C3 relates the current row to a master customer, if one exists. Furthermore, if the master customer is deleted, this row is also deleted.
All of the columns, except for the master-customer-id, are defined as NOT NULL, which means that a value must be provided.
CREATE TABLE us_sales
(invoice# INTEGER NOT NULL
,cust_id INTEGER NOT NULL
,sale_value us_dollars NOT NULL
,sale_insert_ts TIMESTAMP NOT NULL
,sale_update_ts TIMESTAMP NOT NULL
,PRIMARY KEY (invoice#)
,CONSTRAINT u1 CHECK (sale_value > us_dollars(0))
,CONSTRAINT u2 FOREIGN KEY (cust_id)
REFERENCES customer_balance
ON DELETE RESTRICT);
CREATE INDEX us_sales_cust ON us_sales (cust_id);
Figure 867, US-Sales table DDL
The invoice# is defined as the primary key, which automatically generates a unique index on the field, and also prevents updates.
The sale-value uses the type us-dollars.
Constraint U1 checks that the sale-value is always greater than zero.
Constraint U2 checks that the customer-ID exists in the customer-balance table, and also prevents rows from being deleted from the latter if their exists a related row in this table.
All of the columns are defined as NOT NULL, so a value must be provided for each.
A secondary non-unique index is defined on customer-ID, so that deletes to the customer-balance table (which require checking this table for related customer-ID rows) are as efficient as possible.
Insert.
Update.
Delete.
Row changed.
Statement run.
Before the change is made.
After the change is made.
Set the num-sales to zero.
Set the total-sales to zero.
Set the update-timestamp to the current timestamp.
Set the insert-timestamp to the current timestamp.
CREATE TRIGGER cust_balance_ins1
NO CASCADE BEFORE INSERT
ON customer_balance
REFERENCING NEW AS nnn
FOR EACH ROW
MODE DB2SQL
SET nnn.num_sales = 0
,nnn.total_sales = 0
,nnn.cust_insert_ts = CURRENT TIMESTAMP
,nnn.cust_update_ts = CURRENT TIMESTAMP;
Figure 868, Set values during insert
Set the update-timestamp to the current timestamp.
Prevent updates to the insert-timestamp, or sales fields.
CREATE TRIGGER cust_balance_upd1
NO CASCADE BEFORE UPDATE OF cust_update_ts
ON customer_balance
REFERENCING NEW AS nnn
FOR EACH ROW
MODE DB2SQL
SET nnn.cust_update_ts = CURRENT TIMESTAMP;
Figure 869, Set update-timestamp during update
CREATE TRIGGER cust_balance_upd2
NO CASCADE BEFORE UPDATE OF cust_insert_ts
ON customer_balance
FOR EACH ROW
MODE DB2SQL
SIGNAL SQLSTATE VALUE '71001'
SET MESSAGE_TEXT = 'Cannot update CUST insert-ts';
Figure 870, Prevent update of insert-timestamp
CREATE TRIGGER cust_balance_upd3
NO CASCADE BEFORE UPDATE OF num_sales, total_sales
ON customer_balance
REFERENCING NEW AS nnn
FOR EACH ROW
MODE DB2SQL
WHEN (CURRENT TIMESTAMP NOT IN
(SELECT sss.sale_update_ts
FROM us_sales sss
WHERE nnn.cust_id = sss.cust_id))
SIGNAL SQLSTATE VALUE '71001'
SET MESSAGE_TEXT = 'Feilds only updated via US-Sales';
Figure 871, Prevent update of sales fields
Determine the invoice-number, which is unique over multiple tables.
Set the update-timestamp to the current timestamp.
Set the insert-timestamp to the current timestamp.
Add the sale-value to the existing total-sales in the customer-balance table.
Increment the num-sales counter in the customer-balance table.
CREATE SEQUENCE us_sales_seq
AS INTEGER
START WITH 1
INCREMENT BY 1
NO CYCLE
NO CACHE
ORDER;
Figure 872, Define sequence
CREATE TRIGGER us_sales_ins1
NO CASCADE BEFORE INSERT
ON us_sales
REFERENCING NEW AS nnn
FOR EACH ROW
MODE DB2SQL
SET nnn.invoice# = NEXTVAL FOR us_sales_seq
,nnn.sale_insert_ts = CURRENT TIMESTAMP
,nnn.sale_update_ts = CURRENT TIMESTAMP;
Figure 873, Insert trigger
CREATE TRIGGER sales_to_cust_ins1
AFTER INSERT
ON us_sales
REFERENCING NEW AS nnn
FOR EACH ROW
MODE DB2SQL
UPDATE customer_balance ccc
SET ccc.num_sales = ccc.num_sales + 1
,ccc.total_sales = DECIMAL(ccc.total_sales) +
DECIMAL(nnn.sale_value)
WHERE ccc.cust_id = nnn.cust_id;
Figure 874, Propagate change to Customer-Balance table
Set the update-timestamp to the current timestamp.
Prevent the customer-ID or insert-timestamp from being updated.
Propagate the change to the sale-value to the total-sales in the customer-balance table.
CREATE TRIGGER us_sales_upd1
NO CASCADE BEFORE UPDATE OF sale_value
ON us_sales
REFERENCING NEW AS nnn
OLD AS ooo
FOR EACH ROW
MODE DB2SQL
SET nnn.sale_update_ts = CURRENT TIMESTAMP;
Figure 875, Maintain update-timestamp
CREATE TRIGGER us_sales_upd2
NO CASCADE BEFORE UPDATE OF cust_id, sale_insert_ts
ON us_sales
FOR EACH ROW
MODE DB2SQL
SIGNAL SQLSTATE VALUE '71001'
SET MESSAGE_TEXT = 'Can only update sale_value';
Figure 876, Prevent updates to selected columns
CREATE TRIGGER sales_to_cust_upd1
AFTER UPDATE OF sale_value
ON us_sales
REFERENCING NEW AS nnn
OLD AS ooo
FOR EACH ROW
MODE DB2SQL
UPDATE customer_balance ccc
SET ccc.total_sales = DECIMAL(ccc.total_sales) -
DECIMAL(ooo.sale_value) +
DECIMAL(nnn.sale_value)
WHERE ccc.cust_id = nnn.cust_id;
Figure 877, Propagate change to Customer-Balance table
Primary key - to enforce uniqueness, prevent updates, enable referential integrity.
Unique index - to enforce uniqueness.
Non-unique index - for performance during referential integrity check.
Sequence object - to automatically generate key values for multiple tables.
Identity column - to automatically generate key values for 1 table.
Not-null columns - to prevent use of null values.
Column constraints - to enforce basic domain-range rules.
Distinct types - to prevent one type of data from being combined with another type.
Referential integrity - to enforce relationships between rows/tables, and to enable cascading deletes when needed.
Before triggers - to prevent unwanted changes and set certain values.
After triggers - to propagate valid changes.
CREATE TABLE customer
(cust# INTEGER NOT NULL
,cust_name CHAR(10)
,cust_mgr CHAR(10)
,PRIMARY KEY(cust#));
Figure 878, Customer table
CREATE TABLE customer_his
(cust# INTEGER NOT NULL
,cust_name CHAR(10)
,cust_mgr CHAR(10)
,cur_ts TIMESTAMP NOT NULL
,cur_actn CHAR(1) NOT NULL
,cur_user VARCHAR(10) NOT NULL
,prv_cust# INTEGER
,prv_ts TIMESTAMP
,PRIMARY KEY(cust#,cur_ts));
CREATE UNIQUE INDEX customer_his_x1 ON customer_his
(cust#, prv_ts, cur_ts);
Figure 879, Customer-history table
CREATE TRIGGER customer_ins
AFTER
INSERT ON customer
REFERENCING NEW AS nnn
FOR EACH ROW
MODE DB2SQL
INSERT INTO customer_his VALUES
(nnn.cust#
,nnn.cust_name
,nnn.cust_mgr
,CURRENT TIMESTAMP
,'I'
,USER
,NULL
,NULL);
Figure 880, Insert trigger
CREATE TRIGGER customer_upd
AFTER
UPDATE ON customer
REFERENCING NEW AS nnn
OLD AS ooo
FOR EACH ROW
MODE DB2SQL
INSERT INTO customer_his VALUES
(nnn.cust#
,nnn.cust_name
,nnn.cust_mgr
,CURRENT TIMESTAMP
,'U'
,USER
,ooo.cust#
,(SELECT MAX(cur_ts)
FROM customer_his hhh
WHERE ooo.cust# = hhh.cust#));
Figure 881, Update trigger
CREATE TRIGGER customer_del
AFTER
DELETE ON customer
REFERENCING OLD AS ooo
FOR EACH ROW
MODE DB2SQL
INSERT INTO customer_his VALUES
(ooo.cust#
,NULL
,NULL
,CURRENT TIMESTAMP
,'D'
,USER
,ooo.cust#
,(SELECT MAX(cur_ts)
FROM customer_his hhh
WHERE ooo.cust# = hhh.cust#));
Figure 882, Delete trigger
CREATE TABLE profile
(user_id VARCHAR(10) NOT NULL
,bgn_ts TIMESTAMP NOT NULL DEFAULT '9999-12-31-24.00.00'
,PRIMARY KEY(user_id));
Figure 883, Profile table
CREATE VIEW customer_vw AS
SELECT hhh.*
,ppp.bgn_ts
FROM customer_his hhh
,profile ppp
WHERE ppp.user_id = USER
AND hhh.cur_ts <= ppp.bgn_ts
AND hhh.cur_actn <> 'D'
AND NOT EXISTS
(SELECT *
FROM customer_his nnn
WHERE nnn.prv_cust# = hhh.cust#
AND nnn.prv_ts = hhh.cur_ts
AND nnn.cur_ts <= ppp.bgn_ts);
Figure 884, View of Customer history
CREATE TABLE version
(vrsn INTEGER NOT NULL
,vrsn_bgn_ts TIMESTAMP NOT NULL
,CONSTRAINT version1 CHECK(vrsn >= 0)
,CONSTRAINT version2 CHECK(vrsn < 1000000000)
,PRIMARY KEY(vrsn));
Figure 885, Version table
CREATE TABLE profile
(user_id VARCHAR(10) NOT NULL
,vrsn INTEGER NOT NULL
,vrsn_bgn_ts TIMESTAMP NOT NULL
,CONSTRAINT profile1 FOREIGN KEY(vrsn)
REFERENCES version(vrsn)
ON DELETE RESTRICT
,PRIMARY KEY(user_id));
Figure 886, Profile table
CREATE TABLE customer_his
(cust# INTEGER NOT NULL
,cust_name CHAR(10) NOT NULL
,cust_mgr CHAR(10)
,cur_ts TIMESTAMP NOT NULL
,cur_vrsn INTEGER NOT NULL
,cur_actn CHAR(1) NOT NULL
,cur_user VARCHAR(10) NOT NULL
,prv_cust# INTEGER
,prv_ts TIMESTAMP
,prv_vrsn INTEGER
,CONSTRAINT customer1 FOREIGN KEY(cur_vrsn)
REFERENCES version(vrsn)
ON DELETE RESTRICT
,CONSTRAINT customer2 CHECK(cur_actn IN ('I','U','D'))
,PRIMARY KEY(cust#,cur_vrsn,cur_ts));
CREATE INDEX customer_x2 ON customer_his
(prv_cust#
,prv_ts
,prv_vrsn);
Figure 887, Customer table
CREATE VIEW customer_vw AS
SELECT *
FROM customer_his hhh
,profile ppp
WHERE ppp.user_id = USER
AND hhh.cur_actn <> 'D'
AND ((ppp.vrsn = 0
AND hhh.cur_vrsn = 0)
OR (ppp.vrsn > 0
AND hhh.cur_vrsn = 0
AND hhh.cur_ts < ppp.vrsn_bgn_ts)
OR (ppp.vrsn > 0
AND hhh.cur_vrsn = ppp.vrsn))
AND NOT EXISTS
(SELECT *
FROM customer_his nnn
WHERE nnn.prv_cust# = hhh.cust#
AND nnn.prv_ts = hhh.cur_ts
AND nnn.prv_vrsn = hhh.cur_vrsn
AND ((ppp.vrsn = 0
AND nnn.cur_vrsn = 0)
OR (ppp.vrsn > 0
AND nnn.cur_vrsn = 0
AND nnn.cur_ts < ppp.vrsn_bgn_ts)
OR (ppp.vrsn > 0
AND nnn.cur_vrsn = ppp.vrsn)));
Figure 888, Customer view - 1 of 2
CREATE VIEW customer AS
SELECT cust#
,cust_name
,cust_mgr
FROM customer_vw;
Figure 889, Customer view - 2 of 2
CREATE TRIGGER customer_ins
INSTEAD OF
INSERT ON customer_vw
REFERENCING NEW AS nnn
FOR EACH ROW
MODE DB2SQL
INSERT INTO customer_his VALUES
(nnn.cust#
,nnn.cust_name
,nnn.cust_mgr
,CURRENT TIMESTAMP
,(SELECT vrsn
FROM profile
WHERE user_id = USER)
,CASE
WHEN 0 < (SELECT COUNT(*)
FROM customer
WHERE cust# = nnn.cust#)
THEN RAISE_ERROR('71001','ERROR: Duplicate cust#')
ELSE 'I'
END
,USER
,NULL
,NULL
,NULL);
Figure 890, Insert trigger
CREATE TRIGGER customer_upd
INSTEAD OF
UPDATE ON customer_vw
REFERENCING NEW AS nnn
OLD AS ooo
FOR EACH ROW
MODE DB2SQL
INSERT INTO customer_his VALUES
(nnn.cust#
,nnn.cust_name
,nnn.cust_mgr
,CURRENT TIMESTAMP
,ooo.vrsn
,CASE
WHEN nnn.cust# <> ooo.cust#
THEN RAISE_ERROR('72001','ERROR: Cannot change cust#')
ELSE 'U'
END
,ooo.user_id
,ooo.cust#
,ooo.cur_ts
,ooo.cur_vrsn);
Figure 891, Update trigger
CREATE TRIGGER customer_del
INSTEAD OF
DELETE ON customer_vw
REFERENCING OLD AS ooo
FOR EACH ROW
MODE DB2SQL
INSERT INTO customer_his VALUES
(ooo.cust#
,ooo.cust_name
,ooo.cust_mgr
,CURRENT TIMESTAMP
,ooo.vrsn
,'D'
,ooo.user_id
,ooo.cust#
,ooo.cur_ts
,ooo.cur_vrsn);
Figure 892, Delete trigger
SELECT 'SELECT COUNT(*) FROM ' CONCAT
RTRIM(tabschema) CONCAT
'.' CONCAT
tabname CONCAT
';'
FROM syscat.tables
WHERE tabschema = 'SYSCAT'
AND tabname LIKE 'N%'
ORDER BY tabschema ANSWER
,tabname; =========================================
SELECT COUNT(*) FROM SYSCAT.NAMEMAPPINGS;
SELECT COUNT(*) FROM SYSCAT.NODEGROUPDEF;
SELECT COUNT(*) FROM SYSCAT.NODEGROUPS;
Figure 893, Generate SQL to count rows
EXPORT TO C:\FRED.TXT OF DEL
MODIFIED BY NOCHARDEL
SELECT 'SELECT COUNT(*) FROM ' CONCAT
RTRIM(tabschema) CONCAT
'.' CONCAT
tabname CONCAT
';'
FROM syscat.tables
WHERE tabschema = 'SYSCAT'
AND tabname LIKE 'N%'
ORDER BY tabschema
,tabname;
Figure 894, Export generated SQL statements
SELECT 'SELECT ''' CONCAT
tabname CONCAT
''', COUNT(*) FROM ' CONCAT
RTRIM(tabschema) CONCAT
'.' CONCAT
tabname CONCAT
';'
FROM syscat.tables
WHERE tabschema = 'SYSCAT'
AND tabname LIKE 'N%'
ORDER BY tabschema
,tabname;
ANSWER
==========================================================
SELECT 'NAMEMAPPINGS', COUNT(*) FROM SYSCAT.NAMEMAPPINGS;
SELECT 'NODEGROUPDEF', COUNT(*) FROM SYSCAT.NODEGROUPDEF;
SELECT 'NODEGROUPS', COUNT(*) FROM SYSCAT.NODEGROUPS;
Figure 895, Generate SQL to count rows
WITH temp1 (num) AS
(VALUES (1),(2),(3),(4))
SELECT CASE num
WHEN 1 THEN 'SELECT '''
|| tabname
|| ''' AS tname'
WHEN 2 THEN ' ,COUNT(*)'
|| ' AS #rows'
WHEN 3 THEN 'FROM '
|| RTRIM(tabschema)
|| '.' ANSWER
|| tabname ==============================
|| ';' SELECT 'NAMEMAPPINGS' AS tname
WHEN 4 THEN '' ,COUNT(*) AS #rows
END FROM SYSCAT.NAMEMAPPINGS;
FROM syscat.tables
,temp1 SELECT 'NODEGROUPDEF' AS tname
WHERE tabschema = 'SYSCAT' ,COUNT(*) AS #rows
AND tabname LIKE 'N%' FROM SYSCAT.NODEGROUPDEF;
ORDER BY tabschema
,tabname SELECT 'NODEGROUPS' AS tname
,num; ,COUNT(*) AS #rows
FROM SYSCAT.NODEGROUPS;
Figure 896, Generate SQL to count rows
WITH temp1 (num) AS
(VALUES (1),(2),(3),(4))
SELECT CASE num
WHEN 1 THEN 'SELECT SUM(C1)'
when 2 then 'FROM ('
WHEN 3 THEN ' SELECT COUNT(*) AS C1 FROM ' CONCAT
RTRIM(tabschema) CONCAT
'.' CONCAT
tabname CONCAT
CASE dd
WHEN 1 THEN ''
ELSE ' UNION ALL'
END
WHEN 4 THEN ') AS xxx;'
END
FROM (SELECT tab.*
,ROW_NUMBER() OVER(ORDER BY tabschema ASC
,tabname ASC) AS aa
,ROW_NUMBER() OVER(ORDER BY tabschema DESC
,tabname DESC) AS dd
FROM syscat.tables tab
WHERE tabschema = 'SYSCAT'
AND tabname LIKE 'N%'
)AS xxx
,temp1
WHERE (num <= 2 AND aa = 1)
OR (num = 3)
OR (num = 4 AND dd = 1)
ORDER BY tabschema ASC
,tabname ASC
,num ASC;
ANSWER
===========================================================
SELECT SUM(C1)
FROM (
SELECT COUNT(*) AS C1 FROM SYSCAT.NAMEMAPPINGS UNION ALL
SELECT COUNT(*) AS C1 FROM SYSCAT.NODEGROUPDEF UNION ALL
SELECT COUNT(*) AS C1 FROM SYSCAT.NODEGROUPS
) AS xxx;
Figure 897, Generate SQL to count rows (all tables)
CREATE PROCEDURE CountRows(IN in_tabschema VARCHAR(128)
,in_tabname VARCHAR(128))
NOT DETERMINISTIC
DYNAMIC RESULT SETS 1
BEGIN ATOMIC
DECLARE stmt CLOB(1M) DEFAULT '';
DECLARE c1 CURSOR WITH RETURN FOR s1;
SET stmt = 'SELECT 0 FROM sysibm.sysdummy1';
FOR c2 AS
SELECT tabschema
,tabname
,ROW_NUMBER() OVER(ORDER BY tabschema ASC
,tabname ASC) AS aa
,ROW_NUMBER() OVER(ORDER BY tabschema DESC
,tabname DESC) AS dd
FROM syscat.tables
WHERE tabschema LIKE in_tabschema
AND tabname LIKE in_tabname
ORDER BY tabschema ASC
,tabname ASC
WITH UR
DO
IF aa = 1 THEN
SET stmt = 'SELECT SUM(c1) FROM (';
END IF;
SET stmt = stmt || 'SELECT COUNT(*) AS c1 FROM '
|| RTRIM(tabschema)
|| '.'
|| tabname;
IF dd > 1 THEN
SET stmt = stmt || ' UNION ALL ';
ELSE
SET stmt = stmt || ' ) AS xxx WITH UR ';
END IF;
END FOR;
PREPARE s1 FROM stmt;
OPEN c1;
END
Figure 898, Count rows in all matching tables
ANSWERS
=======
CALL CountRows('SYSCAT%','%'); 9848
CALL CountRows('SYSCAT%','N%'); 5
CALL CountRows('SYSCAT%','Z%'); 0
Figure 899, Run procedure
CREATE PROCEDURE CountRows(IN in_tabschema VARCHAR(128)
,in_tabname VARCHAR(128))
NOT DETERMINISTIC
DYNAMIC RESULT SETS 1
BEGIN ATOMIC
DECLARE temp_rows, num_rows, num_tables INTEGER DEFAULT 0;
DECLARE stmt VARCHAR(200);
DECLARE c1 CURSOR FOR s1;
DECLARE c2 CURSOR WITH RETURN FOR s2;
FOR c3 AS
SELECT tabschema
,tabname
FROM syscat.tables
WHERE tabschema LIKE in_tabschema
AND tabname LIKE in_tabname
ORDER BY tabschema ASC
,tabname ASC
WITH UR
DO
SET stmt = 'SELECT COUNT(*) AS c1 FROM '
|| RTRIM(tabschema)
|| '.'
|| tabname;
PREPARE s1 FROM stmt;
OPEN c1;
FETCH c1 INTO temp_rows;
CLOSE c1;
SET num_rows = num_rows + temp_rows;
SET num_tables = num_tables + 1;
END FOR;
SET stmt = 'WITH temp1 (c1) AS (VALUES (1)) '
|| 'SELECT ' || CHAR(num_rows) || ' AS num_rows '
|| ' , ' || CHAR(num_tables) || ' AS num_tables '
|| 'FROM temp1';
PREPARE s2 FROM stmt;
OPEN c2;
END
Figure 900, Count rows in all matching tables
ANSWERS
===================
NUM_ROWS NUM_TABLES
-------- ----------
CALL CountRows('SYSCAT%','%'); 9867 100
CALL CountRows('SYSCAT%','N%'); 5 3
CALL CountRows('SYSCAT%','Z%'); 0 0
Figure 901, Run procedure
CREATE PROCEDURE CountRows(IN in_tabschema VARCHAR(128)
,in_tabname VARCHAR(128))
NOT DETERMINISTIC
DYNAMIC RESULT SETS 1
BEGIN ATOMIC
DECLARE stmt VARCHAR(1000);
DECLARE GLOBAL TEMPORARY TABLE session.tab_list
(tabschema VARCHAR(128) NOT NULL
,tabname VARCHAR(128) NOT NULL
,tabcard BIGINT NOT NULL
,num_rows BIGINT NOT NULL)
ON COMMIT DELETE ROWS
WITH REPLACE;
INSERT INTO session.tab_list
(SELECT tabschema
,tabname
,card
,0
FROM syscat.tables
WHERE tabschema LIKE in_tabschema
AND tabname LIKE in_tabname
ORDER BY tabschema ASC
,tabname ASC);
FOR f1 AS
SELECT tabschema
,tabname
FROM session.tab_list
DO
SET stmt = 'UPDATE session.tab_list ' ||
'SET num_rows = ' ||
' (SELECT COUNT(*) ' ||
' FROM ' ||
tabschema ||
'.' ||
tabname ||
')' ||
'WHERE tabschema = ''' || tabschema || '''' ||
' AND tabname = ''' || tabname || '''' ;
PREPARE s1 FROM stmt;
EXECUTE s1;
END FOR;
BEGIN
DECLARE c1 CURSOR WITH RETURN FOR
SELECT *
FROM session.tab_list
ORDER BY tabschema
,tabname;
OPEN c1;
END;
END
Figure 902, Count rows in each matching table
WITH temp1 (col1) AS ANSWER
(VALUES 0 ======
UNION ALL COL1
SELECT col1 + 1 ----
FROM temp1 0
WHERE col1 + 1 < 100 1
) 2
SELECT * 3
FROM temp1; etc
Figure 903, Use recursion to get list of 100 numbers
SELECT *
FROM TABLE(NumList(100)) AS xxx;
Figure 904, Use user-defined-function to get list of 100 numbers
WITH temp1 (s1, r1) AS ANSWER
(VALUES (0, RAND(1)) ============
UNION ALL SEQ# RAN1
SELECT s1+1, RAND() ---- -----
FROM temp1 0 0.001
WHERE s1+1 < 5 1 0.563
) 2 0.193
SELECT SMALLINT(s1) AS seq# 3 0.808
,DECIMAL(r1,5,3) AS ran1 4 0.585
FROM temp1;
Figure 905, Use RAND to create pseudo-random numbers
WITH temp1 (s1, r1) AS ANSWER
(VALUES (0, RAND(2)) ========================
UNION ALL SEQ# RAN2 RAN1 RAN3
SELECT s1+1, RAND() ---- ---- ------ ----
FROM temp1 0 13 0.0013 0
WHERE s1+1 < 5 1 8916 0.8916 8
) 2 7384 0.7384 7
SELECT SMALLINT(s1) AS seq# 3 5430 0.5430 5
,SMALLINT(r1*10000) AS ran2 4 8998 0.8998 8
,DECIMAL(r1,6,4) AS ran1
,SMALLINT(r1*10) AS ran3
FROM temp1;
Figure 906, Make differing ranges of random numbers
ANSWER
=======================
S# RAN1 RAN2 RAN3
WITH -- ------ ------ ------
temp1 (s1) AS 0 1251 365370 114753
(VALUES (0) 1 350291 280730 88106
UNION ALL 2 710501 149549 550422
SELECT s1 + 1 3 147312 33311 2339
FROM temp1 4 8911 556 73091
WHERE s1 + 1 < 5
)
SELECT SMALLINT(s1) AS s#
,INTEGER((RAND(1)) * 1E6) AS ran1
,INTEGER((RAND() * RAND()) * 1E6) AS ran2
,INTEGER((RAND() * RAND()* RAND()) * 1E6) AS ran3
FROM temp1;
Figure 907, Create RAND data with different distributions
WITH temp1 (s1, r1) AS ANSWER
(VALUES (0, RAND(2)) ===================
UNION ALL SEQ# RAN2 RAN3 RAN4
SELECT s1+1, RAND() ---- ---- ---- ----
FROM temp1 0 65 A 65
WHERE s1+1 < 5 1 88 X 88
) 2 84 T 84
SELECT SMALLINT(s1) AS seq# 3 79 O 79
,SMALLINT(r1*26+65) AS ran2 4 88 X 88
,CHR(SMALLINT(r1*26+65)) AS ran3
,CHAR(SMALLINT(r1*26)+65) AS ran4
FROM temp1;
Figure 908, Converting RAND output from number to character
CREATE TABLE personnel
(emp# INTEGER NOT NULL
,socsec# CHAR(11) NOT NULL
,job_ftn CHAR(4) NOT NULL
,dept SMALLINT NOT NULL
,salary DECIMAL(7,2) NOT NULL
,date_bn DATE NOT NULL WITH DEFAULT
,fst_name VARCHAR(20)
,lst_name VARCHAR(20)
,CONSTRAINT pex1 PRIMARY KEY (emp#)
,CONSTRAINT pe01 CHECK (emp# > 0)
,CONSTRAINT pe02 CHECK (LOCATE(' ',socsec#) = 0)
,CONSTRAINT pe03 CHECK (LOCATE('-',socsec#,1) = 4)
,CONSTRAINT pe04 CHECK (LOCATE('-',socsec#,5) = 7)
,CONSTRAINT pe05 CHECK (job_ftn <> '')
,CONSTRAINT pe06 CHECK (dept BETWEEN 1 AND 99)
,CONSTRAINT pe07 CHECK (salary BETWEEN 0 AND 99999)
,CONSTRAINT pe08 CHECK (fst_name <> '')
,CONSTRAINT pe09 CHECK (lst_name <> '')
,CONSTRAINT pe10 CHECK (date_bn >= '1900-01-01' ));
CREATE UNIQUE INDEX PEX2 ON PERSONNEL (SOCSEC#);
CREATE UNIQUE INDEX PEX3 ON PERSONNEL (DEPT, EMP#);
Figure 909, Production-like test table DDL
INSERT INTO personnel
WITH temp1 (s1,r1,r2,r3,r4) AS
(VALUES (0
,RAND(2)
,RAND()+(RAND()/1E5)
,RAND()* RAND()
,RAND()* RAND()* RAND())
UNION ALL
SELECT s1 + 1
,RAND()
,RAND()+(RAND()/1E5)
,RAND()* RAND()
,RAND()* RAND()* RAND()
FROM temp1
WHERE s1 < 10000)
SELECT 100000 + s1
,SUBSTR(DIGITS(INT(r2*988+10)),8) || '-' ||
SUBSTR(DIGITS(INT(r1*88+10)),9) || '-' ||
TRANSLATE(SUBSTR(DIGITS(s1),7),'9873450126','0123456789')
,CASE
WHEN INT(r4*9) > 7 THEN 'MGR'
WHEN INT(r4*9) > 5 THEN 'SUPR'
WHEN INT(r4*9) > 3 THEN 'PGMR'
WHEN INT(R4*9) > 1 THEN 'SEC'
ELSE 'WKR'
END
,INT(r3*98+1)
,DECIMAL(r4*99999,7,2)
,DATE('1930-01-01') + INT(50-(r4*50)) YEARS
+ INT(r4*11) MONTHS
+ INT(r4*27) DAYS
,CHR(INT(r1*26+65))|| CHR(INT(r2*26+97))|| CHR(INT(r3*26+97))||
CHR(INT(r4*26+97))|| CHR(INT(r3*10+97))|| CHR(INT(r3*11+97))
,CHR(INT(r2*26+65))||
TRANSLATE(CHAR(INT(r2*1E7)),'aaeeiibmty','0123456789')
FROM temp1;
Figure 910, Production-like test table INSERT
EMP# SOCSEC# JOB_ DEPT SALARY DATE_BN F_NME L_NME
------ ----------- ---- ---- --------- ---------- --------- ---------
100000 484-10-9999 WKR 47 13.63 1979-01-01 Ammaef Mimytmbi
100001 449-38-9998 SEC 53 35758.87 1962-04-10 Ilojff Liiiemea
100002 979-90-9997 WKR 1 8155.23 1975-01-03 Xzacaa Zytaebma
100003 580-50-9993 WKR 31 16643.50 1971-02-05 Lpiedd Pimmeeat
100004 264-87-9994 WKR 21 962.87 1979-01-01 Wgfacc Geimteei
100005 661-84-9995 WKR 19 4648.38 1977-01-02 Wrebbc Rbiybeet
100006 554-53-9990 WKR 8 375.42 1979-01-01 Mobaaa Oiiaiaia
100007 482-23-9991 SEC 36 23170.09 1968-03-07 Emjgdd Mimtmamb
100008 536-41-9992 WKR 6 10514.11 1974-02-03 Jnbcaa Nieebayt
Figure 911, Production-like test table, Sample Output
CREATE TABLE time_series
(KYY CHAR(03) NOT NULL
,bgn_dt DATE NOT NULL
,end_dt DATE NOT NULL
,CONSTRAINT tsc1 CHECK (kyy <> '')
,CONSTRAINT tsc2 CHECK (bgn_dt <= end_dt));
COMMIT;
INSERT INTO TIME_series values
('AAA','1995-10-01','1995-10-04'),
('AAA','1995-10-06','1995-10-06'),
('AAA','1995-10-07','1995-10-07'),
('AAA','1995-10-15','1995-10-19'),
('BBB','1995-10-01','1995-10-01'),
('BBB','1995-10-03','1995-10-03');
Figure 912, Sample Table DDL - Time Series
Figure 913, Overlapping Time-Series rows - Definition
SELECT kyy ANSWER
,bgn_dt =========
,end_dt
FROM time_series a
WHERE EXISTS
(SELECT *
FROM time_series b
WHERE a.kyy = b.kyy
AND a.bgn_dt <> b.bgn_dt
AND (a.bgn_dt BETWEEN b.bgn_dt AND b.end_dt
OR b.bgn_dt BETWEEN a.bgn_dt AND a.end_dt))
ORDER BY 1,2;
Figure 914, Find overlapping rows in time-series
SELECT a.kyy TIME_SERIES
,a.bgn_dt +-------------------------+
,a.end_dt |KYY|BGN_DT |END_DT |
,b.bgn_dt |---|----------|----------|
,b.end_dt |AAA|1995-10-01|1995-10-04|
,DAYS(b.bgn_dt) - |AAA|1995-10-06|1995-10-06|
DAYS(A.end_dt) |AAA|1995-10-07|1995-10-07|
as diff |AAA|1995-10-15|1995-10-19|
FROM time_series a |BBB|1995-10-01|1995-10-01|
,time_series b |BBB|1995-10-03|1995-10-03|
WHERE a.kyy = b.kyy +-------------------------+
AND a.end_dt < b.bgn_dt - 1 DAY
AND NOT EXISTS
(SELECT *
FROM time_series z
WHERE z.kyy = a.kyy
AND z.kyy = b.kyy
AND z.bgn_dt > a.bgn_dt
AND z.bgn_dt < b.bgn_dt)
ORDER BY 1,2;
Figure 915, Find gap in Time-Series, SQL
KEYCOL BGN_DT END_DT BGN_DT END_DT DIFF
------ ---------- ---------- ---------- ---------- ----
AAA 1995-10-01 1995-10-04 1995-10-06 1995-10-06 2
AAA 1995-10-07 1995-10-07 1995-10-15 1995-10-19 8
BBB 1995-10-01 1995-10-01 1995-10-03 1995-10-03 2
Figure 916, Find gap in Time-Series, Answer
SELECT a.kyy AS kyy TIME_SERIES
,a.end_dt + 1 DAY AS bgn_gap +-------------------------+
,b.bgn_dt - 1 DAY AS end_gap |KYY|BGN_DT |END_DT |
,(DAYS(b.bgn_dt) - |---|----------|----------|
DAYS(a.end_dt) - 1) AS sz |AAA|1995-10-01|1995-10-04|
FROM time_series a |AAA|1995-10-06|1995-10-06|
,time_series b |AAA|1995-10-07|1995-10-07|
WHERE a.kyy = b.kyy |AAA|1995-10-15|1995-10-19|
AND a.end_dt < b.bgn_dt - 1 DAY |BBB|1995-10-01|1995-10-01|
AND NOT EXISTS |BBB|1995-10-03|1995-10-03|
(SELECT * +-------------------------+
FROM time_series z
WHERE z.kyy = a.kyy ANSWER
AND z.kyy = b.kyy ============================
AND z.bgn_dt > a.bgn_dt KYY BGN_GAP END_GAP SZ
AND z.bgn_dt < b.bgn_dt) --- ---------- ---------- --
ORDER BY 1,2; AAA 1995-10-05 1995-10-05 1
AAA 1995-10-08 1995-10-14 7
BBB 1995-10-02 1995-10-02 1
Figure 917, Find gap in Time-Series
WITH temp TIME_SERIES
(kyy, gap_dt, gsize) AS +-------------------------+
(SELECT a.kyy |KYY|BGN_DT |END_DT |
,a.end_dt + 1 DAY |---|----------|----------|
,(DAYS(b.bgn_dt) - |AAA|1995-10-01|1995-10-04|
DAYS(a.end_dt) - 1) |AAA|1995-10-06|1995-10-06|
FROM time_series a |AAA|1995-10-07|1995-10-07|
,time_series b |AAA|1995-10-15|1995-10-19|
WHERE a.kyy = b.kyy |BBB|1995-10-01|1995-10-01|
AND a.end_dt < b.bgn_dt - 1 DAY |BBB|1995-10-03|1995-10-03|
AND NOT EXISTS +-------------------------+
(SELECT *
FROM time_series z
WHERE z.kyy = a.kyy
AND z.kyy = b.kyy ANSWER
AND z.bgn_dt > a.bgn_dt =======================
AND z.bgn_dt < b.bgn_dt) KEYCOL GAP_DT GSIZE
UNION ALL ------ ---------- -----
SELECT kyy AAA 1995-10-05 1
,gap_dt + 1 DAY AAA 1995-10-08 7
,gsize - 1 AAA 1995-10-09 6
FROM temp AAA 1995-10-10 5
WHERE gsize > 1 AAA 1995-10-11 4
) AAA 1995-10-12 3
SELECT * AAA 1995-10-13 2
FROM temp AAA 1995-10-14 1
ORDER BY 1,2; BBB 1995-10-02 1
Figure 918, Show each day in Time-Series gap
Figure 919, TABLESAMPLE Syntax
SELECT *
FROM staff TABLESAMPLE BERNOULLI(5) REPEATABLE(1234)
ORDER BY id;
Figure 920, Sample rows in STAFF table
SELECT *
FROM employee ee TABLESAMPLE BERNOULLI(18)
,emp_act ea TABLESAMPLE BERNOULLI(25)
WHERE ee.empno = ea.empno
ORDER BY ee.empno;
Figure 921, Sample rows in two tables
DECLARE GLOBAL TEMPORARY TABLE session.nyc_staff
LIKE staff;
SELECT *
FROM session.nyc_staff TABLESAMPLE SYSTEM(34.55)
WHERE id < 100
AND salary > 100
ORDER BY id;
Figure 922, Sample Views used in Join Examples
WITH temp1 (c1) AS ANSWER (numbers shortened)
(VALUES '123 ',' 345 ',' 567') =================================
SELECT c1 C1 DBL DEC SML INT
,DOUBLE(c1) AS dbl ----- ----------- ----- ---- ----
,DECIMAL(c1,3) AS dec 123 +1.2300E+2 123. 123 123
,SMALLINT(c1) AS sml 345 +3.4500E+2 345. 345 345
,INTEGER(c1) AS int 567 +5.6700E+2 567. 567 567
FROM temp1;
Figure 923, Convert Character to Numeric - SQL
INPUT STRING COMPATIBLE FUNCTIONS
============ ==========================================
" 1234" DOUBLE, DECIMAL, INTEGER, SMALLINT, BIGINT
" 12.4" DOUBLE, DECIMAL
" 12E4" DOUBLE
Figure 924, Acceptable conversion values
WITH temp1 (c1) AS (VALUES ' 123','456 ',' 1 2',' 33%',NULL)
SELECT c1
,TRANSLATE(c1,' ','1234567890') AS c2
,LENGTH(LTRIM(TRANSLATE(c1,' ','1234567890'))) AS c3
FROM temp1;
ANSWER
============
C1 C2 C3
---- ---- --
123 0
456 0
1 2 0
33% % 1
- - -
Figure 925, Checking for non-digits
--#SET DELIMITER ! IMPORTANT
============
CREATE FUNCTION isnumeric(instr VARCHAR(40)) This example
RETURNS CHAR(1) uses an "!"
BEGIN ATOMIC as the stmt
DECLARE is_number CHAR(1) DEFAULT 'Y'; delimiter.
DECLARE bgn_blank CHAR(1) DEFAULT 'Y';
DECLARE found_num CHAR(1) DEFAULT 'N';
DECLARE found_pos CHAR(1) DEFAULT 'N';
DECLARE found_neg CHAR(1) DEFAULT 'N';
DECLARE found_dot CHAR(1) DEFAULT 'N';
DECLARE ctr SMALLINT DEFAULT 1;
IF instr IS NULL THEN
RETURN NULL;
END IF;
wloop:
WHILE ctr <= LENGTH(instr) AND
is_number = 'Y'
DO
-----------------------------
--- ERROR CHECKS ---
-----------------------------
IF SUBSTR(instr,ctr,1) NOT IN (' ','.','+','-','0','1','2'
,'3','4','5','6','7','8','9') THEN
SET is_number = 'N';
ITERATE wloop;
END IF;
IF SUBSTR(instr,ctr,1) = ' ' AND
bgn_blank = 'N' THEN
SET is_number = 'N';
ITERATE wloop;
END IF;
IF SUBSTR(instr,ctr,1) = '.' AND
found_dot = 'Y' THEN
SET is_number = 'N';
ITERATE wloop;
END IF;
IF SUBSTR(instr,ctr,1) = '+' AND
(found_neg = 'Y' OR
bgn_blank = 'N') THEN
SET is_number = 'N';
ITERATE wloop;
END IF;
IF SUBSTR(instr,ctr,1) = '-' AND
(found_neg = 'Y' OR
bgn_blank = 'N') THEN
SET is_number = 'N';
ITERATE wloop;
END IF;
-----------------------------
--- MAINTAIN FLAGS & CTR ---
-----------------------------
IF SUBSTR(instr,ctr,1) IN ('0','1','2','3','4'
,'5','6','7','8','9') THEN
SET found_num = 'Y';
END IF;
IF SUBSTR(instr,ctr,1) = '.' THEN
SET found_dot = 'Y';
END IF;
IF SUBSTR(instr,ctr,1) = '+' THEN
SET found_pos = 'Y';
END IF;
IF SUBSTR(instr,ctr,1) = '-' THEN
SET found_neg = 'Y';
END IF;
Figure 926, Check Numeric function, part 1 of 2
IF SUBSTR(instr,ctr,1) <> ' ' THEN
SET bgn_blank = 'N';
END IF;
SET ctr = ctr + 1;
END WHILE wloop;
IF found_num = 'N' THEN
SET is_number = 'N';
END IF;
RETURN is_number;
END!
WITH TEMP1 (C1) AS
(VALUES ' 123'
,'+123.45'
,'456 '
,' 10 2 '
,' -.23' ANSWER
,'++12356' ====================
,'.012349' C1 C2 C3
,' 33%' ------- -- ---------
,' ' 123 Y 123.00000
,NULL) +123.45 Y 123.45000
SELECT C1 AS C1 456 N -
,isnumeric(C1) AS C2 10 2 N -
,CASE -.23 Y -0.23000
WHEN isnumeric(C1) = 'Y' ++12356 N -
THEN DECIMAL(C1,10,6) .012349 Y 0.01234
ELSE NULL 33% N -
END AS C3 N -
FROM TEMP1! - - -
Figure 927, Check Numeric function, part 2 of 2
SELECT d_sal
,CHAR(d_sal) AS d_chr
,DIGITS(d_sal) AS d_dgt
,i_sal
,CHAR(i_sal) AS i_chr
,DIGITS(i_sal) AS i_dgt
FROM (SELECT DEC(salary - 11000,6,2) AS d_sal
,SMALLINT(salary - 11000) AS i_sal
FROM staff
WHERE salary > 10000
AND salary < 12200
)AS xxx ANSWER
ORDER BY d_sal; =========================================
D_SAL D_CHR D_DGT I_SAL I_CHR I_DGT
------- -------- ------ ----- ----- -----
-494.10 -0494.10 049410 -494 -494 00494
-12.00 -0012.00 001200 -12 -12 00012
508.60 0508.60 050860 508 508 00508
1009.75 1009.75 100975 1009 1009 01009
Figure 928, CHAR and DIGITS function usage
CREATE FUNCTION CHAR_RIGHT(inval SMALLINT)
RETURNS CHAR(06)
RETURN RIGHT(CHAR('',06) CONCAT RTRIM(CHAR(inval)),06);
CREATE FUNCTION CHAR_RIGHT(inval INTEGER)
RETURNS CHAR(11)
RETURN RIGHT(CHAR('',11) CONCAT RTRIM(CHAR(inval)),11);
CREATE FUNCTION CHAR_RIGHT(inval BIGINT)
RETURNS CHAR(20)
RETURN RIGHT(CHAR('',20) CONCAT RTRIM(CHAR(inval)),20);
Figure 929, User-defined functions - convert integer to character
SELECT i_sal ANSWER
,CHAR_RIGHT(i_sal) AS i_chr ===========
FROM (SELECT SMALLINT(salary - 11000) AS i_sal I_SAL I_CHR
FROM staff ----- -----
WHERE salary > 10000 -494 -494
AND salary < 12200 -12 -12
)AS xxx 508 508
ORDER BY i_sal; 1009 1009
Figure 930, Convert SMALLINT to CHAR
CREATE FUNCTION CHAR_RIGHT(inval DECIMAL(31,12))
RETURNS CHAR(33)
RETURN CHAR_RIGHT(BIGINT(inval))
CONCAT '.'
CONCAT SUBSTR(DIGITS(inval - TRUNCATE(inval,0)),20,12);
Figure 931, User-defined functions - convert decimal to character
SELECT d_sal
,CHAR_RIGHT(d_sal) AS d_chr
FROM (SELECT DEC(salary - 11000,6,2) AS d_sal
FROM staff
WHERE salary > 10000 ANSWER
AND salary < 12200 =========================
)AS xxx D_SAL D_CHR
ORDER BY d_sal; ------- -----------------
-494.10 -494.100000000000
-12.00 -12.000000000000
508.60 508.600000000000
1009.75 1009.750000000000
Figure 932, Convert DECIMAL to CHAR
WITH tab1(ts1) AS
(VALUES CAST('1998-11-22-03.44.55.123456' AS TIMESTAMP))
SELECT ts1 => 1998-11-22-03.44.55.123456
, HEX(ts1) => 19981122034455123456
, DEC(HEX(ts1),20) => 19981122034455123456.
,FLOAT(DEC(HEX(ts1),20)) => 1.99811220344551e+019
,REAL (DEC(HEX(ts1),20)) => 1.998112e+019
FROM tab1;
Figure 933, Convert Timestamp to number
SELECT empno
,firstnme
,lastname
,job
FROM employee
WHERE empno < '000100'
ORDER BY empno;
Figure 934, Sample query with no column control
SELECT empno
,CASE :host-var-1
WHEN 1 THEN firstnme
ELSE ''
END AS firstnme
,CASE :host-var-2
WHEN 1 THEN lastname
ELSE ''
END AS lastname
,CASE :host-var-3
WHEN 1 THEN VARCHAR(job)
ELSE ''
END AS job
FROM employee
WHERE empno < '000100'
ORDER BY empno;
Figure 935, Sample query with column control
SELECT id
,salary
,INT(salary / 1500) AS len
,REPEAT('*',INT(salary / 1500)) AS salary_chart
FROM staff
WHERE id > 120 ANSWER
AND id < 190 ===================================
ORDER BY id; ID SALARY LEN SALARY_CHART
--- -------- --- ---------------
130 10505.90 7 *******
140 21150.00 14 **************
150 19456.50 12 ************
160 22959.20 15 ***************
170 12258.50 8 ********
180 12009.75 8 ********
Figure 936, Make chart using SQL
ANSWER
===================================
ID SALARY SALARY_CHART
WITH --- -------- --------------------
temp1 (id, salary) AS 130 10505.90 *********
(SELECT id 140 21150.00 ******************
,salary 150 19456.50 ****************
FROM staff 160 22959.20 ********************
WHERE id > 120 170 12258.50 **********
AND id < 190), 180 12009.75 **********
temp2 (max_sal) AS
(SELECT INT(MAX(salary)) / 20
FROM temp1)
SELECT id
,salary
,VARCHAR(REPEAT('*',INT(salary / max_sal)),20) AS salary_chart
FROM temp1
,temp2
ORDER BY id;
Figure 937, Make chart of fixed length
SELECT sex ANSWER >> SEX NUM
,COUNT(*) AS num --- ---
FROM stats F 595
GROUP BY sex M 405
ORDER BY sex;
Figure 938, Use GROUP BY to get counts
WITH f (f) AS (SELECT COUNT(*) FROM stats WHERE sex = 'F')
,m (m) AS (SELECT COUNT(*) FROM stats WHERE sex = 'M')
SELECT f, m
FROM f, m;
Figure 939, Use Common Table Expression to get counts
SELECT SUM(CASE sex WHEN 'F' THEN 1 ELSE 0 END) AS female
,SUM(CASE sex WHEN 'M' THEN 1 ELSE 0 END) AS male
FROM stats;
Figure 940, Use CASE and SUM to get counts
SELECT COUNT(*) AS total
,SUM(CASE sex WHEN 'F' THEN 1 ELSE 0 END) AS female
,SUM(CASE sex WHEN 'M' THEN 1 ELSE 0 END) AS male
FROM stats;
Figure 941, Use CASE and SUM to get counts
SELECT years ANSWER
,COUNT(*) AS #staff =============
FROM staff YEARS #STAFF
WHERE UCASE(name) LIKE '%E%' ----- ------
AND years <= 5 1 1
GROUP BY years; 4 2
5 3
Figure 942, Count staff joined per year
WITH list_years (year#) AS ANSWER
(VALUES (0),(1),(2),(3),(4),(5) ============
) YEARS #STAFF
SELECT year# AS years ----- ------
,COALESCE(#stff,0) AS #staff 0 0
FROM list_years 1 1
LEFT OUTER JOIN 2 0
(SELECT years 3 0
,COUNT(*) AS #stff 4 2
FROM staff 5 3
WHERE UCASE(name) LIKE '%E%'
AND years <= 5
GROUP BY years
)AS xxx
ON year# = years
ORDER BY 1;
Figure 943, Count staff joined per year, all years
WITH list_years (year#) AS ANSWER
(VALUES SMALLINT(0) ============
UNION ALL YEARS #STAFF
SELECT year# + 1 ----- ------
FROM list_years 0 0
WHERE year# < 5) 1 1
SELECT year# AS years 2 0
,COALESCE(#stff,0) AS #staff 3 0
FROM list_years 4 2
LEFT OUTER JOIN 5 3
(SELECT years
,COUNT(*) AS #stff
FROM staff
WHERE UCASE(name) LIKE '%E%'
AND years <= 5
GROUP BY years
)AS xxx
ON year# = years
ORDER BY 1;
Figure 944, Count staff joined per year, all years
WITH list_years (year#) AS ANSWER
(VALUES SMALLINT(0) ======
UNION ALL YEAR#
SELECT year# + 1 -----
FROM list_years 0
WHERE year# < 5) 2
SELECT year# 3
FROM list_years y
WHERE NOT EXISTS
(SELECT *
FROM staff s
WHERE UCASE(s.name) LIKE '%E%'
AND s.years = y.year#)
ORDER BY 1;
Figure 945, List years when no staff joined
WITH category (cat,subcat,dept) AS
(VALUES ('1ST','ROWS IN TABLE ','')
,('2ND','SALARY > $20K ','')
,('3RD','NAME LIKE ABC%','')
,('4TH','NUMBER MALES ','')
UNION
SELECT '5TH',deptname,deptno
FROM department
)
SELECT xxx.cat AS "category"
,xxx.subcat AS "subcategory/dept"
,SUM(xxx.found) AS "#rows"
FROM (SELECT cat.cat
,cat.subcat
,CASE
WHEN emp.empno IS NULL THEN 0
ELSE 1
END AS found
FROM category cat
LEFT OUTER JOIN
employee emp
ON cat.subcat = 'ROWS IN TABLE'
OR (cat.subcat = 'NUMBER MALES'
AND emp.sex = 'M')
OR (cat.subcat = 'SALARY > $20K'
AND emp.salary > 20000)
OR (cat.subcat = 'NAME LIKE ABC%'
AND emp.firstnme LIKE 'ABC%')
OR (cat.dept <> ''
AND cat.dept = emp.workdept)
)AS xxx
GROUP BY xxx.cat
,xxx.subcat
ORDER BY 1,2;
Figure 946, Multiple counts in one pass, SQL
CATEGORY SUBCATEGORY/DEPT #ROWS
-------- ----------------------------- -----
1ST ROWS IN TABLE 32
2ND SALARY > $20K 25
3RD NAME LIKE ABC% 0
4TH NUMBER MALES 19
5TH ADMINISTRATION SYSTEMS 6
5TH DEVELOPMENT CENTER 0
5TH INFORMATION CENTER 3
5TH MANUFACTURING SYSTEMS 9
5TH OPERATIONS 5
5TH PLANNING 1
5TH SOFTWARE SUPPORT 4
5TH SPIFFY COMPUTER SERVICE DIV. 3
5TH SUPPORT SERVICES 1
Figure 947, Multiple counts in one pass, Answer
WITH
temp1 (id, data) AS
(VALUES (01,'SOME TEXT TO PARSE.')
,(02,'MORE SAMPLE TEXT.')
,(03,'ONE-WORD.')
,(04,'')
),
temp2 (id, word#, word, data_left) AS
(SELECT id
,SMALLINT(1)
,SUBSTR(data,1,
CASE LOCATE(' ',data)
WHEN 0 THEN LENGTH(data)
ELSE LOCATE(' ',data)
END)
,LTRIM(SUBSTR(data,
CASE LOCATE(' ',data)
WHEN 0 THEN LENGTH(data) + 1
ELSE LOCATE(' ',data)
END))
FROM temp1
WHERE data <> ''
UNION ALL
SELECT id
,word# + 1
,SUBSTR(data_left,1,
CASE LOCATE(' ',data_left)
WHEN 0 THEN LENGTH(data_left)
ELSE LOCATE(' ',data_left)
END)
,LTRIM(SUBSTR(data_left,
CASE LOCATE(' ',data_left)
WHEN 0 THEN LENGTH(data_left) + 1
ELSE LOCATE(' ',data_left)
END))
FROM temp2
WHERE data_left <> ''
)
SELECT *
FROM temp2
ORDER BY 1,2;
Figure 948, Break text into words - SQL
ID WORD# WORD DATA_LEFT
-- ----- --------- --------------
1 1 SOME TEXT TO PARSE.
1 2 TEXT TO PARSE.
1 3 TO PARSE.
1 4 PARSE.
2 1 MORE SAMPLE TEXT.
2 2 SAMPLE TEXT.
2 3 TEXT.
3 1 ONE-WORD.
Figure 949, Break text into words - Answer
WITH temp1 (dept,w#,name,all_names) AS
(SELECT dept
,SMALLINT(1)
,MIN(name)
,VARCHAR(MIN(name),50)
FROM staff a
GROUP BY dept
UNION ALL
SELECT a.dept
,SMALLINT(b.w#+1)
,a.name
,b.all_names || ' ' || a.name
FROM staff a
,temp1 b
WHERE a.dept = b.dept
AND a.name > b.name
AND a.name =
(SELECT MIN(c.name)
FROM staff c
WHERE c.dept = b.dept
AND c.name > b.name)
)
SELECT dept
,w#
,name AS max_name
,all_names
FROM temp1 d
WHERE w# =
(SELECT MAX(w#)
FROM temp1 e
WHERE d.dept = e.dept)
ORDER BY dept;
Figure 950, Denormalize Normalized Data - SQL
DEPT W# MAX_NAME ALL_NAMES
---- -- --------- -------------------------------------------
10 4 Molinare Daniels Jones Lu Molinare
15 4 Rothman Hanes Kermisch Ngan Rothman
20 4 Sneider James Pernal Sanders Sneider
38 5 Quigley Abrahams Marenghi Naughton O'Brien Quigley
42 4 Yamaguchi Koonitz Plotz Scoutten Yamaguchi
51 5 Williams Fraye Lundquist Smith Wheeler Williams
66 5 Wilson Burke Gonzales Graham Lea Wilson
84 4 Quill Davis Edwards Gafney Quill
Figure 951, Denormalize Normalized Data - Answer
CREATE FUNCTION list_names(indept SMALLINT) IMPORTANT
RETURNS VARCHAR(50) ============
BEGIN ATOMIC This example
DECLARE outstr VARCHAR(50) DEFAULT ''; uses an "!"
FOR list_names AS as the stmt
SELECT name delimiter.
FROM staff
WHERE dept = indept
ORDER BY name
DO
SET outstr = outstr || name || ' ';
END FOR;
SET outstr = rtrim(outstr);
RETURN outstr;
END!
SELECT dept AS DEPT
,SMALLINT(cnt) AS W#
,mxx AS MAX_NAME
,list_names(dept) AS ALL_NAMES
FROM (SELECT dept
,COUNT(*) as cnt
,MAX(name) AS mxx
FROM staff
GROUP BY dept
)as ddd
ORDER BY dept!
Figure 952, Creating a function to denormalize names
--#SET DELIMITER ! IMPORTANT
============
CREATE FUNCTION reverse(instr VARCHAR(50)) This example
RETURNS VARCHAR(50) uses an "!"
BEGIN ATOMIC as the stmt
DECLARE outstr VARCHAR(50) DEFAULT ''; delimiter.
DECLARE curbyte SMALLINT DEFAULT 0;
SET curbyte = LENGTH(RTRIM(instr));
WHILE curbyte >= 1 DO
SET outstr = outstr || SUBSTR(instr,curbyte,1);
SET curbyte = curbyte - 1;
END WHILE;
RETURN outstr;
END!
ANSWER
SELECT id AS ID ====================
,name AS NAME1 ID NAME1 NAME2
,reverse(name) AS NAME2 -- -------- -------
FROM staff 10 Sanders srednaS
WHERE id < 40 20 Pernal lanreP
ORDER BY id! 30 Marenghi ihgneraM
Figure 953, Reversing character field
SELECT id AS ID
,salary AS SALARY1
,DEC(reverse(CHAR(salary)),7,4) AS SALARY2
FROM staff ANSWER
WHERE id < 40 ===================
ORDER BY id; ID SALARY1 SALARY2
-- -------- -------
10 18357.50 5.7538
20 18171.25 52.1718
30 17506.75 57.6057
Figure 954, Reversing numeric field
WITH temp (txt) AS ANSWER
(VALUES (' HAS LEADING BLANKS') =======================
,('HAS TRAILING BLANKS ') TXT2 LEN
,(' BLANKS BOTH ENDS ')) ------------------- ---
SELECT LTRIM(RTRIM(txt)) AS txt2 HAS LEADING BLANKS 18
,LENGTH(LTRIM(RTRIM(txt))) AS len HAS TRAILING BLANKS 19
FROM temp; BLANKS BOTH ENDS 16
Figure 955, Stripping leading and trailing blanks
--#SET DELIMITER !
CREATE FUNCTION strp(in_val VARCHAR(20),in_strip VARCHAR(1))
RETURNS VARCHAR(20)
BEGIN ATOMIC
DECLARE cur_pos SMALLINT;
DECLARE stp_flg CHAR(1);
DECLARE out_val VARCHAR(20);
IF in_strip = '' THEN
SIGNAL SQLSTATE '75001'
SET MESSAGE_TEXT = 'Strip char is zero length';
END IF;
SET cur_pos = 1;
SET stp_flg = 'Y';
WHILE stp_flg = 'Y' AND cur_pos <= length(in_val) DO
IF SUBSTR(in_val,cur_pos,1) <> in_strip THEN
SET stp_flg = 'N';
ELSE
SET cur_pos = cur_pos + 1;
END IF;
END WHILE;
SET out_val = SUBSTR(in_val,cur_pos);
SET cur_pos = length(out_val);
SET stp_flg = 'Y';
WHILE stp_flg = 'Y' AND cur_pos >= 1 DO
IF SUBSTR(out_val,cur_pos,1) <> in_strip THEN
SET stp_flg = 'N';
ELSE
SET cur_pos = cur_pos - 1; IMPORTANT
END IF; ============
END WHILE; This example
SET out_val = SUBSTR(out_val,1,cur_pos); uses an "!"
RETURN out_val; as the stmt
END! delimiter.
Figure 956, Define strip function
WITH word1 (w#, word_val) AS ANSWER
(VALUES(1,'00 abc 000') ========================
,(2,'0 0 abc') W# WORD_VAL STP LEN
,(3,' sdbs') -- ---------- ------ ---
,(4,'000 0') 1 00 abc 000 abc 5
,(5,'0000') 2 0 0 abc 0 abc 6
,(6,'0') 3 sdbs sdbs 5
,(7,'a') 4 000 0 1
,(8,'')) 5 0000 0
SELECT w# 6 0 0
,word_val 7 a a 1
,strp(word_val,'0') AS stp 8 0
,length(strp(word_val,'0')) AS len
FROM word1
ORDER BY w#;
Figure 957, Use strip function
--#SET DELIMITER !
CREATE FUNCTION sort_char(in_val VARCHAR(20),sort_dir VARCHAR(1))
RETURNS VARCHAR(20)
BEGIN ATOMIC
DECLARE cur_pos SMALLINT;
DECLARE do_sort CHAR(1);
DECLARE out_val VARCHAR(20);
IF UCASE(sort_dir) NOT IN ('A','D') THEN
SIGNAL SQLSTATE '75001'
SET MESSAGE_TEXT = 'Sort order not ''A'' or ''D''';
END IF;
SET out_val = in_val;
SET do_sort = 'Y';
WHILE do_sort = 'Y' DO
SET do_sort = 'N'; IMPORTANT
SET cur_pos = 1; ============
WHILE cur_pos < length(in_val) DO This example
IF (UCASE(sort_dir) = 'A' uses an "!"
AND SUBSTR(out_val,cur_pos+1,1) < as the stmt
SUBSTR(out_val,cur_pos,1)) delimiter.
OR (UCASE(sort_dir) = 'D'
AND SUBSTR(out_val,cur_pos+1,1) >
SUBSTR(out_val,cur_pos,1)) THEN
SET do_sort = 'Y';
SET out_val = CASE
WHEN cur_pos = 1
THEN ''
ELSE SUBSTR(out_val,1,cur_pos-1)
END
CONCAT SUBSTR(out_val,cur_pos+1,1)
CONCAT SUBSTR(out_val,cur_pos ,1)
CONCAT
CASE
WHEN cur_pos = length(in_val) - 1
THEN ''
ELSE SUBSTR(out_val,cur_pos+2)
END;
END IF;
SET cur_pos = cur_pos + 1;
END WHILE;
END WHILE;
RETURN out_val;
END!
Figure 958, Define sort-char function
WITH word1 (w#, word_val) AS ANSWER
(VALUES(1,'12345678') =============================
,(2,'ABCDEFG') W# WORD_VAL SA SD
,(3,'AaBbCc') -- --------- ------- --------
,(4,'abccb') 1 12345678 12345678 87654321
,(5,'''%#.') 2 ABCDEFG ABCDEFG GFEDCBA
,(6,'bB') 3 AaBbCc aAbBcC CcBbAa
,(7,'a') 4 abccb abbcc ccbba
,(8,'')) 5 '%#. .'#% %#'.
SELECT w# 6 bB bB Bb
,word_val 7 a a a
,sort_char(word_val,'a') sa 8
,sort_char(word_val,'D') sd
FROM word1
ORDER BY w#;
Figure 959, Use sort-char function
WITH temp1 (num,ts1,ts2) AS
(VALUES (INT(1)
,TIMESTAMP(GENERATE_UNIQUE())
,TIMESTAMP(GENERATE_UNIQUE()))
UNION ALL
SELECT num + 1
,ts1
,TIMESTAMP(GENERATE_UNIQUE())
FROM temp1
WHERE TIMESTAMPDIFF(2,CHAR(ts2-ts1)) < 4
)
SELECT MAX(num) AS #loops
,MIN(ts2) AS bgn_timestamp
,MAX(ts2) AS end_timestamp
FROM temp1;
ANSWER
============================================================
#LOOPS BGN_TIMESTAMP END_TIMESTAMP
------ -------------------------- --------------------------
58327 2001-08-09-22.58.12.754579 2001-08-09-22.58.16.754634
Figure 960, Run query for four seconds
CREATE FUNCTION pause(inval INT)
RETURNS INTEGER
NOT DETERMINISTIC
EXTERNAL ACTION
RETURN
WITH ttt (num, strt, stop) AS
(VALUES (1
,TIMESTAMP(GENERATE_UNIQUE())
,TIMESTAMP(GENERATE_UNIQUE()))
UNION ALL
SELECT num + 1
,strt
,TIMESTAMP(GENERATE_UNIQUE())
FROM ttt
WHERE TIMESTAMPDIFF(2,CHAR(stop - strt)) < inval
)
SELECT MAX(num)
FROM ttt;
Figure 961, Function that pauses for "n" seconds
SELECT id
,SUBSTR(CHAR(TIMESTAMP(GENERATE_UNIQUE())),18) AS ss_mmmmmm
,pause(id / 10) AS #loops
,SUBSTR(CHAR(TIMESTAMP(GENERATE_UNIQUE())),18) AS ss_mmmmmm
FROM staff
WHERE id < 31;
ANSWER
=============================
ID SS_MMMMMM #LOOPS SS_MMMMMM
-- --------- ------ ---------
10 50.068593 76386 50.068587
20 52.068744 144089 52.068737
30 55.068930 206101 55.068923
Figure 962, Query that uses pause function
WITH numbered_rows AS
(SELECT s.*
,ROW_NUMBER() OVER(PARTITION BY job
ORDER BY salary, id) AS row#
FROM staff s
WHERE comm > 0
AND name LIKE '%e%'),
median_row_num AS
(SELECT job
,(MAX(row# + 1.0) / 2) - 0.5 AS med_lo
,(MAX(row# + 1.0) / 2) + 0.5 AS med_hi
FROM numbered_rows
GROUP BY job)
SELECT nn.job
,DEC(AVG(nn.salary),7,2) AS med_sal
FROM numbered_rows nn ANSWER
,median_row_num mr ==============
WHERE nn.job = mr.job JOB MED_SAL
AND nn.row# BETWEEN mr.med_lo AND mr.med_hi ----- --------
GROUP BY nn.job Clerk 13030.50
ORDER BY nn.job; Sales 17432.10
Figure 963, Calculating the median
WITH numbered_rows AS
(SELECT s.*
,ROW_NUMBER() OVER(PARTITION BY job
ORDER BY salary, id) AS row#
FROM staff s
WHERE comm > 0
AND name LIKE '%e%'),
median_row_num AS
(SELECT job
,(MAX(row# + 1.0) / 2) - 0.5 AS med_lo
,(MAX(row# + 1.0) / 2) + 0.5 AS med_hi
,DEC(AVG(salary),7,2) AS avg_sal
,COUNT(*) AS #rows
FROM numbered_rows
GROUP BY job)
SELECT nn.job
,DEC(AVG(nn.salary),7,2) AS med_sal
,MAX(mr.avg_sal) AS avg_sal
,MAX(mr.#rows) AS #r
FROM numbered_rows nn
,median_row_num mr ANSWER
WHERE nn.job = mr.job ==========================
AND nn.row# BETWEEN mr.med_lo JOB MED_SAL AVG_SAL #R
AND mr.med_hi ----- -------- -------- --
GROUP BY nn.job Clerk 13030.50 12857.56 7
ORDER BY nn.job; Sales 17432.10 17460.93 4
Figure 964, Get median plus average
WITH numbered_rows AS
(SELECT s.*
,ROW_NUMBER() OVER(PARTITION BY job
ORDER BY salary, id) AS row#
FROM staff s
WHERE comm > 0
AND name LIKE '%e%'),
median_row_num AS
(SELECT job
,MAX(row# + 1) / 2 AS med_row#
FROM numbered_rows
GROUP BY job)
SELECT nn.job
,nn.salary AS med_sal ANSWER
FROM numbered_rows nn ==============
,median_row_num mr JOB MED_SAL
WHERE nn.job = mr.job ----- --------
AND nn.row# = mr.med_row# Clerk 13030.50
ORDER BY nn.job; Sales 16858.20
Figure 965, Calculating the median
WITH numbered_rows AS
(SELECT s.*
,ROW_NUMBER() OVER(PARTITION BY job
ORDER BY salary, id) AS row#
FROM staff s
WHERE comm > 0
AND name LIKE '%e%')
SELECT job
,salary AS med_sal
FROM numbered_rows
WHERE (job,row#) IN ANSWER
(SELECT job ==============
,MAX(row# + 1) / 2 JOB MED_SAL
FROM numbered_rows ----- --------
GROUP BY job) Clerk 13030.50
ORDER BY job; Sales 16858.20
Figure 966, Calculating the median
WITH numbered_rows AS
(SELECT s.*
,ROW_NUMBER() OVER(PARTITION BY job
ORDER BY salary, id) AS row#
FROM staff s
WHERE comm > 0
AND name LIKE '%e%')
SELECT r1.*
,(SELECT r2.salary
FROM numbered_rows r2
WHERE r2.job = r1.job
AND r2.row# = (SELECT MAX(r3.row# + 1) / 2
FROM numbered_rows r3
WHERE r2.job = r3.job)) AS med_sal
FROM numbered_rows r1
ORDER BY job
,salary;
Figure 967, List matching rows and median
WITH temp1 (c1,t1,t2) AS (VALUES ANSWER
('A' =========
,TIMESTAMP('1996-05-01-24.00.00.000000')
,TIMESTAMP('1996-05-02-00.00.00.000000') ))
SELECT c1
FROM temp1
WHERE t1 = t2;
Figure 968, Timestamp comparison - Incorrect
WITH temp1 (c1,t1,t2) AS (VALUES ANSWER
('A' ======
,TIMESTAMP('1996-05-01-24.00.00.000000') C1
,TIMESTAMP('1996-05-02-00.00.00.000000') )) --
SELECT c1 A
FROM temp1
WHERE t1 + 0 MICROSECOND = t2 + 0 MICROSECOND;
Figure 969, Timestamp comparison - Correct
CREATE TABLE supermarket_sales
(sales_ts TIMESTAMP NOT NULL
,sales_val DECIMAL(8,2) NOT NULL
,PRIMARY KEY(sales_ts));
Figure 970, Sample Table
INSERT INTO supermarket_sales VALUES
('2003-08-01-24.00.00.000000',123.45);
Figure 971, Insert row
SELECT *
FROM supermarket_sales
WHERE DATE(sales_ts) = '2003-08-01'
ORDER BY sales_ts;
Figure 972, Select rows for given date
SELECT *
FROM supermarket_sales
WHERE sales_ts BETWEEN '2003-08-01-00.00.00'
AND '2003-08-01-24.00.00'
ORDER BY sales_ts;
Figure 973, Select rows for given date
SELECT creator ANSWER
FROM sysibm.systables ========
WHERE creator = 'ZZZ';
Figure 974, Query with no matching rows (1 of 8)
SELECT MAX(creator) ANSWER
FROM sysibm.systables ======
WHERE creator = 'ZZZ';
Figure 975, Query with no matching rows (2 of 8)
SELECT MAX(creator) ANSWER
FROM sysibm.systables ========
WHERE creator = 'ZZZ'
HAVING MAX(creator) IS NOT NULL;
Figure 976, Query with no matching rows (3 of 8)
SELECT MAX(creator) ANSWER
FROM sysibm.systables ========
WHERE creator = 'ZZZ'
HAVING MAX(creator) = 'ZZZ';
Figure 977, Query with no matching rows (4 of 8)
SELECT MAX(creator) ANSWER
FROM sysibm.systables ========
WHERE creator = 'ZZZ'
GROUP BY creator;
Figure 978, Query with no matching rows (5 of 8)
SELECT creator ANSWER
FROM sysibm.systables ========
WHERE creator = 'ZZZ'
GROUP BY creator;
Figure 979, Query with no matching rows (6 of 8)
SELECT COUNT(*) ANSWER
FROM sysibm.systables ========
WHERE creator = 'ZZZ'
GROUP BY creator;
Figure 980, Query with no matching rows (7 of 8)
SELECT COUNT(*) ANSWER
FROM sysibm.systables ======
WHERE creator = 'ZZZ'; 0
Figure 981, Query with no matching rows (8 of 8)
SELECT COALESCE(name,noname) AS nme ANSWER
,COALESCE(salary,nosal) AS sal ============
FROM (SELECT 'NO NAME' AS noname NME SAL
,0 AS nosal ------- ----
FROM sysibm.sysdummy1 NO NAME 0.00
)AS nnn
LEFT OUTER JOIN
(SELECT *
FROM staff
WHERE id < 5
)AS xxx
ON 1 = 1
ORDER BY name;
Figure 982, Always get a row, example 1 of 2
WITH nnn (noname, nosal) AS ANSWER
(VALUES ('NO NAME',0)) ============
SELECT COALESCE(name,noname) AS nme NME SAL
,COALESCE(salary,nosal) AS sal ------- ----
FROM nnn NO NAME 0.00
LEFT OUTER JOIN
(SELECT *
FROM staff
WHERE id < 5
)AS xxx
ON 1 = 1
ORDER BY NAME;
Figure 983, Always get a row, example 2 of 2
SELECT DATE('2001-09-22') ANSWER
FROM sysibm.sysdummy1; ==========
2001-09-22
Figure 984, Convert value to DB2 date, right
SELECT DATE(2001-09-22) ANSWER
FROM sysibm.sysdummy1; ==========
0006-05-24
Figure 985, Convert value to DB2 date, wrong
SELECT id ANSWER
,name ===========
FROM staff ID NAME
WHERE id <= 100 -- --------
AND id = (INT(RAND()* 10) * 10) + 10 30 Marenghi
ORDER BY id; 60 Quigley
Figure 986, Get random rows - Incorrect
WITH temp AS ANSWER
(SELECT id ====================
,name ID NAME RAN EQL
,(INT(RAND(0)* 10) * 10) + 10 AS ran --- -------- --- ---
FROM staff 10 Sanders 10 Y
WHERE id <= 100 20 Pernal 30
) 30 Marenghi 70
SELECT t.* 40 O'Brien 10
,CASE id 50 Hanes 30
WHEN ran THEN 'Y' 60 Quigley 40
ELSE ' ' 70 Rothman 30
END AS eql 80 James 100
FROM temp t 90 Koonitz 40
ORDER BY id; 100 Plotz 100 Y
Figure 987, Get random rows - Explanation
WITH ANSWER
staff_numbered AS ===========
(SELECT s.* ID NAME
,ROW_NUMBER() OVER() AS row# --- -------
FROM staff s 10 Sanders
WHERE id <= 100 20 Pernal
), 90 Koonitz
count_rows AS
(SELECT MAX(row#) AS #rows
FROM staff_numbered
),
random_values (RAN#) AS
(VALUES (RAND())
,(RAND())
,(RAND())
),
rows_t0_get AS
(SELECT INT(ran# * #rows) + 1 AS get_row
FROM random_values
,count_rows
)
SELECT id
,name
FROM staff_numbered
,rows_t0_get
WHERE row# = get_row
ORDER BY id;
Figure 988, Get random rows - Non-distinct
SELECT id ANSWER
,name ===========
FROM (SELECT s.* ID NAME
,ROW_NUMBER() OVER(ORDER BY RAND()) AS r -- --------
FROM staff s 10 Sanders
WHERE id <= 100 40 O'Brien
)AS xxx 60 Quigley
WHERE r <= 3
ORDER BY id;
Figure 989, Get random rows - Distinct
WITH temp1 (bgn_tstamp, elp_sec) AS
(VALUES (TIMESTAMP('2001-01-15-01.02.03.000000'), 1.234)
,(TIMESTAMP('2001-01-15-01.02.03.123456'), 1.234)
)
SELECT bgn_tstamp
,elp_sec
,bgn_tstamp + elp_sec SECONDS AS end_tstamp
FROM temp1;
ANSWER
======
BGN_TSTAMP ELP_SEC END_TSTAMP
-------------------------- ------- --------------------------
2001-01-15-01.02.03.000000 1.234 2001-01-15-01.02.04.000000
2001-01-15-01.02.03.123456 1.234 2001-01-15-01.02.04.123456
Figure 990, Date/Time manipulation - wrong
WITH temp1 (bgn_tstamp, elp_sec) AS
(VALUES (TIMESTAMP('2001-01-15-01.02.03.000000'), 1.234)
,(TIMESTAMP('2001-01-15-01.02.03.123456'), 1.234)
)
SELECT bgn_tstamp
,elp_sec
,bgn_tstamp + (elp_sec *1E6) MICROSECONDS AS end_tstamp
FROM temp1;
ANSWER
======
BGN_TSTAMP ELP_SEC END_TSTAMP
-------------------------- ------- --------------------------
2001-01-15-01.02.03.000000 1.234 2001-01-15-01.02.04.234000
2001-01-15-01.02.03.123456 1.234 2001-01-15-01.02.04.357456
Figure 991, Date/Time manipulation - right
WITH temp1 (c0,c1,v1) AS (VALUES ANSWER
('A',CHAR(' ',1),VARCHAR(' ',1)), ======
('B',CHAR(' ',1),VARCHAR('' ,1))) C0
SELECT c0 --
FROM temp1 A
WHERE c1 = v1 B
AND c1 LIKE ' ';
Figure 992, Use LIKE on CHAR field
WITH temp1 (c0,c1,v1) AS (VALUES ANSWER
('A',CHAR(' ',1),VARCHAR(' ',1)), ======
('B',CHAR(' ',1),VARCHAR('' ,1))) C0
SELECT c0 --
FROM temp1 A
WHERE c1 = v1
AND v1 LIKE ' ';
Figure 993, Use LIKE on VARCHAR field
WITH temp1 (c0,c1,v1) AS (VALUES ANSWER
('A',CHAR(' ',1),VARCHAR(' ',1)), ======
('B',CHAR(' ',1),VARCHAR('' ,1))) C0
SELECT c0 --
FROM temp1 A
WHERE c1 = v1 B
AND RTRIM(v1) LIKE '';
Figure 994, Use RTRIM to remove trailing blanks
WITH temp1 (yymmdd) AS ANSWER
(VALUES DATE('2000-01-01') ==========================
UNION ALL YEAR MIN_DT MAX_DT
SELECT yymmdd + 1 DAY ---- ---------- ----------
FROM temp1 2000 2000-08-06 2000-08-12
WHERE yymmdd < '2010-12-31' 2001 2001-08-12 2001-08-18
) 2002 2002-08-11 2002-08-17
SELECT yy AS year 2003 2003-08-10 2003-08-16
,CHAR(MIN(yymmdd),ISO) AS min_dt 2004 2004-08-08 2004-08-14
,CHAR(MAX(yymmdd),ISO) AS max_dt 2005 2005-08-07 2005-08-13
FROM (SELECT yymmdd 2006 2006-08-13 2006-08-19
,YEAR(yymmdd) yy 2007 2007-08-12 2007-08-18
,WEEK(yymmdd) wk 2008 2008-08-10 2008-08-16
FROM temp1 2009 2009-08-09 2009-08-15
WHERE WEEK(yymmdd) = 33 2010 2010-08-08 2010-08-14
)AS xxx
GROUP BY yy
,wk;
Figure 995, Comparing week 33 over 10 years
SELECT SUM(INTEGER(salary)) AS s1 ANSWER
,INTEGER(SUM(salary)) AS s2 =============
FROM staff; S1 S2
------ ------
583633 583647
Figure 996, DB2 data truncation
SELECT SUM(INTEGER(ROUND(salary,-1))) AS s1 ANSWER
,INTEGER(SUM(salary)) AS s2 =============
FROM staff; S1 S2
------ ------
583640 583647
Figure 997, DB2 data rounding
SELECT lastname ANSWER
,sex =================
,CASE LASTNAME SX SXX
WHEN sex >= 'F' THEN 'FEM' ---------- -- ---
WHEN sex >= 'M' THEN 'MAL' JEFFERSON M FEM
END AS sxx JOHNSON F FEM
FROM employee JONES M FEM
WHERE lastname LIKE 'J%'
ORDER BY 1;
Figure 998, Case WHEN Processing - Incorrect
SELECT lastname ANSWER
,sex =================
,CASE LASTNAME SX SXX
WHEN sex >= 'M' THEN 'MAL' ---------- -- ---
WHEN sex >= 'F' THEN 'FEM' JEFFERSON M MAL
END AS sxx JOHNSON F FEM
FROM employee JONES M MAL
WHERE lastname LIKE 'J%'
ORDER BY 1;
Figure 999, Case WHEN Processing - Correct
SELECT AVG(salary) / AVG(comm) AS a1 ANSWER >>> A1 A2
,AVG(salary / comm) AS a2 -- -----
FROM staff; 32 61.98
Figure 1000, Division and Average
SELECT hiredate ANSWER
FROM employee ==========
WHERE hiredate < '1960-01-01' 1947-05-05
ORDER BY 1; 1949-08-17
1958-05-16
Figure 1001, DATE output in year, month, day order
SELECT CHAR(hiredate,USA) ANSWER
FROM employee ==========
WHERE hiredate < '1960-01-01' 05/05/1947
ORDER BY 1; 05/16/1958
08/17/1949
Figure 1002, DATE output in month, day, year order
EXEC-SQL
DECLARE fred CURSOR FOR
SELECT *
FROM staff
WHERE id < 1000
ORDER BY id;
END-EXEC;
EXEC-SQL
OPEN fred
END-EXEC;
DO UNTIL SQLCODE = 100;
EXEC-SQL
FETCH fred
INTO :HOST-VARS
END-EXEC;
IF SQLCODE <> 100 THEN DO;
SET HOST-VAR.ID = HOST-VAR.ID + 500;
EXEC-SQL
INSERT INTO staff VALUES (:HOST-VARS)
END-EXEC;
END-DO;
END-DO;
EXEC-SQL
CLOSE fred
END-EXEC;
Figure 1003, Ambiguous Cursor
WITH temp (f1) AS
(VALUES FLOAT(1.23456789)
UNION ALL
SELECT f1 * 10
FROM temp
WHERE f1 < 1E18
)
SELECT f1 AS float1
,DEC(f1,19) AS decimal1
,BIGINT(f1) AS bigint1
FROM temp;
Figure 1004, Multiply floating-point number by ten, SQL
FLOAT1 DECIMAL1 BIGINT1
------------------------ -------------------- -------------------
+1.23456789000000E+000 1. 1
+1.23456789000000E+001 12. 12
+1.23456789000000E+002 123. 123
+1.23456789000000E+003 1234. 1234
+1.23456789000000E+004 12345. 12345
+1.23456789000000E+005 123456. 123456
+1.23456789000000E+006 1234567. 1234567
+1.23456789000000E+007 12345678. 12345678
+1.23456789000000E+008 123456789. 123456788
+1.23456789000000E+009 1234567890. 1234567889
+1.23456789000000E+010 12345678900. 12345678899
+1.23456789000000E+011 123456789000. 123456788999
+1.23456789000000E+012 1234567890000. 1234567889999
+1.23456789000000E+013 12345678900000. 12345678899999
+1.23456789000000E+014 123456789000000. 123456788999999
+1.23456789000000E+015 1234567890000000. 1234567889999999
+1.23456789000000E+016 12345678900000000. 12345678899999998
+1.23456789000000E+017 123456789000000000. 123456788999999984
+1.23456789000000E+018 1234567890000000000. 1234567889999999744
Figure 1005, Multiply floating-point number by ten, answer
WITH temp (f1,f2) AS
(VALUES (FLOAT(1.23456789E1 * 10 * 10 * 10 * 10 * 10 * 10 * 10)
,FLOAT(1.23456789E8)))
SELECT f1
,f2
FROM temp ANSWER
WHERE f1 <> f2; =============================================
F1 F2
---------------------- ----------------------
+1.23456789000000E+008 +1.23456789000000E+008
Figure 1006, Two numbers that look equal, but aren't equal
WITH temp (f1,f2) AS
(VALUES (FLOAT(1.23456789E1 * 10 * 10 * 10 * 10 * 10 * 10 * 10)
,FLOAT(1.23456789E8)))
SELECT HEX(f1) AS hex_f1
,HEX(f2) AS hex_f2
FROM temp ANSWER
WHERE f1 <> f2; =================================
HEX_F1 HEX_F2
---------------- ----------------
FFFFFF53346F9D41 00000054346F9D41
Figure 1007, Two numbers that look equal, but aren't equal, shown in HEX
WITH
temp1 (dec1, dbl1) AS
(VALUES (DECIMAL(1),DOUBLE(1)))
,temp2 (dec1, dec2, dbl1, dbl2) AS
(SELECT dec1
,dec1 / 3 AS dec2
,dbl1 ANSWER (1 row returned)
,dbl1 / 3 AS dbl2 ==============================
FROM temp1) DEC1 = 1.0
SELECT * DEC2 = 0.33333333333333333333
FROM temp2 DBL1 = +1.00000000000000E+000
WHERE dbl2 <> dec2; DBL2 = +3.33333333333333E-001
Figure 1008, Comparing float and decimal division
WITH temp (f1, d1) AS
(VALUES (FLOAT(1.23456789)
,DEC(1.23456789,20,10))
UNION ALL
SELECT f1 * 10
,d1 * 10
FROM temp
WHERE f1 < 1E9
)
SELECT f1
,d1
,CASE
WHEN d1 = f1 THEN 'SAME'
ELSE 'DIFF'
END AS compare
FROM temp;
Figure 1009, Comparing float and decimal multiplication, SQL
F1 D1 COMPARE
---------------------- --------------------- -------
+1.23456789000000E+000 1.2345678900 SAME
+1.23456789000000E+001 12.3456789000 SAME
+1.23456789000000E+002 123.4567890000 DIFF
+1.23456789000000E+003 1234.5678900000 DIFF
+1.23456789000000E+004 12345.6789000000 DIFF
+1.23456789000000E+005 123456.7890000000 DIFF
+1.23456789000000E+006 1234567.8900000000 SAME
+1.23456789000000E+007 12345678.9000000000 DIFF
+1.23456789000000E+008 123456789.0000000000 DIFF
+1.23456789000000E+009 1234567890.0000000000 DIFF
Figure 1010, Comparing float and decimal multiplication, answer
WITH temp (f1) AS ANSWER
(VALUES FLOAT(0.1)) =======================================
SELECT f1 F1 HEX_F1
,HEX(f1) AS hex_f1 ---------------------- ----------------
FROM temp; +1.00000000000000E-001 9A9999999999B93F
Figure 1011, Internal representation of "one tenth" in floating-point