Some inner queries are a single shared list. Others must look at this employee, this department, or this group. In DB2 for z/OS that second kind is a correlated subquery: it names a column from an outer table. This page compares correlated and uncorrelated subqueries, shows how correlation names work, contrasts EXISTS with IN, and explains when you actually need correlation.
An uncorrelated subquery does not mention outer-row values. For every employee, “employees on project MA2111” is the same inner question:
1234567SELECT EMPNO, LASTNAME, COMM FROM DSN8C10.EMP WHERE EMPNO IN ( SELECT EMPNO FROM DSN8C10.EMPPROJACT WHERE PROJNO = 'MA2111' );
Db2 can run the inner SELECT once, remember the EMPNO set, and test membership. The inner WHERE only uses EMPPROJACT columns and a literal.
A correlated subquery is reevaluated when Db2 examines a new outer row (WHERE) or a new group (HAVING). It contains a correlated reference: a column of a table identified at a higher level.
IBM’s sample: list employees whose education level is greater than the average education of their own department—not the company average.
1234567SELECT EMPNO, LASTNAME, WORKDEPT, EDLEVEL FROM DSN8C10.EMP X WHERE EDLEVEL > ( SELECT AVG(EDLEVEL) FROM DSN8C10.EMP WHERE WORKDEPT = X.WORKDEPT );
X is a correlation name for the outer EMP. X.WORKDEPT in the inner WHERE is the correlated reference. When the outer row is Christine in department A00, the inner query is AVG(EDLEVEL) WHERE WORKDEPT = 'A00'. When the outer row is Michael in B01, the inner query uses B01. An uncorrelated company-wide AVG(EDLEVEL) would be the wrong business question.
Because the same table can appear at many levels, IBM recommends unique correlation names and qualified columns. A qualified name Q.C is a correlated reference only if all three are true:
If Q names a table at more than one higher level, Q.C refers to the lowest level that contains the subquery. Unqualified correlated names are legal but poor practice: name resolution walks outward and is easy to misread.
123456789-- Outer EMP is X; inner EMP is unqualified local columns SELECT EMPNO FROM DSN8C10.EMP X WHERE EXISTS ( SELECT * FROM DSN8C10.EMP WHERE X.WORKDEPT = WORKDEPT AND SALARY < 20000 );
Inner WORKDEPT and SALARY belong to the inner EMP (local FROM). X.WORKDEPT belongs to the outer row. The query lists employee numbers of people who work in a department that has at least one employee earning less than 20000—including possibly themselves.
| Topic | Uncorrelated | Correlated |
|---|---|---|
| Depends on outer row? | No | Yes — correlated reference |
| Logical executions | Once (same result reused) | Once per outer row or group (conceptually) |
| Typical predicates | IN, = (scalar), comparisons to global aggregates | EXISTS, per-row scalar subqueries, per-group HAVING |
| Example need | “On project MA2111” (constant) | “Above my department’s average education” |
You can often rewrite a correlated subquery as a join (sometimes with GROUP BY on the inner grain). Correctness first: a join can duplicate outer rows if the inner match is one-to-many; EXISTS never duplicates the outer row. That is a practical reason EXISTS is popular for “parent has children” filters.
| Form | Meaning |
|---|---|
| EXISTS (fullselect) | True if inner returns ≥1 row; false if 0; never UNKNOWN; columns ignored |
| NOT EXISTS (fullselect) | Logical NOT of EXISTS — true when inner is empty |
| expr IN (fullselect) | True if expr equals any inner value (= ANY); UNKNOWN if comparisons are unknown |
| expr NOT IN (fullselect) | Equivalent to <> ALL; a single inner null can make the whole predicate UNKNOWN |
IN wants a value list (one column). It shines when the inner query is uncorrelated or when you think “is this key in that set?”
12345678910111213141516-- Uncorrelated IN: membership in a fixed project roster SELECT EMPNO, LASTNAME FROM DSN8C10.EMP WHERE EMPNO IN ( SELECT EMPNO FROM DSN8C10.EMPPROJACT WHERE PROJNO = 'MA2111' ); -- Correlated IN is possible but often rewritten as EXISTS SELECT DEPTNO, DEPTNAME FROM DSN8C10.DEPT D WHERE DEPTNO IN ( SELECT WORKDEPT FROM DSN8C10.EMP E WHERE E.WORKDEPT = D.DEPTNO AND E.JOB = 'MANAGER' );
The second form correlates D.DEPTNO into the inner query. EXISTS is usually clearer for that “does a matching employee exist?” reading:
12345678SELECT DEPTNO, DEPTNAME FROM DSN8C10.DEPT D WHERE EXISTS ( SELECT * FROM DSN8C10.EMP E WHERE E.WORKDEPT = D.DEPTNO AND E.JOB = 'MANAGER' );
EXISTS (fullselect) is true when the inner row count is not zero, false when it is zero, and cannot be unknown. The select list is ignored; use SELECT *. There is no built-in keyword “NOT EXISTS” as part of the predicate name; you write the logical operator NOT in front of EXISTS.
EXISTS can contain UNION ALL of several inner sources, each correlated:
1234567891011121314151617SELECT C.SNO FROM CUST C WHERE C.STATE = 'CA' AND EXISTS ( SELECT * FROM MONTH1 WHERE DATE BETWEEN '01/01/2009' AND '01/31/2009' AND C.SNO = SNO UNION ALL SELECT * FROM MONTH2 WHERE DATE BETWEEN '02/01/2009' AND '02/28/2009' AND C.SNO = SNO UNION ALL SELECT * FROM MONTH3 WHERE DATE BETWEEN '03/01/2009' AND '03/31/2009' AND C.SNO = SNO ) ORDER BY C.SNO;
“Departments with no employees” is an anti-join. Prefer NOT EXISTS:
123456DELETE FROM DEPARTMENT THIS WHERE NOT EXISTS ( SELECT * FROM EMPLOYEE WHERE WORKDEPT = THIS.DEPTNO );
THIS is the correlation name for the DELETE target (the highest level of a DELETE is the target table, not a FROM clause). If you write DEPTNO NOT IN (SELECT WORKDEPT FROM EMPLOYEE) and WORKDEPT can be null, a null in the inner result makes NOT IN UNKNOWN, and WHERE keeps nothing—so you might delete zero rows when you expected to delete empty departments. NOT EXISTS stays true/false.
Use a correlated reference when the inner question is about the current outer row or group:
Skip correlation when the inner set is global or constant: “in this project number,” “greater than the company average,” “IN a literal list.” Forcing correlation on a global query (WHERE 1 = 1 AND outer.col = outer.col inside the inner query) does not help and can confuse readers and the optimizer.
If you can state the result as a join of two sets without per-row inner aggregates, a join (or JOIN to a grouped nested table) is often easier to test. Keep the subquery when you need EXISTS semantics (no row multiplication) or a scalar that must error on duplicates (-811).
Correlation in HAVING refers to the current group. A correlated reference to that group must identify a grouping column or be inside a column function.
123456789SELECT WORKDEPT, AVG(SALARY) AS DEPT_AVG FROM DSN8C10.EMP E GROUP BY WORKDEPT HAVING AVG(SALARY) > ( SELECT AVG(SALARY) FROM DSN8C10.EMP M WHERE M.JOB = 'MANAGER' AND M.WORKDEPT = E.WORKDEPT );
E.WORKDEPT is the grouping column of the outer group. The inner query is “average manager salary in this department.” Departments with no manager get a null inner average; the comparison is UNKNOWN; HAVING drops those groups.
Searched UPDATE and DELETE use the target table as the top of the hierarchy. Qualify the target with a correlation name when the subquery must see the current row:
12345678UPDATE DSN8C10.EMP E SET SALARY = SALARY * 1.05 WHERE EXISTS ( SELECT * FROM DSN8C10.EMPPROJACT P WHERE P.EMPNO = E.EMPNO AND P.PROJNO LIKE 'MA%' );
Without E.EMPNO, an inner EMPNO would be ambiguous or would not mean “this employee.”
Uncorrelated is one shared shopping list for the whole class: “everyone whose number is on the project list.” Correlated is walking to each child and asking a question about that child: “is your score higher than the average of your table?” EXISTS is peeking in a backpack: “is there at least one sandwich?” You do not care what the sandwich is. IN is checking whether your lunch ticket number appears on a posted list.
1. What makes a subquery correlated?
2. When is Q.C a correlated reference?
3. How does uncorrelated IN differ from correlated EXISTS for “employees on a project”?
4. Why is NOT EXISTS often safer than NOT IN?
5. When is correlation required?