SQL writes the receipt for every fix.
The whole audit story is one empty log table plus a trigger listening for one change. The feature ends there.
OLD and NEWPseudo-rows hold both versions of the record inside the trigger body.
WHEN clauseFilters out no-op UPDATEs so the audit table stays signal, not noise.
CURRENT_TIMESTAMPThe when of each change comes free from the database.
-- the log table
CREATE TABLE nutrition_audit (
audit_id INTEGER PRIMARY KEY AUTOINCREMENT,
product_id INTEGER NOT NULL,
old_value REAL,
new_value REAL,
changed_at TEXT DEFAULT CURRENT_TIMESTAMP
);
-- the trigger
CREATE TRIGGER trg_audit_sodium
AFTER UPDATE OF sodium_mg ON product_nutrition
FOR EACH ROW
WHEN OLD.sodium_mg IS NOT NEW.sodium_mg
BEGIN
INSERT INTO nutrition_audit
(product_id, old_value, new_value)
VALUES
(OLD.product_id, OLD.sodium_mg, NEW.sodium_mg);
END;