Oracle Statement-Level Trigger FAQs with Examples

A statement-level trigger fires once for the entire SQL statement, regardless of how many rows the statement affects.

The key difference from a row-level trigger is:

-- Statement-level
CREATE OR REPLACE TRIGGER trg_emp
AFTER UPDATE ON employees
BEGIN
    ...
END;
/

There is no FOR EACH ROW.

1. What is a statement-level trigger?

A statement-level trigger executes once when a DML statement occurs.

Suppose:

UPDATE employees
SET salary = salary + 1000;

If this updates 500 rows:

Statement-level trigger → fires 1 time
Row-level trigger       → fires 500 times

Example

CREATE OR REPLACE TRIGGER trg_emp_update
AFTER UPDATE ON employees
BEGIN
    DBMS_OUTPUT.PUT_LINE('Employees table was updated');
END;
/

The trigger fires once for the entire UPDATE.

2. How do I identify a statement-level trigger?

A trigger is statement-level when it doesn't contain:

FOR EACH ROW

Statement-level

CREATE OR REPLACE TRIGGER trg_emp
AFTER INSERT ON employees
BEGIN
    DBMS_OUTPUT.PUT_LINE('Insert statement executed');
END;
/

Row-level

CREATE OR REPLACE TRIGGER trg_emp
AFTER INSERT ON employees
FOR EACH ROW
BEGIN
    DBMS_OUTPUT.PUT_LINE('One row inserted');
END;
/

3. What happens if 1,000 rows are updated?

Consider:

CREATE OR REPLACE TRIGGER trg_emp
AFTER UPDATE ON employees
BEGIN
    DBMS_OUTPUT.PUT_LINE('Trigger fired');
END;
/

Then:

UPDATE employees
SET salary = salary + 500;

If 1,000 rows are affected:

UPDATE statement
      |
      +---- 1,000 rows
      |
      ↓
Statement-level trigger
      |
      ↓
Fires ONE time

4. Can a statement-level trigger use :OLD and :NEW?

No.

This is one of the most common interview questions.

:OLD and :NEW are row-level correlation variables.

This is invalid for a statement-level trigger:

CREATE OR REPLACE TRIGGER trg_emp
AFTER UPDATE ON employees
BEGIN
    DBMS_OUTPUT.PUT_LINE(:NEW.salary);
END;
/

You would get an error because there is no individual row associated with a statement-level trigger.

Use row-level trigger instead

CREATE OR REPLACE TRIGGER trg_emp
AFTER UPDATE ON employees
FOR EACH ROW
BEGIN
    DBMS_OUTPUT.PUT_LINE(:NEW.salary);
END;
/

5. What is the difference between statement-level and row-level triggers?

Feature Statement-level Row-level
FOR EACH ROW ❌ No ✅ Yes
Fires per statement
Fires per row
:OLD available
:NEW available
Good for auditing individual rows Less suitable Good
Good for statement-level processing Good Less suitable

Example:

UPDATE employees
SET department_id = 20
WHERE department_id = 10;

If 100 rows are affected:

Statement trigger → 1 execution
Row trigger       → 100 executions

6. Can a statement-level trigger handle INSERT, UPDATE and DELETE?

Yes.

CREATE OR REPLACE TRIGGER trg_emp_changes
AFTER INSERT OR UPDATE OR DELETE ON employees
BEGIN

    IF INSERTING THEN
        DBMS_OUTPUT.PUT_LINE('INSERT statement executed');

    ELSIF UPDATING THEN
        DBMS_OUTPUT.PUT_LINE('UPDATE statement executed');

    ELSIF DELETING THEN
        DBMS_OUTPUT.PUT_LINE('DELETE statement executed');
    END IF;

END;
/

Notice that:

INSERTING
UPDATING
DELETING

can be used even though :OLD and :NEW cannot.

7. Can we use UPDATE OF with a statement-level trigger?

Yes.

CREATE OR REPLACE TRIGGER trg_salary_update
AFTER UPDATE OF salary ON employees
BEGIN
    DBMS_OUTPUT.PUT_LINE('Salary update statement executed');
END;
/

This trigger fires when salary is included in the UPDATE statement.

For example:

UPDATE employees
SET salary = salary + 1000;

fires the trigger once.

But:

UPDATE employees
SET department_id = 20;

does not fire it.

Important interview point:

UPDATE OF salary doesn't mean Oracle checks whether the value actually changed.

For example:

UPDATE employees
SET salary = salary;

can still cause the trigger to fire.

8. Can a statement-level trigger contain WHEN?

No.

The WHEN clause is associated with row-level triggers, because it evaluates values such as NEW and OLD for individual rows.

For example:

CREATE OR REPLACE TRIGGER trg_emp
AFTER UPDATE ON employees
FOR EACH ROW
WHEN (NEW.salary > 100000)
BEGIN
    ...
