Appendix B: DB2 SQL Quick Reference
This appendix provides a compact reference for the SQL dialect supported by IBM DB2 on both z/OS and LUW platforms. It is not a substitute for the IBM Knowledge Center documentation, but it covers the statements and functions you will reach for daily. Platform-specific differences are noted where they matter.
B.1 Data Retrieval --- SELECT
B.1.1 Full SELECT Syntax
WITH cte_name (col1, col2, ...) AS (
select-statement
)
SELECT [ALL | DISTINCT]
column_expression [AS alias], ...
FROM table_reference
[JOIN table_reference ON condition] ...
[WHERE search_condition]
[GROUP BY grouping_expression, ...]
[HAVING search_condition]
[ORDER BY sort_expression [ASC | DESC] [NULLS FIRST | NULLS LAST], ...]
[FETCH FIRST n ROWS ONLY]
[FOR READ ONLY | FOR UPDATE OF col1, col2, ...]
[WITH UR | CS | RS | RR]
[OPTIMIZE FOR n ROWS]
;
B.1.2 Join Types
-- Inner join
SELECT ... FROM T1 INNER JOIN T2 ON T1.key = T2.key;
-- Left outer join (preserves all rows from T1)
SELECT ... FROM T1 LEFT OUTER JOIN T2 ON T1.key = T2.key;
-- Right outer join
SELECT ... FROM T1 RIGHT OUTER JOIN T2 ON T1.key = T2.key;
-- Full outer join
SELECT ... FROM T1 FULL OUTER JOIN T2 ON T1.key = T2.key;
-- Cross join (Cartesian product)
SELECT ... FROM T1 CROSS JOIN T2;
-- Lateral join (correlated table function, LUW and z/OS v12+)
SELECT ... FROM T1, LATERAL (SELECT ... FROM T2 WHERE T2.fk = T1.pk) AS L;
B.1.3 Subqueries
-- Scalar subquery
SELECT EMPNO, (SELECT DEPTNAME FROM DEPT WHERE DEPT.DEPTNO = EMP.WORKDEPT) AS DNAME
FROM EMP;
-- EXISTS
SELECT * FROM DEPT D WHERE EXISTS (SELECT 1 FROM EMP E WHERE E.WORKDEPT = D.DEPTNO);
-- IN subquery
SELECT * FROM EMP WHERE WORKDEPT IN (SELECT DEPTNO FROM DEPT WHERE LOC = 'NYC');
-- Quantified comparison
SELECT * FROM EMP WHERE SALARY > ALL (SELECT SALARY FROM EMP WHERE WORKDEPT = 'D01');
B.1.4 Common Table Expressions (CTEs)
WITH DEPT_AVG AS (
SELECT WORKDEPT, AVG(SALARY) AS AVG_SAL
FROM EMP
GROUP BY WORKDEPT
)
SELECT E.EMPNO, E.SALARY, D.AVG_SAL
FROM EMP E
JOIN DEPT_AVG D ON E.WORKDEPT = D.WORKDEPT
WHERE E.SALARY > D.AVG_SAL;
Recursive CTE:
WITH ORG_TREE (EMPNO, MGRNO, LVL) AS (
SELECT EMPNO, MGRNO, 0
FROM EMP WHERE MGRNO IS NULL
UNION ALL
SELECT E.EMPNO, E.MGRNO, O.LVL + 1
FROM EMP E JOIN ORG_TREE O ON E.MGRNO = O.EMPNO
)
SELECT * FROM ORG_TREE;
B.1.5 OLAP / Window Functions
SELECT EMPNO, WORKDEPT, SALARY,
ROW_NUMBER() OVER (ORDER BY SALARY DESC) AS RN,
RANK() OVER (PARTITION BY WORKDEPT ORDER BY SALARY DESC) AS DEPT_RANK,
SUM(SALARY) OVER (PARTITION BY WORKDEPT) AS DEPT_TOTAL,
AVG(SALARY) OVER (ORDER BY HIREDATE ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS MOVING_AVG
FROM EMP;
Supported window functions: ROW_NUMBER, RANK, DENSE_RANK, NTILE, LAG, LEAD, FIRST_VALUE, LAST_VALUE, NTH_VALUE, SUM, AVG, MIN, MAX, COUNT (all as windowed aggregates).
B.2 Data Modification
B.2.1 INSERT
-- Single row
INSERT INTO EMP (EMPNO, FIRSTNAME, LASTNAME, WORKDEPT, SALARY)
VALUES ('000999', 'JANE', 'DOE', 'D01', 65000.00);
-- Multi-row (LUW; z/OS v12+)
INSERT INTO EMP (EMPNO, FIRSTNAME, LASTNAME)
VALUES ('001001', 'A', 'ALPHA'),
('001002', 'B', 'BRAVO'),
('001003', 'C', 'CHARLIE');
-- INSERT from SELECT
INSERT INTO EMP_ARCHIVE
SELECT * FROM EMP WHERE HIREDATE < '2000-01-01';
-- INSERT with identity column
INSERT INTO ORDER_LOG (ORDER_DESC) VALUES ('New order');
-- IDENTITY_VAL_LOCAL() returns the generated value
B.2.2 UPDATE
-- Simple update
UPDATE EMP SET SALARY = SALARY * 1.05 WHERE WORKDEPT = 'D01';
-- Correlated update
UPDATE EMP E SET SALARY = (
SELECT AVG(SALARY) FROM EMP WHERE WORKDEPT = E.WORKDEPT
)
WHERE EMPNO = '000100';
-- Update with row limit (LUW)
UPDATE EMP SET BONUS = 1000 WHERE BONUS = 0 FETCH FIRST 100 ROWS ONLY;
B.2.3 DELETE
-- Conditional delete
DELETE FROM EMP WHERE HIREDATE < '1990-01-01';
-- Delete all rows (use TRUNCATE for better performance if no rollback needed)
DELETE FROM TEMP_TABLE;
-- Correlated delete
DELETE FROM EMP WHERE NOT EXISTS (
SELECT 1 FROM DEPT WHERE DEPT.DEPTNO = EMP.WORKDEPT
);
B.2.4 MERGE (Upsert)
MERGE INTO TARGET_TABLE T
USING SOURCE_TABLE S
ON T.KEY_COL = S.KEY_COL
WHEN MATCHED THEN
UPDATE SET T.VAL = S.VAL, T.UPDATED = CURRENT TIMESTAMP
WHEN NOT MATCHED THEN
INSERT (KEY_COL, VAL, UPDATED)
VALUES (S.KEY_COL, S.VAL, CURRENT TIMESTAMP);
B.2.5 TRUNCATE (LUW; z/OS v12+)
TRUNCATE TABLE TEMP_TABLE IMMEDIATE;
-- Much faster than DELETE; does not fire triggers; not logged row-by-row
-- On z/OS: TRUNCATE TABLE TEMP_TABLE IMMEDIATE REUSE STORAGE;
B.3 Data Definition
B.3.1 CREATE TABLE
CREATE TABLE ORDERS (
ORDER_ID INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1),
CUSTOMER_ID INTEGER NOT NULL,
ORDER_DATE DATE NOT NULL WITH DEFAULT CURRENT DATE,
TOTAL_AMT DECIMAL(11,2),
STATUS CHAR(1) NOT NULL DEFAULT 'N',
DESCRIPTION VARCHAR(500),
CONSTRAINT PK_ORDERS PRIMARY KEY (ORDER_ID),
CONSTRAINT FK_CUST FOREIGN KEY (CUSTOMER_ID) REFERENCES CUSTOMERS (CUSTOMER_ID),
CONSTRAINT CHK_STATUS CHECK (STATUS IN ('N', 'P', 'C', 'X'))
)
IN USERSPACE1; -- LUW: table space
-- z/OS: IN DATABASE DSNDB04.ORDERTS
Temporal table (z/OS v10+, LUW v10.1+):
CREATE TABLE POLICY (
POLICY_ID INTEGER NOT NULL,
HOLDER_NAME VARCHAR(100),
SYS_START TIMESTAMP(12) NOT NULL GENERATED ALWAYS AS ROW BEGIN,
SYS_END TIMESTAMP(12) NOT NULL GENERATED ALWAYS AS ROW END,
TX_ID TIMESTAMP(12) GENERATED ALWAYS AS TRANSACTION START ID,
PERIOD SYSTEM_TIME (SYS_START, SYS_END),
PRIMARY KEY (POLICY_ID)
);
CREATE TABLE POLICY_HIST LIKE POLICY;
ALTER TABLE POLICY ADD VERSIONING USE HISTORY TABLE POLICY_HIST;
B.3.2 ALTER TABLE
-- Add a column
ALTER TABLE ORDERS ADD COLUMN SHIP_DATE DATE;
-- Alter column data type (LUW -- limitations apply)
ALTER TABLE ORDERS ALTER COLUMN DESCRIPTION SET DATA TYPE VARCHAR(1000);
-- Add constraint
ALTER TABLE ORDERS ADD CONSTRAINT CHK_AMT CHECK (TOTAL_AMT >= 0);
-- Drop constraint
ALTER TABLE ORDERS DROP CONSTRAINT CHK_AMT;
-- z/OS: Add a partition
ALTER TABLE ORDERS ALTER PARTITION 5 ENDING AT ('2026-12-31') INCLUSIVE;
B.3.3 CREATE INDEX
-- Standard index
CREATE INDEX IX_ORDERS_CUST ON ORDERS (CUSTOMER_ID);
-- Unique index
CREATE UNIQUE INDEX IX_ORDERS_PK ON ORDERS (ORDER_ID);
-- Composite index with included columns (LUW)
CREATE INDEX IX_ORDERS_DATE ON ORDERS (ORDER_DATE DESC)
INCLUDE (CUSTOMER_ID, TOTAL_AMT);
-- z/OS: Index with CLUSTER attribute
CREATE INDEX IX_ORDERS_CL ON ORDERS (ORDER_DATE)
CLUSTER;
-- Expression-based index (LUW 11.1+)
CREATE INDEX IX_UPPER_NAME ON CUSTOMERS (UPPER(LAST_NAME));
B.4 Built-in Functions
B.4.1 String Functions
| Function | Description | Example |
|---|---|---|
LENGTH(s) |
Length in bytes | LENGTH('DB2') = 3 |
CHARACTER_LENGTH(s) |
Length in characters | handles multi-byte |
SUBSTR(s, start, len) |
Substring | SUBSTR('DB2', 1, 2) = 'DB' |
UPPER(s) / LOWER(s) |
Case conversion | UPPER('db2') = 'DB2' |
TRIM(s) |
Remove leading/trailing blanks | TRIM(' DB2 ') = 'DB2' |
LTRIM(s) / RTRIM(s) |
One-sided trim | |
REPLACE(s, from, to) |
String substitution | REPLACE('A-B', '-', '_') = 'A_B' |
LOCATE(search, s) |
Position of substring | LOCATE('2', 'DB2') = 3 |
CONCAT(s1, s2) or s1 \|\| s2 |
Concatenation | 'DB' \|\| '2' = 'DB2' |
LEFT(s, n) / RIGHT(s, n) |
Leftmost/rightmost chars | LEFT('DB2', 2) = 'DB' |
LPAD(s, n, pad) / RPAD(s, n, pad) |
Pad to length | LPAD('42', 5, '0') = '00042' |
STRIP(s, B\|L\|T, char) |
Strip specific character | z/OS syntax variant |
REGEXP_LIKE(s, pattern) |
Regex match (LUW 11.1+) | REGEXP_LIKE(NAME, '^[A-Z]') |
B.4.2 Numeric Functions
| Function | Description |
|---|---|
ABS(n) |
Absolute value |
MOD(n, m) |
Remainder of n/m |
ROUND(n, d) |
Round to d decimal places |
TRUNC(n, d) / TRUNCATE(n, d) |
Truncate to d decimal places |
CEIL(n) / CEILING(n) |
Smallest integer >= n |
FLOOR(n) |
Largest integer <= n |
POWER(n, e) |
n raised to power e |
SQRT(n) |
Square root |
LN(n) / LOG10(n) |
Natural log / base-10 log |
RAND() |
Random double between 0 and 1 |
SIGN(n) |
-1, 0, or 1 |
DECIMAL(n, p, s) |
Cast to decimal |
B.4.3 Date and Time Functions
| Function | Description |
|---|---|
CURRENT DATE / CURRENT_DATE |
Today's date |
CURRENT TIME |
Current time |
CURRENT TIMESTAMP |
Current timestamp |
DATE(expr) |
Convert to DATE |
TIME(expr) |
Convert to TIME |
TIMESTAMP(expr) |
Convert to TIMESTAMP |
YEAR(d), MONTH(d), DAY(d) |
Extract components |
HOUR(t), MINUTE(t), SECOND(t) |
Time components |
DAYOFWEEK(d) |
1=Sunday through 7=Saturday |
DAYOFYEAR(d) |
Day number in year (1-366) |
DAYS(d) |
Integer number of days since 0001-01-01 |
TIMESTAMPDIFF(unit, expr) |
Difference between timestamps |
d + n DAYS / d + n MONTHS |
Date arithmetic |
ADD_MONTHS(d, n) |
LUW: add months |
LAST_DAY(d) |
Last day of the month |
B.4.4 Conversion and Casting Functions
| Function | Description |
|---|---|
CAST(expr AS type) |
Standard SQL cast |
INTEGER(expr) |
Cast to integer |
DECIMAL(expr, p, s) |
Cast to decimal |
CHAR(expr) |
Cast to fixed-length string |
VARCHAR(expr) |
Cast to variable-length string |
VARCHAR_FORMAT(ts, fmt) |
Format timestamp as string |
TIMESTAMP_FORMAT(s, fmt) |
Parse string as timestamp |
HEX(expr) |
Hexadecimal representation |
COALESCE(e1, e2, ...) |
First non-null value |
NULLIF(e1, e2) |
NULL if e1 = e2 |
CASE WHEN ... THEN ... END |
Conditional expression |
DECODE(expr, s1, r1, ...) |
Oracle-style decode (LUW compat) |
B.5 DB2-Specific SQL Extensions
B.5.1 Isolation Levels in the Query
DB2 allows specifying isolation at the statement level---a feature uncommon in other RDBMS:
SELECT * FROM ORDERS WITH UR; -- Uncommitted Read (dirty read)
SELECT * FROM ORDERS WITH CS; -- Cursor Stability (default)
SELECT * FROM ORDERS WITH RS; -- Read Stability
SELECT * FROM ORDERS WITH RR; -- Repeatable Read (serializable)
B.5.2 OPTIMIZE FOR n ROWS
Hints the optimizer that only n rows will be fetched, favoring nested-loop index access over table scans:
SELECT * FROM ORDERS WHERE CUSTOMER_ID = 100
ORDER BY ORDER_DATE DESC
FETCH FIRST 10 ROWS ONLY
OPTIMIZE FOR 10 ROWS;
B.5.3 Special Registers
| Register | Description |
|---|---|
CURRENT SCHEMA |
Default schema for unqualified names |
CURRENT PATH |
Function resolution path |
CURRENT SQLID |
z/OS: current SQL authorization ID |
CURRENT TIMESTAMP |
Current timestamp |
CURRENT DEGREE |
Degree of parallelism |
CURRENT ISOLATION |
Current isolation level |
CURRENT MEMBER |
z/OS data sharing: current member |
CURRENT APPLICATION ENCODING SCHEME |
z/OS: EBCDIC/ASCII/Unicode |
B.5.4 SELECT FROM INSERT / UPDATE / DELETE
DB2 supports selecting from modification statements:
SELECT ORDER_ID FROM FINAL TABLE (
INSERT INTO ORDERS (CUSTOMER_ID, ORDER_DATE) VALUES (100, CURRENT DATE)
);
This returns the generated ORDER_ID without a separate query.
B.5.5 XMLTABLE and JSON Functions
-- XMLTABLE
SELECT T.* FROM XML_DOC D,
XMLTABLE('$doc/orders/order' PASSING D.XML_COL AS "doc"
COLUMNS
ORDER_ID INTEGER PATH '@id',
AMOUNT DECIMAL(11,2) PATH 'amount'
) AS T;
-- JSON_VALUE (Db2 LUW 11.1+)
SELECT JSON_VALUE(JSON_COL, '$.customer.name') FROM ORDERS;
B.6 Common SQLCODE and SQLSTATE Values
B.6.1 Successful Completion
| SQLCODE | SQLSTATE | Meaning |
|---|---|---|
| 0 | 00000 | Successful execution |
| +100 | 02000 | No row found / end of cursor |
B.6.2 Warning Codes (Positive)
| SQLCODE | SQLSTATE | Meaning |
|---|---|---|
| +222 | 01594 | Fetch after end of cursor |
| +231 | 01004 | String truncated on assignment |
| +304 | 01515 | Value not within range of target |
| +802 | 01519 | Null indicator needed but not supplied |
B.6.3 Common Error Codes (Negative)
| SQLCODE | SQLSTATE | Meaning |
|---|---|---|
| -117 | 42802 | Number of insert values does not match columns |
| -180 | 22007 | Invalid date/time/timestamp value |
| -181 | 22007 | Invalid string representation of date/time |
| -204 | 42704 | Object not defined (table/view not found) |
| -205 | 42703 | Column name not found |
| -206 | 42703 | Column not in specified tables |
| -302 | 22001 | Value too large for variable |
| -305 | 22002 | Null indicator variable required |
| -407 | 23502 | NULL value not allowed in NOT NULL column |
| -501 | 24501 | Cursor not open |
| -502 | 24502 | Cursor already open |
| -530 | 23503 | Foreign key violation (parent not found) |
| -532 | 23504 | Delete restricted by foreign key |
| -539 | 42912 | Table does not have a primary key |
| -551 | 42501 | Authorization failure |
| -803 | 23505 | Duplicate key on unique index |
| -805 | 51002 | Package not found in plan (z/OS) |
| -811 | 21000 | Subquery returned more than one row |
| -818 | 51003 | Timestamp mismatch between plan and DBRM (z/OS) |
| -904 | 57011 | Resource unavailable (table space stopped, etc.) |
| -911 | 40001 | Deadlock or timeout, rollback performed |
| -913 | 57033 | Deadlock or timeout, no rollback (z/OS) |
B.6.4 Utility and Resource Codes
| SQLCODE | SQLSTATE | Meaning |
|---|---|---|
| -289 | 57003 | Unable to allocate new page (table space full) |
| -290 | 55024 | Table in CHECK PENDING status |
| -668 | 57016 | Table in REORG PENDING state (LUW) |
| -964 | 57011 | Transaction log full |
| -1024 | 57058 | Database connection required (not connected) |
| -1218 | 54008 | Maximum concurrent statements exceeded |
B.6.5 Using SQLCODE in Application Programs
-- In a stored procedure
DECLARE CONTINUE HANDLER FOR SQLSTATE '23505'
SET duplicate_found = 1;
-- Or using SQLCODE
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
BEGIN
GET DIAGNOSTICS CONDITION 1
err_sqlcode = DB2_RETURNED_SQLCODE,
err_msg = MESSAGE_TEXT;
END;
B.6.6 SQLSTATE Class Structure
| Class | Meaning |
|---|---|
| 00xxx | Successful completion |
| 01xxx | Warning |
| 02xxx | No data |
| 21xxx | Cardinality violation |
| 22xxx | Data exception |
| 23xxx | Constraint violation |
| 24xxx | Invalid cursor state |
| 40xxx | Transaction rollback |
| 42xxx | Syntax/access error |
| 51xxx | Invalid application state |
| 54xxx | SQL or product limit exceeded |
| 55xxx | Object not in prerequisite state |
| 57xxx | Resource not available |
| 58xxx | System error |
SQLSTATE values are portable across DB2 platforms and (mostly) across database vendors. SQLCODE values are DB2-specific. Prefer SQLSTATE in new application code for better portability.
B.7 Quick Syntax Reference Card
SELECT cols FROM tabs [WHERE cond] [GROUP BY cols] [HAVING cond] [ORDER BY cols]
INSERT INTO tab [(cols)] VALUES (vals) | SELECT ...
UPDATE tab SET col=val [WHERE cond]
DELETE FROM tab [WHERE cond]
MERGE INTO tab USING src ON cond WHEN MATCHED THEN UPDATE ... WHEN NOT MATCHED THEN INSERT ...
CREATE TABLE tab (col type [constraints], ...) [IN tbsp]
ALTER TABLE tab ADD|ALTER|DROP COLUMN|CONSTRAINT ...
CREATE [UNIQUE] INDEX idx ON tab (cols) [INCLUDE (cols)]
DROP TABLE|INDEX|VIEW name
GRANT privs ON tab TO user|role
REVOKE privs ON tab FROM user|role
CALL procedure(args)
This reference covers roughly 80% of the SQL you will write against DB2 in a production environment. For the remaining 20%---global temporary tables, array types, inline SQL PL, autonomous transactions, and advanced XML/JSON processing---consult the IBM Knowledge Center or the relevant chapters in this book.
B.8 Aggregate Functions
| Function | Description | Notes |
|---|---|---|
COUNT(*) |
Count all rows | Includes NULLs |
COUNT(col) |
Count non-null values | Excludes NULLs |
COUNT(DISTINCT col) |
Count distinct non-null values | |
SUM(col) |
Sum of values | Ignores NULLs |
AVG(col) |
Average of values | Ignores NULLs |
MIN(col) |
Minimum value | Ignores NULLs |
MAX(col) |
Maximum value | Ignores NULLs |
STDDEV(col) |
Standard deviation | Population or sample variants available |
VARIANCE(col) |
Variance | |
LISTAGG(col, delimiter) |
Concatenate values into a string | LUW 11.1+, z/OS v12+ |
XMLAGG(expr) |
Aggregate XML values | |
ARRAY_AGG(col) |
Aggregate into an array | LUW 11.1+ |
LISTAGG example:
SELECT WORKDEPT,
LISTAGG(LASTNAME, ', ') WITHIN GROUP (ORDER BY LASTNAME) AS EMP_LIST
FROM EMP
GROUP BY WORKDEPT;
Result: D01 -> ADAMS, BAKER, CHEN
B.9 Transaction Control
-- Commit current transaction
COMMIT;
-- Rollback current transaction
ROLLBACK;
-- Savepoint management
SAVEPOINT sp1 ON ROLLBACK RETAIN CURSORS;
-- ... perform operations ...
ROLLBACK TO SAVEPOINT sp1;
RELEASE SAVEPOINT sp1;
-- Implicit commit behavior: DDL statements (CREATE, ALTER, DROP) implicitly
-- commit on LUW. On z/OS, DDL does NOT implicitly commit.
B.10 GRANT and REVOKE
-- Table privileges
GRANT SELECT, INSERT, UPDATE ON TABLE MYSCHEMA.ORDERS TO USER appuser;
GRANT ALL PRIVILEGES ON TABLE MYSCHEMA.ORDERS TO ROLE app_admin;
GRANT SELECT ON TABLE MYSCHEMA.ORDERS TO PUBLIC;
-- Schema privileges
GRANT CREATEIN ON SCHEMA MYSCHEMA TO USER developer1;
-- Database authorities (LUW)
GRANT DBADM ON DATABASE TO USER db2admin;
GRANT CONNECT ON DATABASE TO USER appuser;
-- Revoke
REVOKE INSERT ON TABLE MYSCHEMA.ORDERS FROM USER appuser;
REVOKE DBADM ON DATABASE FROM USER oldadmin;
-- Role management (LUW 9.5+, z/OS v9+)
CREATE ROLE app_reader;
GRANT SELECT ON TABLE MYSCHEMA.ORDERS TO ROLE app_reader;
GRANT ROLE app_reader TO USER analyst1;
B.11 Global Temporary Tables
-- Declared global temporary table (session scope, not logged)
DECLARE GLOBAL TEMPORARY TABLE SESSION.TEMP_RESULTS (
ID INTEGER,
RESULT VARCHAR(200)
) ON COMMIT PRESERVE ROWS NOT LOGGED;
INSERT INTO SESSION.TEMP_RESULTS VALUES (1, 'Intermediate result');
SELECT * FROM SESSION.TEMP_RESULTS;
-- Automatically dropped at end of session
-- Created global temporary table (definition persists; data is per-session)
CREATE GLOBAL TEMPORARY TABLE MYSCHEMA.GTT_WORK (
BATCH_ID INTEGER,
WORK_ITEM VARCHAR(500)
) ON COMMIT PRESERVE ROWS;
B.12 Sequence Objects
-- Create a sequence
CREATE SEQUENCE ORDER_SEQ
AS INTEGER
START WITH 1000
INCREMENT BY 1
NO MAXVALUE
NO CYCLE
CACHE 50;
-- Use in INSERT
INSERT INTO ORDERS (ORDER_ID, CUSTOMER_ID)
VALUES (NEXT VALUE FOR ORDER_SEQ, 100);
-- Get current value (must call NEXT VALUE first in the session)
SELECT PREVIOUS VALUE FOR ORDER_SEQ FROM SYSIBM.SYSDUMMY1;
-- Alter a sequence
ALTER SEQUENCE ORDER_SEQ RESTART WITH 5000;
B.13 Views and Materialized Query Tables
-- Standard view
CREATE OR REPLACE VIEW ACTIVE_EMPLOYEES AS
SELECT EMPNO, FIRSTNAME, LASTNAME, WORKDEPT, SALARY
FROM EMPLOYEE
WHERE STATUS = 'A';
-- With check option (prevents inserts/updates that would not be visible through the view)
CREATE VIEW HIGH_EARNERS AS
SELECT * FROM EMPLOYEE WHERE SALARY > 100000
WITH CHECK OPTION;
-- Materialized query table (MQT) -- LUW
CREATE TABLE DEPT_SUMMARY AS (
SELECT WORKDEPT, COUNT(*) AS EMP_COUNT, AVG(SALARY) AS AVG_SAL
FROM EMPLOYEE
GROUP BY WORKDEPT
) DATA INITIALLY DEFERRED REFRESH DEFERRED;
-- Refresh the MQT
REFRESH TABLE DEPT_SUMMARY;
-- The optimizer may automatically route qualifying queries to the MQT
-- (controlled by CURRENT REFRESH AGE and CURRENT MAINTAINED TABLE TYPES FOR OPTIMIZATION)
SET CURRENT REFRESH AGE ANY;
SET CURRENT MAINTAINED TABLE TYPES FOR OPTIMIZATION ALL;
B.14 Data Type Quick Reference
| Category | Data Type | Description | Storage |
|---|---|---|---|
| Integer | SMALLINT |
-32,768 to 32,767 | 2 bytes |
| Integer | INTEGER / INT |
-2.1B to 2.1B | 4 bytes |
| Integer | BIGINT |
-9.2E18 to 9.2E18 | 8 bytes |
| Decimal | DECIMAL(p,s) / NUMERIC |
Exact numeric, precision p, scale s | (p/2)+1 bytes |
| Float | REAL / FLOAT(n) where n<=24 |
Single precision | 4 bytes |
| Float | DOUBLE / FLOAT(n) where n>24 |
Double precision | 8 bytes |
| Float | DECFLOAT(16\|34) |
Decimal floating point | 8 or 16 bytes |
| Character | CHAR(n) |
Fixed-length, 1-254 bytes | n bytes |
| Character | VARCHAR(n) |
Variable-length, 1-32,672 bytes | actual + 2 |
| Character | CLOB(n) |
Character large object, up to 2 GB | varies |
| Binary | BINARY(n) |
Fixed-length binary | n bytes |
| Binary | VARBINARY(n) |
Variable-length binary | actual + 2 |
| Binary | BLOB(n) |
Binary large object, up to 2 GB | varies |
| Date/Time | DATE |
YYYY-MM-DD | 4 bytes |
| Date/Time | TIME |
HH.MM.SS | 3 bytes |
| Date/Time | TIMESTAMP(p) |
Date + time + fractional seconds | 7-13 bytes |
| XML | XML |
Native XML storage | varies |
| Boolean | BOOLEAN |
TRUE, FALSE, or NULL (LUW 11.1+) | 1 byte |
Platform differences in character types: On z/OS, CHAR and VARCHAR are stored in EBCDIC by default (unless the table space uses Unicode). On LUW, the encoding depends on the database code set (typically UTF-8). When connecting cross-platform, DB2 handles conversion via DRDA, but be aware of potential length expansion with multi-byte characters.
B.15 Conditional Expressions
-- Simple CASE
SELECT EMPNO,
CASE STATUS
WHEN 'A' THEN 'Active'
WHEN 'I' THEN 'Inactive'
WHEN 'T' THEN 'Terminated'
ELSE 'Unknown'
END AS STATUS_DESC
FROM EMPLOYEE;
-- Searched CASE
SELECT EMPNO,
CASE
WHEN SALARY > 100000 THEN 'Executive'
WHEN SALARY > 60000 THEN 'Senior'
WHEN SALARY > 35000 THEN 'Mid-Level'
ELSE 'Entry'
END AS PAY_BAND
FROM EMPLOYEE;
-- COALESCE (returns first non-null)
SELECT EMPNO, COALESCE(PHONE_WORK, PHONE_MOBILE, PHONE_HOME, 'No phone') AS CONTACT
FROM EMPLOYEE;
-- NULLIF (returns NULL if equal)
SELECT EMPNO, NULLIF(BONUS, 0) AS BONUS_OR_NULL
FROM EMPLOYEE;
-- Useful to avoid division by zero: SALARY / NULLIF(HOURS, 0)
B.16 SET Operators
-- UNION (removes duplicates)
SELECT EMPNO, LASTNAME FROM EMP_NYC
UNION
SELECT EMPNO, LASTNAME FROM EMP_LON;
-- UNION ALL (preserves duplicates, faster)
SELECT EMPNO, LASTNAME FROM EMP_NYC
UNION ALL
SELECT EMPNO, LASTNAME FROM EMP_LON;
-- INTERSECT (rows in both)
SELECT EMPNO FROM ACTIVE_EMPLOYEES
INTERSECT
SELECT EMPNO FROM BONUS_ELIGIBLE;
-- EXCEPT (rows in first but not second)
SELECT EMPNO FROM ALL_EMPLOYEES
EXCEPT
SELECT EMPNO FROM TERMINATED_EMPLOYEES;
-- Multiple set operations with precedence (use parentheses for clarity)
(SELECT EMPNO FROM DEPT_A UNION SELECT EMPNO FROM DEPT_B)
EXCEPT
SELECT EMPNO FROM ON_LEAVE;
B.17 Stored Procedure Quick Syntax
-- Create a stored procedure (SQL PL)
CREATE OR REPLACE PROCEDURE process_order(
IN p_customer_id INTEGER,
IN p_amount DECIMAL(11,2),
OUT p_order_id INTEGER
)
LANGUAGE SQL
MODIFIES SQL DATA
BEGIN
DECLARE v_status CHAR(1) DEFAULT 'N';
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
SET p_order_id = -1;
END;
INSERT INTO ORDERS (CUSTOMER_ID, TOTAL_AMT, STATUS)
VALUES (p_customer_id, p_amount, v_status);
SET p_order_id = IDENTITY_VAL_LOCAL();
IF p_amount > 10000 THEN
INSERT INTO AUDIT_LOG (ORDER_ID, EVENT)
VALUES (p_order_id, 'HIGH_VALUE_ORDER');
END IF;
COMMIT;
END;
-- Call the procedure
CALL process_order(100, 15000.00, ?);
B.18 Trigger Quick Syntax
-- BEFORE trigger (validation)
CREATE OR REPLACE TRIGGER check_salary
BEFORE UPDATE OF SALARY ON EMPLOYEE
REFERENCING NEW AS N OLD AS O
FOR EACH ROW
WHEN (N.SALARY > O.SALARY * 1.50)
SIGNAL SQLSTATE '75001' SET MESSAGE_TEXT = 'Salary increase exceeds 50% limit';
-- AFTER trigger (audit logging)
CREATE OR REPLACE TRIGGER audit_salary_change
AFTER UPDATE OF SALARY ON EMPLOYEE
REFERENCING NEW AS N OLD AS O
FOR EACH ROW
INSERT INTO SALARY_AUDIT (EMPNO, OLD_SALARY, NEW_SALARY, CHANGE_DATE, CHANGED_BY)
VALUES (N.EMPNO, O.SALARY, N.SALARY, CURRENT TIMESTAMP, SESSION_USER);