runtime
65 / 88

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 idaccount_idnumber · links to accountscard_typetextnetworktextlast4textissued_atdateexpires_ondatestatustext
11debitvisa19952024-09-062028-09-05active
22debitvisa32522025-06-112029-06-10active
33debitvisa12412024-07-132028-07-12active
45creditvisa30442024-09-262028-09-25active
56debitmastercard99452023-07-182027-07-17active
68debitvisa74432023-08-232027-08-22active
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.

  1. 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.sql
    SELECT status, COUNT(*) AS cards
    FROM cards
    GROUP BY status;
  2. 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.sql
    CREATE 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.

challenge 1

Create card_audit(card_id INTEGER, old_status TEXT, new_status TEXT) and trigger card_status_audit: AFTER UPDATE OF status ON cards, logging only real changes.

Set card 1’s status to 'frozen'.

Return card_id, old_status and new_status from card_audit.

what to return

Columns
card_idold_statusnew_status in this order
Rows
any order
Also checked
TEMP / table changes from your script
challenge-1.sql
 
To master this lesson, solve both challenges without hints and pass the concept check. 3 of 3 to go.
Triggers & visible audit effects — Database engineering · Runtime