END;
/

This is row-level.

A statement-level trigger cannot use:

WHEN (NEW.salary > 100000)

because there is no individual NEW row.

9. Can a statement-level trigger query the same table?

Generally, yes.

This is an important difference from a row-level trigger.

Example:

CREATE OR REPLACE TRIGGER trg_emp_update
AFTER UPDATE ON employees
DECLARE
    v_count NUMBER;
BEGIN

    SELECT COUNT(*)
    INTO v_count
    FROM employees;

    DBMS_OUTPUT.PUT_LINE(
        'Employees = ' || v_count
    );

END;
/

A statement-level trigger isn't in the middle of processing an individual row, so the classic mutating-table problem associated with querying the triggering table from a row-level trigger does not apply in the same way.

10. What is the mutating-table difference?

This is a popular interview question.

Row-level trigger

CREATE OR REPLACE TRIGGER trg_emp
AFTER UPDATE ON employees
FOR EACH ROW
DECLARE
    v_count NUMBER;
BEGIN

    SELECT COUNT(*)
    INTO v_count
    FROM employees;

END;
/

This can result in:

ORA-04091: table EMPLOYEES is mutating

Statement-level trigger

CREATE OR REPLACE TRIGGER trg_emp
AFTER UPDATE ON employees
DECLARE
    v_count NUMBER;
BEGIN

    SELECT COUNT(*)
    INTO v_count
    FROM employees;

END;
/

This is generally allowed because the trigger executes at the statement level.

11. Can a statement-level trigger perform DML?

Yes.

For example, suppose you want to record that the EMPLOYEES table was modified.

CREATE TABLE employee_change_log (
    change_type VARCHAR2(20),
    changed_by  VARCHAR2(100),
    changed_on  TIMESTAMP
);

Trigger:

CREATE OR REPLACE TRIGGER trg_emp_change
AFTER INSERT OR UPDATE OR DELETE ON employees
BEGIN

    INSERT INTO employee_change_log
    VALUES (
        CASE
            WHEN INSERTING THEN 'INSERT'
            WHEN UPDATING  THEN 'UPDATE'
            WHEN DELETING  THEN 'DELETE'
        END,
        SYS_CONTEXT('USERENV', 'SESSION_USER'),
        SYSTIMESTAMP
    );

END;
/

If an update changes 500 rows, only one audit row is inserted.

That's exactly where a statement-level trigger can be useful.

12. Can a statement-level trigger modify :NEW?

No.

There is no :NEW in a statement-level trigger.

If you want to change a value before it is stored, use a row-level BEFORE trigger.

CREATE OR REPLACE TRIGGER trg_emp_upper
BEFORE INSERT ON employees
FOR EACH ROW
BEGIN
    :NEW.first_name := UPPER(:NEW.first_name);
END;
/

13. Can a statement-level trigger validate data?

Yes, but it is suited to statement-level validation, rather than individual-row validation.

For example, you might check a table-wide condition after an operation:

CREATE OR REPLACE TRIGGER trg_emp_check
AFTER UPDATE ON employees
DECLARE
    v_count NUMBER;
BEGIN

    SELECT COUNT(*)
    INTO v_count
    FROM employees
    WHERE salary < 0;

    IF v_count > 0 THEN
        RAISE_APPLICATION_ERROR(
            -20001,
            'Negative salary found'
        );
    END IF;

END;
/

For checking each incoming row individually, a row-level trigger is usually more appropriate.

14. Can a statement-level trigger fire for a statement that affects zero rows?

Yes.

This is a very important interview point.

Suppose:

UPDATE employees
SET salary = salary + 1000
WHERE employee_id = -9999;

If no rows match:

Rows affected = 0

A statement-level trigger associated with UPDATE can still fire because the UPDATE statement was executed.

A row-level trigger does not fire because there were no rows to process.

Remember

Statement-level:
SQL statement executed → trigger fires

Row-level:
Row affected → trigger fires

15. What happens with DELETE affecting zero rows?

Same principle.

DELETE FROM employees
WHERE employee_id = -9999;

If no rows are deleted:

Statement-level trigger → can fire
Row-level trigger       → doesn't fire

16. Can a statement-level trigger commit?

No.

This is invalid:

CREATE OR REPLACE TRIGGER trg_emp
AFTER UPDATE ON employees
BEGIN
    COMMIT;
END;
/

It results in an error such as:

ORA-04092: cannot COMMIT in a trigger

A trigger normally participates in the same transaction as the DML that fired it.

17. Can a statement-level trigger call a procedure?

Yes.

CREATE OR REPLACE TRIGGER trg_emp
AFTER UPDATE ON employees
BEGIN
    log_employee_update;
END;
/

Procedure:

CREATE OR REPLACE PROCEDURE log_employee_update
IS
BEGIN
    INSERT INTO employee_change_log
    VALUES (
        'UPDATE',
        USER,
        SYSTIMESTAMP
    );
