Two predicates can mean the same business question and cost wildly different CPU. DB2 for z/OS evaluates WHERE and ON predicates in stages: first indexable matching, then other stage 1 (sargable) filters in the Data Manager, then stage 2 residual predicates in the Relational Data System. This page is the map: what those words mean, how Boolean combinations and XML predicates fit, and how to rewrite SQL so filtering happens early.
Imagine 10 million rows and a predicate that keeps 100. If the predicate is indexable and a good index exists, Db2 may touch a tiny slice of the index and a handful of data pages. If it is only stage 1, the Data Manager can still discard rows while reading pages—cheaper than shipping every row upward. If it is stage 2, rows flow to RDS first; CPU and getpages explode when nothing else filters early.
IBM’s processing order (simplified):
| Step | Component | When |
|---|---|---|
| Matching indexable | Index manager / Data Manager | While probing index keys |
| Stage 1 index screening | Data Manager | Index columns that were not chosen as matching |
| Page-range screening | Data Manager | Partitioning columns limit which partitions are read |
| Other stage 1 | Data Manager | After data page access, still close to the row |
| Stage 2 residual | Relational Data System (RDS) | After the row is returned from stage 1 |
Vocabulary you will hear from DBAs:
IBM’s rule of thumb in older wording: all indexable predicates are stage 1. The predicate C1 LIKE '%BC' is stage 1 but not indexable. Later manuals also list a small class that is indexable but not stage 1—notably XMLEXISTS.
An indexable predicate can match index entries. It might or might not become matching, depending on available indexes and the chosen access path.
1234567-- Indexable: can match an index on LASTNAME SELECT * FROM DSN8C10.EMP WHERE LASTNAME = 'SMITH'; -- Not indexable: <> is not a matching predicate SELECT * FROM DSN8C10.EMP WHERE SEX <> 'F';
Typical indexable forms (when types/lengths cooperate):
Non-indexable classics: <> / NOT, leading-wildcard LIKE, many expressions that wrap the column (COL concatenated, COL + 0, scalar functions not in the indexable list). DECFLOAT columns or constants can force a predicate that “looks” indexable to be treated as not stage 1. Over-long string comparisons can also lose stage 1.
Indexable is not a guarantee. EXPLAIN’s PLAN_TABLE (METHOD, ACCESSTYPE, MATCHCOLS) shows whether matching happened.
Stage depends on syntax, data types and lengths, join sequence, and whether the predicate is applied before or after a join. A predicate evaluated after a join is always stage 2. The same ON/WHERE text can be stage 1 if that table is first in the join sequence and stage 2 if it is the inner table after the join.
Outer join ON clauses: for a full outer join, ON is evaluated like a stage 2 predicate during the join. Predicates in a table expression can run before the join and stay stage 1—push filters into a nested table expression when you need early stage 1 on one side of a full join.
1234567-- EDLEVEL > 100 can be stage 1 inside the table expression -- before the full join SELECT * FROM (SELECT * FROM DSN8C10.EMP WHERE EDLEVEL > 100) AS X FULL JOIN DSN8C10.DEPT ON X.WORKDEPT = DSN8C10.DEPT.DEPTNO;
Compound predicates inherit stage from how they are combined.
12345678-- Still indexable if both sides are indexable WHERE WORKDEPT = 'A00' OR WORKDEPT = 'B01' -- equivalent and often cleaner: WHERE WORKDEPT IN ('A00', 'B01') -- Stage 2 OR: YEAR(...) residual poisons the whole OR WHERE WORKDEPT = 'A00' OR YEAR(HIREDATE) = 2010
Rewrite poisoned ORs as UNION of two queries, or replace the residual with a range so both legs stay indexable.
Wrapping a column in a function is the usual way a beginner accidentally writes stage 2 SQL. Prefer predicates that leave the column bare on the left.
123456789-- Weaker (often stage 2 / non-matching) unless rewritten WHERE YEAR(HIREDATE) = 2010 -- Stronger: indexable range on HIREDATE WHERE HIREDATE BETWEEN '2010-01-01' AND '2010-12-31' -- SUBSTR start 1 can be indexable; LIKE is clearer WHERE SUBSTR(LASTNAME, 1, 3) = 'SMI' WHERE LASTNAME LIKE 'SMI%'
IBM documents that predicates may be indexable if the expression is DATE, YEAR, or SUBSTR with start 1. The optimizer may also rewrite YEAR(date) = n into a BETWEEN. Do not rely on rewrite for every function: UPPER(LASTNAME) = 'SMITH' is a common residual unless you have expression-based indexes (limited on z/OS compared with LUW) or you store a search column already folded.
CASE in a WHERE clause is listed among stage 2 predicates. Use CASE in the SELECT list; keep WHERE as simple comparisons when you care about matching.
XMLEXISTS is the SQL/XML predicate that tests whether an XQuery/XPath expression returns a non-empty sequence. IBM’s summary classifies XMLEXISTS as indexable but not stage 1: it can be evaluated during XML index access, but it is not an ordinary Data Manager stage 1 predicate on a relational column. NOT XMLEXISTS is stage 2.
12345678-- Prefer simple XPath in XMLEXISTS so an XML index can match WHERE XMLEXISTS( '$d/order/item[@sku="W42"]' PASSING ORDER_DOC AS "d" ) -- FLWOR in XMLEXISTS is generally not indexable -- Keep FLWOR in XMLQUERY after rows already qualified
XML indexes are defined with an XMLPATTERN. The XMLEXISTS path needs to be compatible with that pattern (and namespaces). Combine a relational indexable predicate (ORDER_ID = ?) with XMLEXISTS so the relational filter runs first when it is selective.
| Predicate | Typical class | Note |
|---|---|---|
| LASTNAME = 'SMITH' | Indexable + stage 1 | Equality on a column; matching if index on LASTNAME |
| SALARY BETWEEN 40000 AND 60000 | Indexable + stage 1 | Range; matching on SALARY index |
| WORKDEPT IN ('A00','B01') | Indexable + stage 1 (rules apply) | IN-list matching when conditions in the SQL Reference are met |
| LASTNAME LIKE 'SMI%' | Indexable + stage 1 | Trailing wildcard only |
| LASTNAME LIKE '%ITH' | Stage 1, not indexable | Leading wildcard; IBM’s classic example |
| SEX <> 'F' | Not indexable | Inequalities like <> are not matching predicates |
| SUBSTR(LASTNAME,1,3) = 'SMI' | Can be indexable + stage 1 | SUBSTR start must be 1; still prefer LASTNAME LIKE 'SMI%' |
| YEAR(HIREDATE) = 2010 | Often rewritten; else stage 2 | Prefer BETWEEN on the date column |
| CASE ... in WHERE | Stage 2 | CASE expressions in predicates are residual |
| XMLEXISTS(xpath ...) | Indexable, not stage 1 | XML index possible; NOT XMLEXISTS is stage 2 |
Always confirm with EXPLAIN and, when available, DSN_FILTER_TABLE / predicate instrumentation. Join sequence and data types can move a “usually stage 1” predicate to stage 2.
The library has a card catalog (the index) and a huge stack of books (the table). An indexable question is one the catalog can answer: “cards that start with SMI.” A stage 1 question can be checked while pulling a book off the shelf. A stage 2 question is “open every book and see if chapter seven mentions cats”—you already carried the book to the desk. LIKE '%ITH' is “titles that end with ITH”: the catalog is in A–Z order, so you still scan. OR-ing a hard question with an easy one is like saying “catalog SMI or books that mention cats”—the librarian cannot use the catalog for the whole request.
1. What does “indexable” mean for a Db2 predicate?
2. Where are stage 2 (residual) predicates evaluated?
3. LASTNAME LIKE '%ITH' is typically:
4. If you OR an indexable predicate with a stage 2 predicate, the compound predicate is:
5. XMLEXISTS with a suitable XML index is classified by IBM as: