Most interactive DB2 queries return a result set you scroll through. Programs are different: a COBOL, PL/I, or SQL PL routine often needs one row (or a handful of expression results) dropped straight into variables. That is what SELECT INTO and VALUES INTO do. This page also covers the related beginner questions “can I SELECT without FROM?” and “can VALUES act like a tiny table?”
The SELECT INTO statement produces a result table that contains at most one row and assigns the values in that row to targets. Targets can be host variables (with optional indicator variables), SQL variables or parameters inside SQL PL, global variables, or array elements in supported contexts. The number of items in the select list must equal the number of INTO targets, and a target must not appear more than once.
Think of SELECT INTO as “look up this key and put the columns in my working storage.” It is not a replacement for a cursor. If you need to walk many rows, declare a cursor and FETCH. If you need a single known row, SELECT INTO keeps the program shorter and avoids OPEN / FETCH / CLOSE for a one-shot read.
1234SELECT EMPNO, LASTNAME, SALARY INTO :HV-EMPNO, :HV-LASTNAME, :HV-SALARY:HV-SALARY-IND FROM DSN8C10.EMP WHERE EMPNO = :HV-EMPNO-IN;
Assignment follows Db2 assignment and comparison rules, left to right. If any assignment fails, Db2 does not leave a half-updated list of targets in a usable state for that statement—treat the whole INTO as failed and inspect SQLCODE / GET DIAGNOSTICS.
| Result table | Typical SQLCODE | Effect |
|---|---|---|
| Exactly one row | 0 | Values assigned to INTO targets in order |
| Zero rows | +100 (SQLSTATE 02000) | Targets are not assigned |
| Two or more rows | -811 (SQLSTATE 21000) | Statement fails; no reliable assignment |
| Null column, no indicator | -305 | Null cannot be stored in the host variable |
Always check SQLCODE after SELECT INTO. A program that assumes SQLCODE 0 and then uses host variables after +100 is reading leftover values from the previous successful call. That class of bug is extremely common in payroll and inquiry transactions.
Three honest ways to keep SELECT INTO legal:
123456SELECT LASTNAME, HIREDATE INTO :HV-LASTNAME, :HV-HIREDATE FROM DSN8C10.EMP WHERE WORKDEPT = :HV-DEPT ORDER BY HIREDATE DESC FETCH FIRST 1 ROW ONLY;
The select list is not limited to column names. You can mix columns, host variables, constants, expressions, and aggregate functions—as long as the result is still one row and each item has a matching target.
12345678910SELECT EMPNO, LASTNAME, :HV-RAISE, SALARY + :HV-RAISE INTO :HV-EMPNO, :HV-LASTNAME, :HV-RAISE-OUT, :HV-NEW-SAL FROM DSN8C10.EMP WHERE EMPNO = :HV-KEY;
Isolation clauses (WITH UR, WITH CS, and so on), FOR READ ONLY, common table expressions, and ORDER BY / FETCH / OFFSET can appear on SELECT INTO the same way they appear on a cursor SELECT, with the extra rule that FOR UPDATE must not be specified on SELECT INTO (SQLSTATE 42829). SELECT INTO is a read into variables, not a positioned update.
Beginners coming from other databases often write SELECT CURRENT DATE with no FROM clause. In Db2 for z/OS application programming, the documented pattern for “I need a SELECT but I am not reading a business table” is to use a dummy table, or to skip SELECT entirely and use VALUES INTO.
SYSIBM.SYSDUMMY1 is a one-row catalog table Db2 provides so you can select expressions. IBM notes that it uses the EBCDIC encoding scheme. Related dummy tables exist for other encodings (commonly discussed as SYSDUMMYA for ASCII and SYSDUMMYU for Unicode). If a special register or function result looks “wrong” in a Unicode application, check which dummy table you used—the FROM object’s encoding can influence string results.
12345678SELECT CURRENT DATE, CURRENT TIME, CURRENT TIMESTAMP, CURRENT SQLID FROM SYSIBM.SYSDUMMY1; SELECT RAND(:HV-SEED) FROM SYSIBM.SYSDUMMY1;
Dummy-table SELECT is also how you pull session data into a result set for QMF, SPUFI, or DSNTEP2 when you are not querying EMP or DEPT. It is a real table reference: you still need SELECT privilege on SYSIBM.SYSDUMMY1 (normally granted to PUBLIC in a typical install), and the statement still goes through prepare/bind.
When the goal is only to fill host variables, prefer VALUES INTO (next section) or a SET assignment in SQL PL. Those forms make the intent obvious: “evaluate expressions,” not “query a table that happens to have one row.”
VALUES INTO assigns the result of one or more expressions to variables. It does not name a FROM table. Use it for special registers, arithmetic, scalar functions, and other expressions you would otherwise wrap in SELECT … FROM SYSIBM.SYSDUMMY1 INTO …
12345VALUES CURRENT DATE, CURRENT TIME INTO :HV-DATE, :HV-TIME; VALUES 1 + 1, CURRENT SQLID INTO :HV-TWO, :HV-SQLID;
Rules that match SELECT INTO in spirit:
Do not confuse VALUES INTO with the VALUES statement used in a basic trigger. In a triggered action, VALUES expression is a way to invoke a user-defined function for its side effect; the result is discarded and is not assigned to host variables. That trigger VALUES is not how application programs retrieve dates.
A values-clause is a form of fullselect. That means VALUES can supply rows wherever a fullselect is legal: as a nested table in FROM, as a branch of UNION / EXCEPT / INTERSECT, or as the source of INSERT. This is how you build a tiny in-line table of constants without CREATE TABLE.
123456789SELECT ID, DEPT FROM (VALUES (1, 'A00'), (2, 'B01'), (3, 'C01')) AS T(ID, DEPT); INSERT INTO SESSION.STAGING (CODE, LABEL) SELECT CODE, LABEL FROM (VALUES ('A00', 'SPIFFY COMPUTER SERVICE DIV.'), ('B01', 'PLANNING')) AS X(CODE, LABEL);
Name the result columns in the correlation clause (AS T(ID, DEPT)). Without names, the engine still produces unnamed columns that are awkward to reference. Keep row widths consistent: every VALUES row must have the same number of expressions, and corresponding expressions must have compatible types (the same compatibility rules used for UNION).
Practical uses on z/OS:
VALUES as a table is not a substitute for a real lookup table when many programs share the codes. Constants in SQL drift. For production reference data, prefer a maintained table. Use VALUES when the set is tiny, local to one statement, and honestly constant.
In COBOL, host variables in INTO must be declared so the precompiler can see them (WORKING STORAGE, a DCLGEN copybook, or an SQL TYPE). Varying-length strings need a length prefix the precompiler understands. LOB targets may be locators or file reference variables when the value is large. Keep SELECT INTO lists short and aligned with the copybook so a DCLGEN change is obvious at compile time.
SELECT INTO is like asking the librarian for one specific book and putting it in your backpack. If the book is missing, your backpack stays as it was and the librarian says “not found” (+100). If two books match the title, the librarian refuses to guess which one you meant (-811). VALUES INTO is like writing today’s date on a sticky note without walking into the stacks at all. VALUES as a query source is like making a tiny homemade shelf of three sample books so you can practice lining them up before you touch the real library.
For a true single-row path, SELECT INTO is the usual choice and avoids cursor overhead. The bigger cost is usually the access path (index versus tablespace scan), not INTO versus FETCH. Do not use SELECT INTO inside a loop that should have been one set-oriented statement.
No. INTO names variables, not a table. To put query results into a table, use INSERT … SELECT, CREATE TABLE … AS, or a declared global temporary table plus INSERT.
It prevents -811, but if two rows qualify and you did not ORDER BY, you may silently pick an arbitrary row. Use it when you truly want “any one” or “the first after ORDER BY,” not as a way to ignore duplicate keys you should have prevented.
1. What happens when SELECT INTO finds no row?
2. What SQLCODE means SELECT INTO returned more than one row?
3. How do you SELECT an expression when you have no business table?
4. What is VALUES INTO used for?
5. Which clause can guarantee SELECT INTO sees at most one row?