END;
/

This can be useful when the trigger itself should remain small.

18. What is the firing order?

For a simple DML operation, conceptually:

BEFORE STATEMENT
       ↓
BEFORE EACH ROW
       ↓
AFTER EACH ROW
       ↓
AFTER STATEMENT

For example, with an update:

UPDATE employees
SET salary = salary + 1000;

Oracle can execute triggers in this general sequence:

BEFORE UPDATE statement trigger
          ↓
BEFORE UPDATE row trigger × N rows
          ↓
AFTER UPDATE row trigger × N rows
          ↓
AFTER UPDATE statement trigger

This is an extremely useful concept for interviews.

19. Example with all four trigger timings

Suppose an update affects 3 employees.

CREATE OR REPLACE TRIGGER trg_before_stmt
BEFORE UPDATE ON employees
BEGIN
    DBMS_OUTPUT.PUT_LINE('BEFORE STATEMENT');
END;
/

CREATE OR REPLACE TRIGGER trg_before_row
BEFORE UPDATE ON employees
FOR EACH ROW
BEGIN
    DBMS_OUTPUT.PUT_LINE('BEFORE ROW');
END;
/

CREATE OR REPLACE TRIGGER trg_after_row
AFTER UPDATE ON employees
FOR EACH ROW
BEGIN
    DBMS_OUTPUT.PUT_LINE('AFTER ROW');
END;
/

CREATE OR REPLACE TRIGGER trg_after_stmt
AFTER UPDATE ON employees
BEGIN
    DBMS_OUTPUT.PUT_LINE('AFTER STATEMENT');
END;
/

For:

UPDATE employees
SET salary = salary + 1000;

Output conceptually:

BEFORE STATEMENT

BEFORE ROW
AFTER ROW

BEFORE ROW
AFTER ROW

BEFORE ROW
AFTER ROW

AFTER STATEMENT

So:

Statement triggers → once
Row triggers       → once per affected row

20. What is a good real-world use case?

A very good use case is maintaining a statement-level change log.

CREATE TABLE employee_statement_log (
    operation      VARCHAR2(20),
    username       VARCHAR2(100),
    operation_time TIMESTAMP
);

Trigger:

CREATE OR REPLACE TRIGGER trg_emp_statement_log
AFTER INSERT OR UPDATE OR DELETE ON employees
BEGIN

    INSERT INTO employee_statement_log
    (
        operation,
        username,
        operation_time
    )
    VALUES
    (
        CASE
            WHEN INSERTING THEN 'INSERT'
            WHEN UPDATING  THEN 'UPDATE'
            WHEN DELETING  THEN 'DELETE'
        END,
        SYS_CONTEXT('USERENV', 'SESSION_USER'),
        SYSTIMESTAMP
    );

END;
/

Then:

UPDATE employees
SET salary = salary + 1000;

If 2,000 employees are updated:

2,000 rows changed
       ↓
1 UPDATE statement
       ↓
1 statement-level trigger execution
       ↓
1 log record

21. What are common statement-level trigger interview traps?

Trap 1

Question: Does a statement-level trigger fire once per row?

Answer: ❌ No.

It fires once per SQL statement.

Trap 2

Question: Can statement-level triggers use :OLD and :NEW?

Answer: ❌ No.

Those are row-level correlation variables.

Trap 3

Question: Does a statement-level trigger fire if zero rows are affected?

Answer: ✅ Yes, the trigger can fire because the DML statement was executed.

Trap 4

Question: Does a row-level trigger fire if zero rows are affected?

Answer: ❌ No.

There are no rows for the trigger to process.

Trap 5

Question: Can a statement-level trigger query its triggering table?

Answer: ✅ Yes, generally. The classic mutating-table restriction applies to row-level access to the table being modified.

Trap 6

Question: Can a statement-level trigger modify :NEW?

Answer: ❌ No.

There is no :NEW in a statement-level trigger.

22. Statement-level vs row-level: interview summary

Memorize this:

Feature Statement Level Row Level
FOR EACH ROW No Yes
Firing Once / statement Once / row
:OLD No Yes
:NEW No Yes
WHEN No Yes
Modify :NEW No Yes
(BEFORE trigger)
Zero rows Can fire Doesn't fire
Mutating table Generally okay Can be a problem
The simplest way to remember

Statement-level = "Something happened to the table."

Row-level = "This particular row changed."

For example:

UPDATE employees
SET salary = salary * 1.10;

If 10,000 rows are affected:

Statement trigger:  1 time
Row trigger:       10,000 times

Interview Preparation:

The key concept to understand is the difference between statement-level and row-level triggers. Once this is clear, the next important topics are compound triggers, mutating-table errors, trigger firing order, autonomous transactions, recursive triggers, and trigger execution behavior.
```

No comments:

Post a Comment