Chapter 07 · DML, Transactions, Read Consistency, Undo, Locks, and Concurrency

INSERT, Multi-Table INSERT, UPDATE, DELETE, MERGE, RETURNING, and Error Logging

Apply Oracle DML safely with deterministic source sets, RETURNING, multi-table INSERT, and row-level error logging while preserving rollback paths.

Intermediate → Advanced110–130 minutesDML + MERGE/error-log lab26ai RU 23.26.3 baselineFree · SERVICEHUB_OWNER + optional DBA observerLast reviewed: August 2026

Learning outcomes

ServiceHub receives work-order changes from APIs, batch files, and technicians in the field. A careless mass update can close every ticket, two source rows can make one MERGE target ambiguous, and one malformed batch row can abort an otherwise useful load. Oracle DML is not merely syntax for changing rows; it is transaction-scoped state change with locking, redo/undo, privilege, and determinism consequences.

01

Choose single-table and multi-table INSERT forms from the required target behavior.

02

Preview and verify UPDATE/DELETE predicates before destructive DML.

03

Use deterministic MERGE source sets and explain ORA-30926 risk.

04

Use RETURNING to avoid a follow-up query when changed values are needed.

05

Use DML error logging as controlled reject capture, not as permission to ignore bad data.

Lab, release, and licensing baseline

Mandatory exercises target the disposable ServiceHub schema in Oracle AI Database Free 26ai. Current documentation was re-checked against RU 23.26.3, SQL Developer 26.2, and SQLcl 26.2. Oracle AI Database Free is limited to 2 foreground CPUs, 2 GB combined SGA/PGA memory, and 12 GB user data and receives no Oracle patches or Support SRs. No paid option, management pack, RAC, Data Guard, Exadata, or cloud service is required. Dynamic performance views shown as observer evidence can require catalog privileges and are clearly separated from application-owner work.

1. DML changes rows inside a transaction

INSERT, UPDATE, DELETE, and MERGE acquire transactional locks and generate undo/redo as required by the operation. Until commit, your session can see its own changes while other sessions normally see only committed versions. A statement error usually rolls back that statement, not every successful statement already issued in the transaction.

sql · safe setup for the chapter DML probe
DROP TABLE sh07_dml_stage IF EXISTS PURGE;DROP TABLE sh07_dml_target IF EXISTS PURGE;CREATE TABLE sh07_dml_target (    work_order_id NUMBER PRIMARY KEY,    external_key  VARCHAR2(30) NOT NULL UNIQUE,    status_code   VARCHAR2(12) NOT NULL                  CHECK (status_code IN ('OPEN','HOLD','CLOSED')),    amount        NUMBER(10,2) NOT NULL);CREATE TABLE sh07_dml_stage (    source_seq    NUMBER PRIMARY KEY,    external_key  VARCHAR2(30),    status_code   VARCHAR2(12),    amount        NUMBER(10,2));INSERT INTO sh07_dml_target VALUES (101,'WO-101','OPEN',250);INSERT INTO sh07_dml_target VALUES (102,'WO-102','HOLD',400);COMMIT;

2. INSERT and multi-table INSERT express different routing jobs

A conventional single-table insert adds rows to one target. Oracle multi-table insert can route one source query to several targets with INSERT ALL or conditionally with WHEN clauses. It is useful for relational routing, not a replacement for transactions or validation.

sql · route one source set to two audit targets
DROP TABLE sh07_open_audit IF EXISTS PURGE;DROP TABLE sh07_closed_audit IF EXISTS PURGE;CREATE TABLE sh07_open_audit   (external_key VARCHAR2(30), amount NUMBER(10,2));CREATE TABLE sh07_closed_audit (external_key VARCHAR2(30), amount NUMBER(10,2));INSERT ALL  WHEN status_code = 'OPEN' THEN    INTO sh07_open_audit(external_key, amount) VALUES(external_key, amount)  WHEN status_code = 'CLOSED' THEN    INTO sh07_closed_audit(external_key, amount) VALUES(external_key, amount)SELECT external_key, status_code, amountFROM sh07_dml_target;ROLLBACK;

