Working with Database Triggers

1. Creating Triggers

ComponentDetail
EventINSERT / UPDATE / DELETE / TRUNCATE
TimingBEFORE / AFTER / INSTEAD OF
GranularityFOR EACH ROW / FOR EACH STATEMENT
ConditionWHEN (expr)
BodyCalls a trigger function (PG) or inline code

Example: Audit Trigger (PG)

CREATE FUNCTION audit_orders() RETURNS TRIGGER LANGUAGE plpgsql AS $
BEGIN
  INSERT INTO order_audit(order_id, op, changed_at, changed_by)
  VALUES (COALESCE(NEW.id, OLD.id), TG_OP, NOW(), current_user);
  RETURN COALESCE(NEW, OLD);
END;
$;
CREATE TRIGGER trg_audit_orders
AFTER INSERT OR UPDATE OR DELETE ON orders
FOR EACH ROW EXECUTE FUNCTION audit_orders();

2. Understanding Trigger Types

TypeUse
BEFORE rowModify NEW before write, validation
AFTER rowAudit, propagation
BEFORE/AFTER statementSet-level logic, batch counters
INSTEAD OFMake views writable
DDL triggersSchema-change auditing
Event triggers (PG)Fire on DDL events

3. Implementing Row-Level Triggers

VariableMeaning
NEWNew row (INSERT/UPDATE)
OLDOld row (UPDATE/DELETE)
TG_OP'INSERT' / 'UPDATE' / 'DELETE'
TG_TABLE_NAMESource table

4. Implementing Statement-Level Triggers

AspectDetail
Fire once per statementRegardless of row count
Transition tables (PG/SQL Server)REFERENCING NEW TABLE AS ... — set of affected rows
UseBulk counters, batch logging

5. Using Trigger Events

EventDetail
INSERTOLD is NULL
UPDATE [OF col]Optional column filter
DELETENEW is NULL
TRUNCATEStatement-level only

6. Accessing Old and New Values

PatternUse
NEW.col := ...Mutate in BEFORE row trigger
IF NEW.col IS DISTINCT FROM OLD.colDetect column changes
RAISE EXCEPTIONBlock invalid changes
RETURN NULL (PG)Skip operation (BEFORE row)

7. Implementing Audit Trails with Triggers

ApproachDetail
Audit table per sourceMirror columns + meta (op, ts, user)
Generic JSON auditStore row_to_json(OLD/NEW)
Temporal tables (SQL Server, MariaDB)Built-in history
CDC (Debezium)External stream — avoids trigger overhead

8. Cascading Trigger Execution

ConcernDetail
Trigger on table A firesModifies table B → trigger on B fires
RecursionSame trigger refires on its own writes — guard with column checks
Max depth (SQL Server)32 nested levels
Disable nestingSQL Server: sp_configure 'nested triggers',0

9. Disabling and Enabling Triggers

DBCommand
PostgresALTER TABLE t DISABLE TRIGGER name; / ENABLE
MySQLDrop & recreate (no native disable)
SQL ServerDISABLE TRIGGER ALL ON t;
OracleALTER TRIGGER name DISABLE;

10. Managing Trigger Performance Impact

TipDetail
Avoid heavy logicTriggers add cost to every write
Prefer set-basedUse statement triggers with transition tables
No external callsHTTP / FS calls block writes
Push to asyncNOTIFY + worker, or CDC
MonitorTrack tx duration before/after deploy