lesson 65 Database engineering · advanced
Triggers & visible audit effects
Observe how triggers record changes in an audit table.
the idea
A trigger runs statements automatically when a table changes. An AFTER UPDATE OF column trigger can record OLD and NEW values in an audit table, so every change leaves a trace. Use WHEN to log only real changes.
Dataset: Finance / Payments
The tables the challenges use, with their first 6 rows. Under each column name: the kind of value it holds, and which table it links to.
cards8 columns · 1,774 rows
| card_idnumber · unique id | account_idnumber · links to accounts | card_typetext | networktext | last4text | issued_atdate | expires_ondate | statustext |
|---|---|---|---|---|---|---|---|
| 1 | 1 | debit | visa | 1995 | 2024-09-06 | 2028-09-05 | active |
| 2 | 2 | debit | visa | 3252 | 2025-06-11 | 2029-06-10 | active |
| 3 | 3 | debit | visa | 1241 | 2024-07-13 | 2028-07-12 | active |
| 4 | 5 | credit | visa | 3044 | 2024-09-26 | 2028-09-25 | active |
| 5 | 6 | debit | mastercard | 9945 | 2023-07-18 | 2027-07-17 | active |
| 6 | 8 | debit | visa | 7443 | 2023-08-23 | 2027-08-22 | active |
- card_idnumber · unique id
- 1
- account_idnumber · links to accounts
- 1
- card_typetext
- debit
- networktext
- visa
- last4text
- 1995
- issued_atdate
- 2024-09-06
- expires_ondate
- 2028-09-05
- statustext
- active
- card_idnumber · unique id
- 2
- account_idnumber · links to accounts
- 2
- card_typetext
- debit
- networktext
- visa
- last4text
- 3252
- issued_atdate
- 2025-06-11
- expires_ondate
- 2029-06-10
- statustext
- active
- card_idnumber · unique id
- 3
- account_idnumber · links to accounts
- 3
- card_typetext
- debit
- networktext
- visa
- last4text
- 1241
- issued_atdate
- 2024-07-13
- expires_ondate
- 2028-07-12
- statustext
- active
- card_idnumber · unique id
- 4
- account_idnumber · links to accounts
- 5
- card_typetext
- credit
- networktext
- visa
- last4text
- 3044
- issued_atdate
- 2024-09-26
- expires_ondate
- 2028-09-25
- statustext
- active
- card_idnumber · unique id
- 5
- account_idnumber · links to accounts
- 6
- card_typetext
- debit
- networktext
- mastercard
- last4text
- 9945
- issued_atdate
- 2023-07-18
- expires_ondate
- 2027-07-17
- statustext
- active
- card_idnumber · unique id
- 6
- account_idnumber · links to accounts
- 8
- card_typetext
- debit
- networktext
- visa
- last4text
- 7443
- issued_atdate
- 2023-08-23
- expires_ondate
- 2027-08-22
- statustext
- active
first 6 of 1,774 rows · your queries use all of them
worked example
Run these steps in order to see the idea before you write your own query. The step marked “the pattern” is what the challenges ask for. Running the example does not count toward progress.
Count cards by status. Run this. Most cards are active; lost, expired, and frozen show up as smaller groups—context for the status changes your trigger will log.
step-1.sqlSELECT status, COUNT(*) AS cards FROM cards GROUP BY status;
Log status changes with a trigger. The AFTER UPDATE trigger inserts into status_log when you freeze card 1—you get an audit row without writing a separate INSERT yourself.the pattern
example.sqlCREATE TABLE status_log (card_id INTEGER, changed_to TEXT); CREATE TRIGGER log_status AFTER UPDATE OF status ON cards BEGIN INSERT INTO status_log VALUES (NEW.card_id, NEW.status); END; UPDATE cards SET status = 'frozen' WHERE card_id = 1; SELECT * FROM status_log;
common mistake
A trigger fires once per affected row; an UPDATE touching 50 rows writes 50 audit rows.