Current Oracle restrictions matter: a multi-table insert does not support the ordinary RETURNING clause. If the application needs generated values back per row, choose a different DML shape rather than assuming every INSERT form has identical capabilities.

3. Mass UPDATE/DELETE: preview the row set first

The dangerous statement is often syntactically valid. Omitting WHERE from UPDATE or DELETE targets every eligible row. In a disposable lab, a savepoint makes the risk observable without keeping the damage.

sql · deliberately wrong, then repair before commit
SAVEPOINT before_mass_change;UPDATE sh07_dml_targetSET status_code = 'CLOSED';-- Every row was updated. This is valid SQL, but the business intent was one ticket.SELECT work_order_id, status_codeFROM sh07_dml_targetORDER BY work_order_id;ROLLBACK TO before_mass_change;-- Preview the intended key before changing it.SELECT work_order_id, external_key, status_codeFROM sh07_dml_targetWHERE external_key = 'WO-101';UPDATE sh07_dml_targetSET status_code = 'CLOSED'WHERE external_key = 'WO-101';ROLLBACK;

In production, add application guardrails such as expected-row-count checks, bounded batches, and transaction rollback on mismatch. Do not depend on a human noticing “2 rows updated” after an accidental mass change has already been committed.

4. MERGE is deterministic: make the source unique per target key

Oracle defines MERGE as deterministic: the same target row cannot be updated more than once by one MERGE statement. A staging set with multiple rows mapping to one existing target key is therefore not a harmless duplicate—it can make the statement unstable and raise ORA-30926.

sql · bad source: two rows map to WO-101
TRUNCATE TABLE sh07_dml_stage;INSERT INTO sh07_dml_stage VALUES (1,'WO-101','HOLD',260);INSERT INTO sh07_dml_stage VALUES (2,'WO-101','CLOSED',270);COMMIT;MERGE INTO sh07_dml_target tUSING sh07_dml_stage sON (t.external_key = s.external_key)WHEN MATCHED THEN UPDATE SET    t.status_code = s.status_code,    t.amount = s.amountWHEN NOT MATCHED THEN INSERT    (work_order_id, external_key, status_code, amount)VALUES    (1000 + s.source_seq, s.external_key, s.status_code, s.amount);-- Ambiguous source-to-target mapping can raise ORA-30926.

The safe repair is to define the source winner explicitly. “Whichever row Oracle happens to read last” is not a business rule.

sql · deterministic source using ROW_NUMBER
MERGE INTO sh07_dml_target tUSING (    SELECT external_key, status_code, amount, source_seq    FROM (        SELECT s.*,               ROW_NUMBER() OVER (                   PARTITION BY external_key                   ORDER BY source_seq DESC               ) AS rn        FROM sh07_dml_stage s    )    WHERE rn = 1) sON (t.external_key = s.external_key)WHEN MATCHED THEN UPDATE SET    t.status_code = s.status_code,    t.amount = s.amountWHEN NOT MATCHED THEN INSERT    (work_order_id, external_key, status_code, amount)VALUES    (1000 + s.source_seq, s.external_key, s.status_code, s.amount);ROLLBACK;

5. RETURNING reduces round trips

For eligible DML, RETURNING ... INTO can return values from changed rows directly to host or PL/SQL variables. This is useful for generated identifiers, timestamps, or before/after values when the SQL form supports them.

sql · single-row INSERT RETURNING
SET SERVEROUTPUT ONDECLARE    l_id     NUMBER;    l_status VARCHAR2(12);BEGIN    INSERT INTO sh07_dml_target(work_order_id, external_key, status_code, amount)    VALUES (103,'WO-103','OPEN',175)    RETURNING work_order_id, status_code INTO l_id, l_status;    DBMS_OUTPUT.PUT_LINE('id=' || l_id || ', status=' || l_status);    ROLLBACK;END;/

Do not assume RETURNING works with every DML variant: current documentation excludes cases such as multi-table inserts, parallel DML, and remote objects. Match the feature to the actual statement shape and driver binding model.

