lesson 56 Advanced product analysis · advanced
Revenue & payment reconciliation
Reconcile revenue with payments and identify differences.
the idea
Reconciliation compares two records of the same money. Bring each side to the same grain (one row per settlement), join them, and classify every row: matched, missing on one side, or different amounts. Unmatched rows are findings, not errors to filter away.
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.
settlements4 columns · 37,071 rows
| settlement_idnumber · unique id | authorization_idnumber · links to authorizations | settled_atdate & time | amount_centsnumber |
|---|---|---|---|
| 1 | 3 | 2025-01-23 01:12:53 | 7655 |
| 2 | 4 | 2025-01-30 13:04:51 | 6852 |
| 3 | 5 | 2025-01-30 17:41:21 | 5771 |
| 4 | 6 | 2025-02-11 06:57:34 | 3717 |
| 5 | 7 | 2025-02-11 19:25:53 | 4401 |
| 6 | 8 | 2025-02-20 03:08:18 | 9288 |
- settlement_idnumber · unique id
- 1
- authorization_idnumber · links to authorizations
- 3
- settled_atdate & time
- 2025-01-23 01:12:53
- amount_centsnumber
- 7655
- settlement_idnumber · unique id
- 2
- authorization_idnumber · links to authorizations
- 4
- settled_atdate & time
- 2025-01-30 13:04:51
- amount_centsnumber
- 6852
- settlement_idnumber · unique id
- 3
- authorization_idnumber · links to authorizations
- 5
- settled_atdate & time
- 2025-01-30 17:41:21
- amount_centsnumber
- 5771
- settlement_idnumber · unique id
- 4
- authorization_idnumber · links to authorizations
- 6
- settled_atdate & time
- 2025-02-11 06:57:34
- amount_centsnumber
- 3717
- settlement_idnumber · unique id
- 5
- authorization_idnumber · links to authorizations
- 7
- settled_atdate & time
- 2025-02-11 19:25:53
- amount_centsnumber
- 4401
- settlement_idnumber · unique id
- 6
- authorization_idnumber · links to authorizations
- 8
- settled_atdate & time
- 2025-02-20 03:08:18
- amount_centsnumber
- 9288
first 6 of 37,071 rows · your queries use all of them
refunds5 columns · 1,230 rows
| refund_idnumber · unique id | settlement_idnumber · links to settlements | refunded_atdate & time | amount_centsnumber | reasontext |
|---|---|---|---|---|
| 1 | 25 | 2025-02-12 02:56:32 | 2820 | returned_item |
| 2 | 27 | 2025-03-01 03:02:52 | 11883 | returned_item |
| 3 | 268 | 2025-03-01 07:28:00 | 367 | canceled_service |
| 4 | 313 | 2025-05-30 14:18:37 | 1601 | returned_item |
| 5 | 313 | 2025-06-15 14:18:37 | 2440 | canceled_service |
| 6 | 348 | 2025-05-11 19:21:17 | 19843 | price_adjustment |
- refund_idnumber · unique id
- 1
- settlement_idnumber · links to settlements
- 25
- refunded_atdate & time
- 2025-02-12 02:56:32
- amount_centsnumber
- 2820
- reasontext
- returned_item
- refund_idnumber · unique id
- 2
- settlement_idnumber · links to settlements
- 27
- refunded_atdate & time
- 2025-03-01 03:02:52
- amount_centsnumber
- 11883
- reasontext
- returned_item
- refund_idnumber · unique id
- 3
- settlement_idnumber · links to settlements
- 268
- refunded_atdate & time
- 2025-03-01 07:28:00
- amount_centsnumber
- 367
- reasontext
- canceled_service
- refund_idnumber · unique id
- 4
- settlement_idnumber · links to settlements
- 313
- refunded_atdate & time
- 2025-05-30 14:18:37
- amount_centsnumber
- 1601
- reasontext
- returned_item
- refund_idnumber · unique id
- 5
- settlement_idnumber · links to settlements
- 313
- refunded_atdate & time
- 2025-06-15 14:18:37
- amount_centsnumber
- 2440
- reasontext
- canceled_service
- refund_idnumber · unique id
- 6
- settlement_idnumber · links to settlements
- 348
- refunded_atdate & time
- 2025-05-11 19:21:17
- amount_centsnumber
- 19843
- reasontext
- price_adjustment
first 6 of 1,230 rows · your queries use all of them
transactions12 columns · 84,798 rows
| transaction_idnumber · unique id | account_idnumber · links to accounts | transaction_typetext | statustext | amount_centsnumber | initiated_atdate & time | posted_atdate & time | descriptiontext | merchant_idnumber · links to merchants | authorization_idnumber · links to authorizations | refund_idnumber · links to refunds | chargeback_idnumber · links to chargebacks |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 103 | card_purchase | posted | -1399 | 2025-01-01 04:00:00 | 2025-01-02 06:23:00 | TRUE STREAMING #711 | 959 | 1980 | NULL | NULL |
| 2 | 377 | card_purchase | posted | -3999 | 2025-01-01 04:00:00 | 2025-01-03 10:26:00 | VISTA GYM #196 | 965 | 6710 | NULL | NULL |
| 3 | 442 | card_purchase | posted | -699 | 2025-01-01 04:00:00 | 2025-01-03 04:21:00 | WILLOW STREAMING #817 | 955 | 7848 | NULL | NULL |
| 4 | 447 | card_purchase | posted | -4999 | 2025-01-01 04:00:00 | 2025-01-02 12:31:00 | OAK GYM #964 | 969 | 7931 | NULL | NULL |
| 5 | 450 | card_purchase | posted | -2499 | 2025-01-01 04:00:00 | 2025-01-02 08:01:00 | BIRCH GYM #498 | 968 | 8096 | NULL | NULL |
| 6 | 497 | card_purchase | posted | -899 | 2025-01-01 04:00:00 | 2025-01-02 12:11:00 | VERDE STREAMING #385 | 958 | 8870 | NULL | NULL |
- transaction_idnumber · unique id
- 1
- account_idnumber · links to accounts
- 103
- transaction_typetext
- card_purchase
- statustext
- posted
- amount_centsnumber
- -1399
- initiated_atdate & time
- 2025-01-01 04:00:00
- posted_atdate & time
- 2025-01-02 06:23:00
- descriptiontext
- TRUE STREAMING #711
- merchant_idnumber · links to merchants
- 959
- authorization_idnumber · links to authorizations
- 1980
- refund_idnumber · links to refunds
- NULL
- chargeback_idnumber · links to chargebacks
- NULL
- transaction_idnumber · unique id
- 2
- account_idnumber · links to accounts
- 377
- transaction_typetext
- card_purchase
- statustext
- posted
- amount_centsnumber
- -3999
- initiated_atdate & time
- 2025-01-01 04:00:00
- posted_atdate & time
- 2025-01-03 10:26:00
- descriptiontext
- VISTA GYM #196
- merchant_idnumber · links to merchants
- 965
- authorization_idnumber · links to authorizations
- 6710
- refund_idnumber · links to refunds
- NULL
- chargeback_idnumber · links to chargebacks
- NULL
- transaction_idnumber · unique id
- 3
- account_idnumber · links to accounts
- 442
- transaction_typetext
- card_purchase
- statustext
- posted
- amount_centsnumber
- -699
- initiated_atdate & time
- 2025-01-01 04:00:00
- posted_atdate & time
- 2025-01-03 04:21:00
- descriptiontext
- WILLOW STREAMING #817
- merchant_idnumber · links to merchants
- 955
- authorization_idnumber · links to authorizations
- 7848
- refund_idnumber · links to refunds
- NULL
- chargeback_idnumber · links to chargebacks
- NULL
- transaction_idnumber · unique id
- 4
- account_idnumber · links to accounts
- 447
- transaction_typetext
- card_purchase
- statustext
- posted
- amount_centsnumber
- -4999
- initiated_atdate & time
- 2025-01-01 04:00:00
- posted_atdate & time
- 2025-01-02 12:31:00
- descriptiontext
- OAK GYM #964
- merchant_idnumber · links to merchants
- 969
- authorization_idnumber · links to authorizations
- 7931
- refund_idnumber · links to refunds
- NULL
- chargeback_idnumber · links to chargebacks
- NULL
- transaction_idnumber · unique id
- 5
- account_idnumber · links to accounts
- 450
- transaction_typetext
- card_purchase
- statustext
- posted
- amount_centsnumber
- -2499
- initiated_atdate & time
- 2025-01-01 04:00:00
- posted_atdate & time
- 2025-01-02 08:01:00
- descriptiontext
- BIRCH GYM #498
- merchant_idnumber · links to merchants
- 968
- authorization_idnumber · links to authorizations
- 8096
- refund_idnumber · links to refunds
- NULL
- chargeback_idnumber · links to chargebacks
- NULL
- transaction_idnumber · unique id
- 6
- account_idnumber · links to accounts
- 497
- transaction_typetext
- card_purchase
- statustext
- posted
- amount_centsnumber
- -899
- initiated_atdate & time
- 2025-01-01 04:00:00
- posted_atdate & time
- 2025-01-02 12:11:00
- descriptiontext
- VERDE STREAMING #385
- merchant_idnumber · links to merchants
- 958
- authorization_idnumber · links to authorizations
- 8870
- refund_idnumber · links to refunds
- NULL
- chargeback_idnumber · links to chargebacks
- NULL
first 6 of 84,798 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.
Summarize March ledger activity. Run this. Posted card purchases and other types in March 2025—the bank side of reconciliation.the pattern
example.sqlSELECT transaction_type, status, COUNT(*) AS entries, SUM(amount_cents) AS cents FROM transactions WHERE posted_at >= '2025-03-01' AND posted_at < '2025-04-01' GROUP BY transaction_type, status;
Line up settlement and posting. Run this. Each settlement ties to card_purchase rows through authorization_id so you can compare amounts row by row.
step-2.sqlSELECT s.settlement_id, s.amount_cents, t.transaction_id, t.amount_cents AS ledger_cents FROM settlements s LEFT JOIN transactions t ON t.authorization_id = s.authorization_id AND t.transaction_type = 'card_purchase' WHERE s.settlement_id <= 5;
Add partial refunds per charge. Run this. Several refund rows can share one settlement_id—sum them before you net against settled cents.
step-3.sqlSELECT settlement_id, COUNT(*) AS refunds, SUM(amount_cents) AS refunded_cents FROM refunds WHERE settlement_id <= 300 GROUP BY settlement_id;
Compare month totals. Run this. settled_cents from the card processor should line up with negated posted card_purchase cents when nothing is missing.
step-4.sqlSELECT ( SELECT SUM(amount_cents) FROM settlements WHERE settled_at >= '2025-02-01' AND settled_at < '2025-03-01' ) AS settled_cents, ( SELECT -SUM(amount_cents) FROM transactions WHERE transaction_type = 'card_purchase' AND status = 'posted' AND posted_at >= '2025-02-01' AND posted_at < '2025-03-01' ) AS ledger_cents;
common mistake
An inner join between settlements and ledger postings silently drops the settlements that never posted, which are exactly the problems to find.