Identity columns, ROWID, defaults, and BEFORE triggers all change values during an INSERT. A separate SELECT afterward can race with other work and needs a key you might not have yet. DB2 for z/OS lets you nest the data-change statement in the FROM clause of a SELECT: data-change-table-reference. This page covers FINAL TABLE / OLD TABLE, SELECT FROM INSERT, UPDATE, DELETE, and MERGE.
The intermediate result is the set of rows directly affected by the nested INSERT, searched UPDATE, searched DELETE, or MERGE. IBM syntax (simplified):
12345FINAL TABLE ( INSERT … ) FINAL TABLE ( searched UPDATE … ) OLD TABLE ( searched UPDATE … ) OLD TABLE ( searched DELETE … ) FINAL TABLE ( MERGE … )
| Nested statement | Qualifier | Intermediate rows |
|---|---|---|
| INSERT | FINAL TABLE | Inserted rows after defaults, generated columns, BEFORE triggers, constraints |
| searched UPDATE | FINAL TABLE or OLD TABLE | After images or before images of updated rows |
| searched DELETE | OLD TABLE | Rows as they were before deletion |
| MERGE | FINAL TABLE | Rows inserted or updated by the MERGE as they exist at completion |
Rules beginners must memorize:
Content is determined when the cursor is opened (or when SELECT INTO runs). Later searched updates in the same program do not change that already-built result. Scrollable cursors over SELECT FROM INSERT must be INSENSITIVE.
Nest INSERT inside FINAL TABLE. The result includes generated identity / ROWID / row-change-timestamp values, defaults, and BEFORE INSERT trigger assignments. Constraints are enforced before the result is produced. AFTER INSERT triggers do not change those result values; AFTER triggers that further modify the target are rejected with FINAL TABLE.
123456-- EMPNO is GENERATED ALWAYS AS IDENTITY SELECT EMPNO, HIRETYPE, HIREDATE FROM FINAL TABLE ( INSERT INTO EMPSAMP (NAME, SALARY, DEPTNO, LEVEL) VALUES ('Mary Smith', 35000.00, 11, 'Associate') );
Host-language form:
123456SELECT EMPNO, HIRETYPE, HIREDATE INTO :HV-EMPNO, :HV-HIRETYPE, :HV-HIREDATE FROM FINAL TABLE ( INSERT INTO EMPSAMP (NAME, SALARY, DEPTNO, LEVEL) VALUES (:NAME, :SALARY, :DEPTNO, :LEVEL) );
Target of the INSERT must be a base table, a view with WITH CASCADED CHECK, or a view with no WHERE clause. If the view has a search condition, rows that would not satisfy the view are not inserted (the INSERT fails). Multiple-row INSERT inside SELECT cannot be NOT ATOMIC when other host variables appear in the outer fullselect, and cannot be atomic multi-row with USING DESCRIPTOR in that situation.
12345678910111213141516DECLARE CS1 CURSOR FOR SELECT EMP_ROWID FROM FINAL TABLE ( INSERT INTO DSN8C10.EMP_PHOTO_RESUME (EMPNO) SELECT EMPNO FROM DSN8C10.EMP ); -- Preserve insert order from a host-variable array DECLARE CS3 CURSOR FOR SELECT EMP_ROWID FROM FINAL TABLE ( INSERT INTO DSN8C10.EMP_PHOTO_RESUME (EMPNO) VALUES (:hva_empno) FOR 5 ROWS ) ORDER BY INPUT SEQUENCE;
ORDER BY INPUT SEQUENCE is valid only when INSERT is in the SELECT’s FROM clause. FETCH FIRST n ROWS ONLY on the outer SELECT limits how many of those inserted rows you fetch; it does not stop the INSERT of the remaining rows—the INSERT still runs as written. To insert fewer rows, change the INSERT itself.
INCLUDE (col type, …) on the nested INSERT adds result columns that are not stored in the target. The INSERT fullselect (or VALUES) supplies values for target columns and INCLUDE columns together.
1234567DECLARE CS1 CURSOR FOR SELECT manager_num, projname FROM FINAL TABLE ( INSERT INTO PROJ (DEPTNO) INCLUDE (manager_num CHAR(6)) SELECT DEPTNO, MGRNO FROM DEPT );
Use a searched UPDATE (WHERE, not WHERE CURRENT OF). FINAL TABLE returns after-images; OLD TABLE returns before-images. The UPDATE cannot correlate outside itself. AFTER triggers that further change the target are not allowed. Views must be symmetric or WITH CASCADED CHECK OPTION (IBM: if the SELECT FROM UPDATE references a view, define it WITH CASCADED CHECK OPTION).
12345678910111213SELECT EMPNO, SALARY FROM FINAL TABLE ( UPDATE DSN8C10.EMP SET SALARY = SALARY * 1.10 WHERE JOB = 'CLERK' ); SELECT EMPNO, SALARY AS OLD_SALARY FROM OLD TABLE ( UPDATE DSN8C10.EMP SET SALARY = SALARY * 1.10 WHERE JOB = 'CLERK' );
A searched UPDATE inside SELECT does not clear AREO* status of a table (an IBM restriction to be aware of for REORG/advisory states).
Only OLD TABLE applies: you cannot select rows after they are gone. Searched DELETE only; no correlation to the outer query; same view and AFTER-trigger restrictions as UPDATE.
123456SELECT EMPNO, LASTNAME, WORKDEPT FROM OLD TABLE ( DELETE FROM DSN8C10.EMP WHERE WORKDEPT = 'E21' AND JOB = 'FIELDREP' );
Typical uses: write an audit table in the same unit of work from the returned old rows, or return to a client what was removed. The DELETE still happens when the SELECT runs (at OPEN for a cursor).
FINAL TABLE (MERGE …) returns the rows MERGE inserted or updated, as they exist at completion. Extra IBM rules include:
12345678910SELECT ACCT, AMT FROM FINAL TABLE ( MERGE INTO ACCOUNT AS T USING (SELECT ACCT, AMT FROM STAGE) AS S ON T.ACCT = S.ACCT WHEN MATCHED THEN UPDATE SET T.AMT = T.AMT + S.AMT WHEN NOT MATCHED THEN INSERT (ACCT, AMT) VALUES (S.ACCT, S.AMT) );
FINAL TABLE with a view that has an INSTEAD OF trigger for that change type is an error. OLD TABLE can be used when selecting expressions from a view in some cases where FINAL TABLE cannot (IBM restricts NEXT VALUE, non-deterministic / external action functions, and functions that read or modify SQL data in view expressions unless OLD TABLE is specified).
INSERT is putting a new toy in the box. Sometimes the box stamps a secret number on the toy. SELECT FROM INSERT is looking at the toy while you put it in, so you can read the stamp immediately. FINAL TABLE is “how it looks after the box’s rules.” OLD TABLE is a photo from before you changed or threw the toy away. You are not allowed to dump two boxes into the same glance—the special FROM clause may list only that one change.
1. What is a data-change-table-reference?
2. Which qualifier is used for INSERT and MERGE?
3. What does FINAL TABLE include from BEFORE triggers?
4. Where may a data-change-table-reference appear?
5. What is INCLUDE on the nested INSERT for?