6. DML error logging keeps acceptable rows moving—but preserves evidence

DBMS_ERRLOG.CREATE_ERROR_LOG creates a reject table containing diagnostic columns such as ORA_ERR_NUMBER$, ORA_ERR_MESG$, and ORA_ERR_TAG$. The LOG ERRORS clause can record supported row-level failures while allowing other rows to succeed.

sql · create rejects and load good rows
BEGIN    DBMS_ERRLOG.CREATE_ERROR_LOG('SH07_DML_TARGET');EXCEPTION    WHEN OTHERS THEN        IF SQLCODE != -955 THEN RAISE; END IF;END;/TRUNCATE TABLE sh07_dml_stage;INSERT INTO sh07_dml_stage VALUES (10,'WO-110','OPEN',100);INSERT INTO sh07_dml_stage VALUES (11,'WO-111','BROKEN',200);INSERT INTO sh07_dml_stage VALUES (12,'WO-112','CLOSED',300);COMMIT;INSERT INTO sh07_dml_target(work_order_id, external_key, status_code, amount)SELECT 1000 + source_seq, external_key, status_code, amountFROM sh07_dml_stageLOG ERRORS INTO err$_sh07_dml_target ('LOAD-20260825')REJECT LIMIT UNLIMITED;SELECT external_key, status_codeFROM sh07_dml_targetWHERE external_key LIKE 'WO-11%'ORDER BY external_key;SELECT ora_err_number$, ora_err_mesg$, ora_err_tag$, external_key, status_codeFROM err$_sh07_dml_target;ROLLBACK;

Error logging is not universal. Some errors terminate the statement, and logged rejects still need ownership, review, retention, and remediation. A production loader should fail the overall job when the reject rate or error classes exceed an explicit policy.

7. Cleanup and production judgment

sql · cleanup only the disposable Chapter 07 objects
DROP TABLE err$_sh07_dml_target IF EXISTS PURGE;DROP TABLE sh07_open_audit IF EXISTS PURGE;DROP TABLE sh07_closed_audit IF EXISTS PURGE;DROP TABLE sh07_dml_stage IF EXISTS PURGE;DROP TABLE sh07_dml_target IF EXISTS PURGE;

Use DML only after defining target grain, predicates, concurrency expectations, and rollback. Deduplicate MERGE sources by business rule, not physical row order. Treat error tables as production data with access control and lifecycle rules. These operations require ordinary object privileges; no licensed pack or special COMPATIBLE change is required on the declared 26ai baseline.

8. Summary and next step

Safe Oracle DML means more than “the statement ran”: the affected set is verified, MERGE input is deterministic, returned values come from supported forms, row rejects are inspected, and the transaction is still under deliberate control. Lesson 2 now defines exactly where that transaction begins and ends.

Check your understanding

  1. Why can duplicate MERGE source rows be a correctness problem?
  2. What is the safest first step before a destructive UPDATE or DELETE?
  3. What does RETURNING eliminate in many application flows?
  4. Does LOG ERRORS mean every possible DML error becomes a reject row?
  5. Why is ROLLBACK still central even when the DML syntax is correct?
Review the answers

MERGE is deterministic and cannot update the same target row multiple times; ambiguous source mappings can raise ORA-30926 or otherwise violate the intended rule.

Preview the exact predicate/key set and establish an expected affected-row count before changing it.

It can return changed/generated values directly, avoiding a separate follow-up SELECT when the statement form supports it.

No. Oracle documents restrictions; some errors still terminate the statement.

Syntax correctness does not prove business correctness. A transaction rollback is the safety boundary before commit.

Authoritative references

  • INSERT — multi-table INSERT, RETURNING, and error logging rules
  • UPDATE — predicate, RETURNING, and wait behavior
  • DELETE — DELETE semantics and RETURNING restrictions
  • MERGE — deterministic MERGE and privileges
  • Managing Tables — Error Logging — DML error logging restrictions and caveats

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only Ethereum or ERC-20 compatible assets to this address.