Earlier lessons treated IN, EXISTS, and scalar lookups as recipes. This DB2 for z/OS page maps the grammar: fullselect, scalar subquery, table subquery (nested table expression), and correlated references—so you can see why a form is legal in WHERE but illegal in GROUP BY or ON.
IBM layers a query like this:
| Layer | Contains |
|---|---|
| subselect | SELECT, FROM, WHERE, GROUP BY, HAVING, OFFSET, FETCH |
| fullselect | subselect UNION/EXCEPT/INTERSECT subselect … |
| select-statement | WITH, fullselect, ORDER BY, FOR UPDATE/READ ONLY, isolation, OPTIMIZE FOR, SKIP LOCKED, QUERYNO |
A subquery is a fullselect in parentheses. That means the inner query may itself contain UNION:
1234567SELECT LASTNAME, FIRSTNME, SALARY FROM DSN8C10.EMP WHERE EMPNO = ( SELECT RESPEMP FROM PROJA1 WHERE MAJPROJ = 'SECRET' UNION SELECT RESPEMP FROM PROJA2 WHERE MAJPROJ = 'SECRET' );
The inner fullselect is still a scalar context: one column, at most one row after UNION’s duplicate elimination. Two different RESPEMP values still cause SQLCODE -811.
Set operators inside a subquery follow the same compatibility rules as a standalone UNION: same degree, compatible types. ORDER BY belongs on the select-statement (outer query), not on each inner subselect, unless the inner query is wrapped as a nested table expression that itself is a select-statement in contexts that allow it. Scalar fullselects may use ORDER BY with FETCH FIRST 1 ROW ONLY to pick a deterministic row.
A scalar-fullselect returns a single column value from a single row. Empty → null. More than one row → error. Use it anywhere an expression is allowed, except IBM’s banned list: CHECK, CREATE VIEW WITH CHECK OPTION, CALL input arguments, most aggregate arguments, ORDER BY, GROUP BY, and join ON.
123456SELECT DEPTNO, DEPTNAME, (SELECT COUNT(*) FROM DSN8C10.EMP E WHERE E.WORKDEPT = D.DEPTNO) AS EMP_COUNT FROM DSN8C10.DEPT D;
COUNT(*) is a scalar aggregate: one row even when the department has zero employees (count is 0, not null). A non-aggregated (SELECT EMPNO FROM EMP WHERE WORKDEPT = D.DEPTNO) would be -811 for any department with two people.
123SELECT EMPNO, SALARY FROM DSN8C10.EMP WHERE SALARY > (SELECT AVG(SALARY) FROM DSN8C10.EMP);
1234567SELECT EMPNO, SALARY, WORKDEPT FROM DSN8C10.EMP X WHERE SALARY > ( SELECT AVG(SALARY) FROM DSN8C10.EMP WHERE WORKDEPT = X.WORKDEPT );
A nested table expression is a fullselect in FROM. It is a table, not a scalar. You must provide a correlation name to qualify its columns in most useful queries.
12345678SELECT D.DEPTNO, D.DEPTNAME, A.AVG_SAL, A.N FROM DSN8C10.DEPT D INNER JOIN ( SELECT WORKDEPT, AVG(SALARY) AS AVG_SAL, COUNT(*) AS N FROM DSN8C10.EMP GROUP BY WORKDEPT ) AS A ON D.DEPTNO = A.WORKDEPT;
This is often clearer than a correlated scalar in the SELECT list and gives the optimizer a join to a grouped result. Column names inside the nested query that are not unique cannot be referenced from outside.
Write TABLE (fullselect) when the nested query must see columns from table-references to its left in the same FROM clause (lateral correlation). Without TABLE, the nested expression is not allowed to search the hierarchy above itself for those names.
12345678SELECT D.DEPTNO, X.EMPNO, X.LASTNAME FROM DSN8C10.DEPT D, TABLE ( SELECT EMPNO, LASTNAME FROM DSN8C10.EMP E WHERE E.WORKDEPT = D.DEPTNO FETCH FIRST 3 ROWS ONLY ) AS X;
Each department contributes up to three employees. That is a table subquery with a correlated reference to D. A JOIN LATERAL in other dialects is this TABLE () pattern on z/OS.
Correlation is not a separate syntax—it is a reference rule. Q.C is correlated when it appears in the subquery, Q is not in that subquery’s FROM, and Q names a higher-level table. Unique correlation names are recommended because EMP can appear at many levels.
12345678SELECT EMPNO FROM DSN8C10.EMP X WHERE EXISTS ( SELECT * FROM DSN8C10.EMPPROJACT P WHERE P.EMPNO = X.EMPNO AND P.PROJNO LIKE 'MA%' );
DELETE/UPDATE targets are the top of the hierarchy; qualify them when the subquery must see the current row:
123456DELETE FROM DSN8C10.DEPT THIS WHERE NOT EXISTS ( SELECT * FROM DSN8C10.EMP WHERE WORKDEPT = THIS.DEPTNO );
| Form | Result | Typical place |
|---|---|---|
| Predicate subquery | Used by IN, EXISTS, quantified, or = scalar | WHERE / HAVING |
| Scalar subquery | One column, 0–1 row (null if empty) | Expression (SELECT list, comparison, SET, …) |
| Table subquery | A derived table (many rows/columns) | FROM (nested table expression) |
| INSERT/UPDATE fullselect | A table of values to assign | INSERT … SELECT; SET col = (scalar) |
A nested table expression in FROM is still a query, not a data change. FINAL TABLE (INSERT …) is a different table-reference: it must stand alone in FROM and it modifies data. Do not mix them in one FROM list.
Rewrites: correlated EXISTS ↔ semi-join; scalar correlated aggregate ↔ join to grouped nested table; IN uncorrelated ↔ join (watch duplicates). Prefer the form whose cardinality matches the business rule (error on two matches vs extra rows).
A fullselect is a whole recipe (and you can tape two recipes together with UNION). If you use the recipe as a number (how many cookies?), that is scalar—you are allowed one number. If you use the recipe as a tray of cookies to sit beside another tray (FROM), that is a table subquery. Correlation is asking about this child’s tray, not the whole school’s.
1. What is a fullselect?
2. What is a table subquery in FROM?
3. When is a scalar subquery an error?
4. What makes a subquery correlated?
5. Which form ignores inner column values?