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   &apos;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&apos;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