Sample data turns an empty table into something you can query, join, update, and test. In this hands-on tutorial, you will use INSERT VALUES for rows you write yourself and INSERT SELECT for rows produced by a query. You will also learn why explicit column lists matter, how identity columns, defaults, and null values behave, and how to protect a batch with constraints, diagnostics, COMMIT, and ROLLBACK.
Begin by understanding the target table. An INSERT must satisfy its data types, nullability rules, generated-column rules, unique keys, check constraints, and referential integrity. A script that looks valid can still fail because a department code has no matching parent row or because a salary is outside an allowed range. Looking at the table definition first is faster than guessing from an error later.
The examples below assume a training table named TRAINING.EMPLOYEE. Your authorization ID may use a different schema. Qualifying the table with its schema makes the destination unambiguous and avoids depending on CURRENT SQLID.
1234567891011121314CREATE TABLE TRAINING.EMPLOYEE ( EMP_ID INTEGER GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1), EMP_NAME VARCHAR(60) NOT NULL, DEPT_CODE CHAR(3) NOT NULL, JOB_TITLE VARCHAR(40) NOT NULL WITH DEFAULT 'TRAINEE', HIRE_DATE DATE NOT NULL WITH DEFAULT CURRENT DATE, SALARY DECIMAL(9,2), ACTIVE_FLAG CHAR(1) NOT NULL WITH DEFAULT 'Y', CONSTRAINT PK_TRAIN_EMP PRIMARY KEY (EMP_ID), CONSTRAINT CK_TRAIN_ACTIVE CHECK (ACTIVE_FLAG IN ('Y', 'N')), CONSTRAINT CK_TRAIN_SALARY CHECK (SALARY IS NULL OR SALARY >= 0) );
This design gives Db2 responsibility for EMP_ID. JOB_TITLE, HIRE_DATE, and ACTIVE_FLAG have defaults. SALARY is nullable, so a row can represent an employee whose salary is not known yet. EMP_NAME and DEPT_CODE are required and have no defaults; every INSERT must provide them.
Use INSERT with VALUES when you already know the values for a row. Put target columns after the table name, then put values in exactly the same order. The number of values must equal the number of listed columns, and each expression must be compatible with its target data type.
1234INSERT INTO TRAINING.EMPLOYEE (EMP_NAME, DEPT_CODE, JOB_TITLE, HIRE_DATE, SALARY) VALUES ('ALEX MORGAN', 'A10', 'APPLICATION DEVELOPER', DATE('2026-01-12'), 62000.00);
EMP_ID is omitted, so Db2 generates it. ACTIVE_FLAG is also omitted, so it receives its default of Y. Date conversion is explicit, which makes the intent clear and avoids depending on an installation-specific date format. Decimal salary is written without currency symbols or thousands separators because it is a numeric value, not display text.
Db2 permits an INSERT without a target column list when values are supplied for the table columns in their defined order. That shortcut is fragile. A future ALTER TABLE can add a column, and a reader must inspect the DDL to discover what each value means. Explicit names make review easier and let omitted columns use their defaults.
1234567-- Clear and resilient INSERT INTO TRAINING.EMPLOYEE (EMP_NAME, DEPT_CODE, SALARY) VALUES ('PRIYA SHAH', 'B20', 58500.00); -- Avoid positional inserts that depend on every table column -- INSERT INTO TRAINING.EMPLOYEE VALUES (...);
The first statement documents the mapping directly: the first value is EMP_NAME, the second is DEPT_CODE, and the third is SALARY. JOB_TITLE becomes TRAINEE, HIRE_DATE becomes the current date, and ACTIVE_FLAG becomes Y. That is deliberate defaulting, not missing data.
These three ideas are easy to confuse. An identity column generates a unique sequence value according to its definition. The DEFAULT keyword asks Db2 to apply a column's default. NULL means the value is unknown or not applicable; it does not mean zero, blank, or default.
123INSERT INTO TRAINING.EMPLOYEE (EMP_NAME, DEPT_CODE, JOB_TITLE, HIRE_DATE, SALARY, ACTIVE_FLAG) VALUES ('MATEO RUIZ', 'A10', DEFAULT, DEFAULT, NULL, DEFAULT);
This row intentionally says that Mateo's salary is unknown. The other DEFAULT markers request TRAINEE, the current date, and Y. If SALARY were NOT NULL, the explicit NULL would fail. If EMP_NAME were omitted, that would also fail because its required column has no default.
The most portable approach is a sequence of ordinary INSERT statements inside one transaction. It works across supported Db2 for z/OS releases, is easy to diagnose, and lets you give each row a readable comment. Run them without committing between rows when the complete sample set should succeed or fail together.
1234567891011INSERT INTO TRAINING.EMPLOYEE (EMP_NAME, DEPT_CODE, JOB_TITLE, SALARY) VALUES ('LINDA CHEN', 'A10', 'SYSTEMS ANALYST', 71000.00); INSERT INTO TRAINING.EMPLOYEE (EMP_NAME, DEPT_CODE, JOB_TITLE, SALARY) VALUES ('NOAH WILLIAMS', 'B20', 'DATABASE ADMINISTRATOR', 79000.00); INSERT INTO TRAINING.EMPLOYEE (EMP_NAME, DEPT_CODE, JOB_TITLE, SALARY, ACTIVE_FLAG) VALUES ('SARA IBRAHIM', 'C30', 'TEST ANALYST', 54000.00, 'Y');
Recent Db2 levels offer a shorter multi-row VALUES list. On Db2 13 for z/OS this form requires function level 506 and an APPLCOMPAT level that enables the feature. Do not copy it into an older subsystem without checking the package and subsystem level.
123456-- Db2 13 FL506 with APPLCOMPAT V13R1M506 or later INSERT INTO TRAINING.EMPLOYEE (EMP_NAME, DEPT_CODE, JOB_TITLE, SALARY) VALUES ('EMMA DAVIS', 'A10', 'PROGRAMMER', 57000.00), ('JAMES KIM', 'B20', 'PROGRAMMER', 59000.00), ('OLIVIA MARTIN', 'C30', 'BUSINESS ANALYST', 61000.00);
Application programs can also use multiple-row INSERT with host-variable arrays and FOR n ROWS. That reduces trips between a batch program and Db2. ATOMIC behavior treats the array as one statement; NOT ATOMIC CONTINUE ON SQLEXCEPTION can preserve successful array elements while reporting failures. Choose deliberately: partial success is useful only when the program records exactly which inputs failed.
INSERT SELECT is the set-based choice when source rows already exist or when a query can generate the desired rows. The SELECT is called a fullselect. Its number of output columns must match the INSERT target list, in order, and the source data types must be assignable to the targets.
1234567891011INSERT INTO TRAINING.EMPLOYEE (EMP_NAME, DEPT_CODE, JOB_TITLE, HIRE_DATE, SALARY, ACTIVE_FLAG) SELECT LASTNAME CONCAT ', ' CONCAT FIRSTNME, WORKDEPT, JOB, HIREDATE, SALARY, 'Y' FROM DSN8C10.EMP WHERE WORKDEPT IN ('A00', 'B01') AND SALARY IS NOT NULL;
Db2 evaluates the query and inserts every qualifying result row. Identity values are generated separately for the new target rows because EMP_ID is not in the target list. Constraints and triggers still run for each inserted row. INSERT SELECT does not bypass data quality rules merely because its source is another table.
A rerunnable setup script needs a stable rule. If the table has a business key, filter source rows with NOT EXISTS. In this training table, names are not guaranteed unique, so this pattern is suitable only if your exercise defines name plus department as its temporary sample key. Production tables should enforce a real business key with a unique constraint.
123456789INSERT INTO TRAINING.EMPLOYEE (EMP_NAME, DEPT_CODE, JOB_TITLE, HIRE_DATE, SALARY) SELECT S.EMP_NAME, S.DEPT_CODE, S.JOB_TITLE, S.HIRE_DATE, S.SALARY FROM TRAINING.EMPLOYEE_STAGE S WHERE NOT EXISTS (SELECT 1 FROM TRAINING.EMPLOYEE T WHERE T.EMP_NAME = S.EMP_NAME AND T.DEPT_CODE = S.DEPT_CODE);
Another safe classroom approach is to delete only rows tagged with a dedicated test batch identifier before reinserting them. Avoid deleting all rows from a shared table. Sample-data cleanup must be as carefully scoped as the insert itself.
Constraints are not obstacles to turn off; they are the table's safety checks. A primary key or unique constraint rejects duplicate keys, commonly reported as SQLCODE -803. Referential integrity rejects a child row whose parent key does not exist, commonly SQLCODE -530. A NOT NULL violation can produce SQLCODE -407, while a check constraint violation can produce SQLCODE -545.
When one ordinary INSERT statement fails, that statement's changes are backed out, but earlier successful statements in the same unit of recovery remain uncommitted. Your program or script must decide whether to correct and continue or issue ROLLBACK to undo the whole sample set.
A transaction groups related changes into a unit of recovery. COMMIT makes all changes since the previous commit permanent. ROLLBACK backs out the uncommitted changes. For a small teaching data set, insert all rows, validate them, and commit once only when the set is complete.
12345678910111213141516INSERT INTO TRAINING.EMPLOYEE (EMP_NAME, DEPT_CODE, JOB_TITLE, SALARY) VALUES ('AVA THOMPSON', 'A10', 'DEVELOPER', 64000.00); INSERT INTO TRAINING.EMPLOYEE (EMP_NAME, DEPT_CODE, JOB_TITLE, SALARY) VALUES ('LIAM BROWN', 'B20', 'DATA ANALYST', 60000.00); SELECT DEPT_CODE, COUNT(*) AS ROW_COUNT FROM TRAINING.EMPLOYEE WHERE EMP_NAME IN ('AVA THOMPSON', 'LIAM BROWN') GROUP BY DEPT_CODE; -- Choose one after checking the result: COMMIT; -- ROLLBACK;
Never place COMMIT before validation merely to make an error disappear. Once committed, ROLLBACK cannot remove those rows. In CICS or IMS, the transaction manager normally owns the syncpoint; use the environment's transaction API instead of independently issuing SQL COMMIT. In JDBC, check whether autocommit is enabled because it may commit every statement immediately.
Large sample sets and test-data generators need the same operational discipline as production batch. Establish the expected input count, record the starting state, use a repeatable key strategy, and stop on unexpected negative SQLCODEs. A warning or zero-row result can also be important even when it is not a hard SQL error.
12345678910111213EXEC SQL INSERT INTO TRAINING.EMPLOYEE (EMP_NAME, DEPT_CODE, JOB_TITLE, SALARY) VALUES (:WS-NAME, :WS-DEPT, :WS-JOB, :WS-SALARY:WS-SALARY-IND) END-EXEC. IF SQLCODE = 0 ADD 1 TO WS-INSERT-COUNT ELSE MOVE SQLCODE TO WS-FAILED-SQLCODE EXEC SQL ROLLBACK END-EXEC END-IF.
The indicator paired with WS-SALARY lets the program insert NULL when salary is unknown. In real COBOL, stop normal processing after rollback, log the source record key and SQL diagnostic fields, and return a failing condition code. Continuing without understanding an error can turn one rejected row into a misleading partial data set.
Verification should answer three questions: did the expected number of rows arrive, did defaults and generated values behave correctly, and are the business values accurate? Select named columns, sort the result for repeatable viewing, and aggregate counts by a useful category.
1234567891011121314SELECT EMP_ID, EMP_NAME, DEPT_CODE, JOB_TITLE, HIRE_DATE, SALARY, ACTIVE_FLAG FROM TRAINING.EMPLOYEE ORDER BY EMP_ID; SELECT DEPT_CODE, COUNT(*) AS EMPLOYEE_COUNT FROM TRAINING.EMPLOYEE GROUP BY DEPT_CODE ORDER BY DEPT_CODE; SELECT COUNT(*) AS INVALID_ACTIVE_FLAGS FROM TRAINING.EMPLOYEE WHERE ACTIVE_FLAG NOT IN ('Y', 'N') OR ACTIVE_FLAG IS NULL;
The last query should return zero. Although the constraint already prevents invalid active flags, validation queries document your expectations and teach you how to test a load. Do not use SELECT * in long-lived application code; named columns make the expected result shape clear.
Imagine the table is a box of employee cards. INSERT VALUES means you write one card yourself. INSERT SELECT means a copier reads cards from another box and makes new cards for this box. The column list labels the blanks so a name never lands in the salary space. DEFAULT is a helper stamp that fills in a normal answer, NULL means “we do not know this answer,” and identity is the ticket machine giving every card a number. Constraints are the grown-up checking that every card follows the rules. COMMIT locks the finished cards into the box; ROLLBACK takes out all cards you added since the last time you locked it.
1. Why should an INSERT normally include an explicit column list?
2. What is the main purpose of INSERT SELECT?
3. What happens when a value is omitted for a NOT NULL column with no default?
4. When should a batch load issue COMMIT?
5. Which multi-row VALUES form requires a recent Db2 for z/OS level?