lesson 57 Advanced product analysis · advanced
Usage cost & contribution margin
Compare usage revenue with costs to calculate contribution margin.
the idea
Contribution margin is revenue minus the variable cost of serving it. Revenue comes from cash collected (invoice payments net of refunds) in the period; usage cost is each logical event multiplied by its unit cost. Compute both at the same grain, then subtract.
Dataset: Product / SaaS Analytics
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.
events8 columns · 135,428 rows
| delivery_idnumber · unique id | event_idtext | user_idnumber · links to users | account_idnumber · links to accounts | session_idnumber · links to sessions | event_nametext | occurred_atdate & time | received_atdate & time |
|---|---|---|---|---|---|---|---|
| 1 | evt_0000001 | 1 | 1 | 1 | user_signed_up | 2024-01-14 18:52:14 | 2024-01-14 18:52:33 |
| 2 | evt_0000002 | 1 | 1 | 1 | onboarding_started | 2024-01-14 18:52:51 | 2024-01-14 18:53:06 |
| 3 | evt_0000084 | 6 | 2 | 32 | user_signed_up | 2024-01-15 13:21:52 | 2024-01-15 13:22:00 |
| 4 | evt_0000085 | 6 | 2 | 32 | onboarding_started | 2024-01-15 13:22:05 | 2024-01-15 13:22:16 |
| 5 | evt_0000086 | 6 | 2 | 32 | onboarding_completed | 2024-01-15 13:23:52 | 2024-01-15 13:24:12 |
| 6 | evt_0000019 | 1 | 1 | 9 | search_performed | 2024-01-17 20:56:55 | 2024-01-17 20:56:59 |
- delivery_idnumber · unique id
- 1
- event_idtext
- evt_0000001
- user_idnumber · links to users
- 1
- account_idnumber · links to accounts
- 1
- session_idnumber · links to sessions
- 1
- event_nametext
- user_signed_up
- occurred_atdate & time
- 2024-01-14 18:52:14
- received_atdate & time
- 2024-01-14 18:52:33
- delivery_idnumber · unique id
- 2
- event_idtext
- evt_0000002
- user_idnumber · links to users
- 1
- account_idnumber · links to accounts
- 1
- session_idnumber · links to sessions
- 1
- event_nametext
- onboarding_started
- occurred_atdate & time
- 2024-01-14 18:52:51
- received_atdate & time
- 2024-01-14 18:53:06
- delivery_idnumber · unique id
- 3
- event_idtext
- evt_0000084
- user_idnumber · links to users
- 6
- account_idnumber · links to accounts
- 2
- session_idnumber · links to sessions
- 32
- event_nametext
- user_signed_up
- occurred_atdate & time
- 2024-01-15 13:21:52
- received_atdate & time
- 2024-01-15 13:22:00
- delivery_idnumber · unique id
- 4
- event_idtext
- evt_0000085
- user_idnumber · links to users
- 6
- account_idnumber · links to accounts
- 2
- session_idnumber · links to sessions
- 32
- event_nametext
- onboarding_started
- occurred_atdate & time
- 2024-01-15 13:22:05
- received_atdate & time
- 2024-01-15 13:22:16
- delivery_idnumber · unique id
- 5
- event_idtext
- evt_0000086
- user_idnumber · links to users
- 6
- account_idnumber · links to accounts
- 2
- session_idnumber · links to sessions
- 32
- event_nametext
- onboarding_completed
- occurred_atdate & time
- 2024-01-15 13:23:52
- received_atdate & time
- 2024-01-15 13:24:12
- delivery_idnumber · unique id
- 6
- event_idtext
- evt_0000019
- user_idnumber · links to users
- 1
- account_idnumber · links to accounts
- 1
- session_idnumber · links to sessions
- 9
- event_nametext
- search_performed
- occurred_atdate & time
- 2024-01-17 20:56:55
- received_atdate & time
- 2024-01-17 20:56:59
first 6 of 135,428 rows · your queries use all of them
event_costs2 columns · 8 rows
| event_nametext · unique id | cost_microdollarsnumber |
|---|---|
| dashboard_viewed | 900 |
| search_performed | 1500 |
| filter_applied | 400 |
| report_created | 12000 |
| report_shared | 2500 |
| data_exported | 30000 |
- event_nametext · unique id
- dashboard_viewed
- cost_microdollarsnumber
- 900
- event_nametext · unique id
- search_performed
- cost_microdollarsnumber
- 1500
- event_nametext · unique id
- filter_applied
- cost_microdollarsnumber
- 400
- event_nametext · unique id
- report_created
- cost_microdollarsnumber
- 12000
- event_nametext · unique id
- report_shared
- cost_microdollarsnumber
- 2500
- event_nametext · unique id
- data_exported
- cost_microdollarsnumber
- 30000
first 6 of 8 rows · your queries use all of them
billing_events6 columns · 4,193 rows
| billing_event_idnumber · unique id | account_idnumber · links to accounts | subscription_idnumber · links to subscriptions | event_typetext | occurred_atdate & time | amount_centsnumber |
|---|---|---|---|---|---|
| 1 | 5 | 8 | invoice_paid | 2024-02-01 12:13:43 | 2900 |
| 2 | 29 | 51 | invoice_paid | 2024-03-01 20:37:15 | 2900 |
| 3 | 5 | 8 | invoice_paid | 2024-03-02 11:47:43 | 2900 |
| 4 | 43 | 80 | invoice_paid | 2024-03-08 15:54:29 | 99000 |
| 5 | 7 | 11 | invoice_paid | 2024-03-09 01:45:58 | 99000 |
| 6 | 11 | 17 | invoice_paid | 2024-03-20 10:01:01 | 99000 |
- billing_event_idnumber · unique id
- 1
- account_idnumber · links to accounts
- 5
- subscription_idnumber · links to subscriptions
- 8
- event_typetext
- invoice_paid
- occurred_atdate & time
- 2024-02-01 12:13:43
- amount_centsnumber
- 2900
- billing_event_idnumber · unique id
- 2
- account_idnumber · links to accounts
- 29
- subscription_idnumber · links to subscriptions
- 51
- event_typetext
- invoice_paid
- occurred_atdate & time
- 2024-03-01 20:37:15
- amount_centsnumber
- 2900
- billing_event_idnumber · unique id
- 3
- account_idnumber · links to accounts
- 5
- subscription_idnumber · links to subscriptions
- 8
- event_typetext
- invoice_paid
- occurred_atdate & time
- 2024-03-02 11:47:43
- amount_centsnumber
- 2900
- billing_event_idnumber · unique id
- 4
- account_idnumber · links to accounts
- 43
- subscription_idnumber · links to subscriptions
- 80
- event_typetext
- invoice_paid
- occurred_atdate & time
- 2024-03-08 15:54:29
- amount_centsnumber
- 99000
- billing_event_idnumber · unique id
- 5
- account_idnumber · links to accounts
- 7
- subscription_idnumber · links to subscriptions
- 11
- event_typetext
- invoice_paid
- occurred_atdate & time
- 2024-03-09 01:45:58
- amount_centsnumber
- 99000
- billing_event_idnumber · unique id
- 6
- account_idnumber · links to accounts
- 11
- subscription_idnumber · links to subscriptions
- 17
- event_typetext
- invoice_paid
- occurred_atdate & time
- 2024-03-20 10:01:01
- amount_centsnumber
- 99000
first 6 of 4,193 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.
Read the unit cost table. Run this. Each event_name maps to a cost in microdollars—the variable cost you multiply by logical event counts.the pattern
example.sqlSELECT event_name, cost_microdollars FROM event_costs ORDER BY cost_microdollars DESC;
See May billing cash. Run this. invoice_paid and refund_issued rows show money collected in May 2025—the revenue side of margin.
step-2.sqlSELECT event_type, COUNT(*) AS events, SUM(amount_cents) / 100.0 AS usd FROM billing_events WHERE occurred_at >= '2025-05-01' AND occurred_at < '2025-06-01' GROUP BY event_type;
common mistake
Multiplying raw deliveries by unit cost charges retries twice; deduplicate events before costing them.