Queries · q299 · Head of Data · week 2, night 1
“At the portfolio level, how much of the gap between order value and money collected is just timing (recent orders not yet settled) versus a permanent shortfall that age doesn't fix?”
Fictional people at a fictional company, each played by a language-model agent. Who they are and what they are told
Week 1; shared with Tobias Lindqvist, Chief Financial Officer
Before the board pack goes out, Data must certify the key metrics. Do revenue, orders, customers, refunds and payouts agree wherever they are recorded, and where they do not, why not and by how much?
The manager, at the start of week 2
Subject: Good work — a few things I need pinned down before we certify
Yuki,
This is exactly the kind of digging I wanted — especially catching that invoices weren't a real fifth number, and that your first "20-40% gap" read was wrong and you went back to check it. That's the instinct I want on this team.
Three things surprised me and need explaining before I can tell Tobias we're ready:
Questions for this week:
Keep the FX-adjusted view for USD/NOK on your list — that one's solid and the board will ask about it.
Thanks for the pace you're keeping on this.
Rosa
The analyst · week 2, night 1, request 1 of 3
Rosa's three questions this week are each really "is this timing, is this concentrated, and is this already solved elsewhere" - all testable with the right grain. (1) For the portfolio-level revenue gap, the order-level work showed <1-5% residual gap once refunded/partially_refunded payments are included, but that used orders aged 6+ months - I never actually tested whether that residual shrinks with age (timing) or is flat from day one (structural). An age-bucket grain since placed_at will show the ramp-up curve directly: if match rate keeps climbing past 90-180 days it's timing; if it plateaus early at e.g. 95-98% and stays there, that plateau is the real structural ceiling. (2) For refund/payout traceability, Rosa wants concentration AND trend together - I can get both from one grain (tenant) by comparing H1 vs H2 2025 late-refund euros per tenant, which also directly tests whether the problem is spreading or shrinking. (3) For the B2B sizing question, rather than rebuilding my fuzzy industry-text merchant list, I'm pulling revenue by industry vertical directly - this settles both "how big is B2B wholesale" and incidentally double-checks my prior ad hoc filter against the real industry categories. I'm holding the customer-definition-usage question (which other systems already use "ever purchased" vs "last 12 months") for tomorrow night, since it likely means digging into segments/cohorts/CRM definitions rather than a straightforward aggregate query, and I'd rather scope that precisely after seeing tonight's results.
The engineer · 1 dry run on the empty schema, then the submission
WITH order_base AS ( SELECT o.id AS order_id, o.grand_total, o.currency_code, o.placed_at, (DATE '2025-12-31' - CAST(o.placed_at AS date)) AS age_days, CASE WHEN (DATE '2025-12-31' - CAST(o.placed_at AS date)) <= 3 THEN '0-3 days' WHEN (DATE '2025-12-31' - CAST(o.placed_at AS date)) <= 7 THEN '4-7 days' WHEN (DATE '2025-12-31' - CAST(o.placed_at AS date)) <= 14 THEN '8-14 days' WHEN (DATE '2025-12-31' - CAST(o.placed_at AS date)) <= 30 THEN '15-30 days' WHEN (DATE '2025-12-31' - CAST(o.placed_at AS date)) <= 60 THEN '31-60 days' WHEN (DATE '2025-12-31' - CAST(o.placed_at AS date)) <= 90 THEN '61-90 days' WHEN (DATE '2025-12-31' - CAST(o.placed_at AS date)) <= 180 THEN '91-180 days' WHEN (DATE '2025-12-31' - CAST(o.placed_at AS date)) <= 365 THEN '181-365 days' ELSE '366+ days' END AS age_bucket FROM orders o WHERE o.status <> 'cancelled' AND o.placed_at >= TIMESTAMP '2023-01-01' AND o.placed_at < TIMESTAMP '2026-01-01' ), order_count_agg AS ( SELECT t0.age_bucket, COUNT(*) AS order_count FROM order_base t0 GROUP BY t0.age_bucket ), grand_total_agg AS ( SELECT t1.age_bucket, SUM(t1.grand_total / t2.rate) AS orders_grand_total_eur FROM order_base t1 JOIN exchange_rates t2 ON t2.base_code = 'EUR' AND t2.quote_code = t1.currency_code AND t2.as_of = CAST(t1.placed_at AS date) GROUP BY t1.age_bucket ), payment_agg AS ( SELECT t3.age_bucket, SUM(t4.amount / t5.rate) AS matched_money_collected_eur FROM order_base t3 JOIN payments t4 ON t4.order_id = t3.order_id AND t4.status IN ('captured', 'refunded', 'partially_refunded') JOIN exchange_rates t5 ON t5.base_code = 'EUR' AND t5.quote_code = t4.currency_code AND t5.as_of = CAST(t4.captured_at AS date) GROUP BY t3.age_bucket ) SELECT t6.age_bucket, t6.order_count, ROUND(COALESCE(t7.orders_grand_total_eur, 0), 2) AS orders_grand_total_eur, ROUND(COALESCE(t8.matched_money_collected_eur, 0), 2) AS matched_money_collected_eur, ROUND(COALESCE(t8.matched_money_collected_eur, 0) / NULLIF(t7.orders_grand_total_eur, 0) * 100, 1) AS match_rate_pct FROM order_count_agg t6 LEFT JOIN grand_total_agg t7 ON t7.age_bucket = t6.age_bucket LEFT JOIN payment_agg t8 ON t8.age_bucket = t6.age_bucket ORDER BY CASE t6.age_bucket WHEN '0-3 days' THEN 1 WHEN '4-7 days' THEN 2 WHEN '8-14 days' THEN 3 WHEN '15-30 days' THEN 4 WHEN '31-60 days' THEN 5 WHEN '61-90 days' THEN 6 WHEN '91-180 days' THEN 7 WHEN '181-365 days' THEN 8 ELSE 9 END
“Query compiles and returns the expected columns. Submitting.”
“Submitted. The query buckets non-cancelled orders placed between 2023-01-01 and 2025-12-31 by age as of 2025-12-31, converts both order grand totals and matched payment amounts (captured/refunded/partially_refunded) to EUR using the same-day exchange rate, and reports count, both EUR totals, and the match rate per bucket — ordered youngest to oldest as requested.”
As Yuki Tanaka saw it, from the small dataset the agents worked on
age_bucket | order_count | orders_grand_total_eur | matched_money_collected_eur | match_rate_pct -------------+-------------+------------------------+-----------------------------+--------------- 0-3 days | 348 | 34188.23 | 34188.23 | 100.0 4-7 days | 298 | 23198.54 | 23198.54 | 100.0 8-14 days | 594 | 58990.07 | 49213.08 | 83.4 15-30 days | 1754 | 216605.73 | 195981.86 | 90.5 31-60 days | 2550 | 328682.7 | 308288.1 | 93.8 61-90 days | 1777 | 228982.28 | 218550.72 | 95.4 91-180 days | 4645 | 524129.52 | 524439.53 | 100.1 181-365 days | 9556 | 1299647.47 | 1293594.88 | 99.5 366+ days | 27805 | 3711376.17 | 3669424.92 | 98.9 (9 rows)
The memo and the queries that followed; links found by title, so inferred
Later queries that build on this report:
It builds on:
Changed only where the benchmark needs the same result on every system
WITH order_base AS ( SELECT o.id AS order_id, o.grand_total, o.currency_code, o.placed_at, o.grand_total AS grand_total, o.currency_code AS currency_code, o.placed_at AS placed_at, (DATE '2025-12-31' - CAST(o.placed_at AS date)) AS age_days, CASE WHEN (DATE '2025-12-31' - CAST(o.placed_at AS date)) <= 3 THEN '0-3 days' WHEN (DATE '2025-12-31' - CAST(o.placed_at AS date)) <= 7 THEN '4-7 days' WHEN (DATE '2025-12-31' - CAST(o.placed_at AS date)) <= 14 THEN '8-14 days' WHEN (DATE '2025-12-31' - CAST(o.placed_at AS date)) <= 30 THEN '15-30 days' WHEN (DATE '2025-12-31' - CAST(o.placed_at AS date)) <= 60 THEN '31-60 days' WHEN (DATE '2025-12-31' - CAST(o.placed_at AS date)) <= 90 THEN '61-90 days' WHEN (DATE '2025-12-31' - CAST(o.placed_at AS date)) <= 180 THEN '91-180 days' WHEN (DATE '2025-12-31' - CAST(o.placed_at AS date)) <= 365 THEN '181-365 days' ELSE '366+ days' END AS age_bucket FROM orders o WHERE o.status <> 'cancelled' AND o.placed_at >= TIMESTAMP '2023-01-01' AND o.placed_at < TIMESTAMP '2026-01-01'),order_count_agg AS ( SELECT t0.age_bucket, COUNT(*) AS order_count SELECT t0.age_bucket AS age_bucket, COUNT(*) AS order_count FROM order_base t0 GROUP BY t0.age_bucket),grand_total_agg AS ( SELECT t1.age_bucket, SUM(t1.grand_total / t2.rate) AS orders_grand_total_eur SELECT t1.age_bucket AS age_bucket, SUM(CAST(t1.grand_total / t2.rate AS NUMERIC(18,6))) AS orders_grand_total_eur FROM order_base t1 JOIN exchange_rates t2 ON t2.base_code = 'EUR' AND t2.quote_code = t1.currency_code AND t2.as_of = CAST(t1.placed_at AS date) GROUP BY t1.age_bucket),payment_agg AS ( SELECT t3.age_bucket, SUM(t4.amount / t5.rate) AS matched_money_collected_eur SELECT t3.age_bucket AS age_bucket, SUM(CAST(t4.amount / t5.rate AS NUMERIC(18,6))) AS matched_money_collected_eur FROM order_base t3 JOIN payments t4 ON t4.order_id = t3.order_id AND t4.status IN ('captured', 'refunded', 'partially_refunded') JOIN exchange_rates t5 ON t5.base_code = 'EUR' AND t5.quote_code = t4.currency_code AND t5.as_of = CAST(t4.captured_at AS date) GROUP BY t3.age_bucket)SELECT t6.age_bucket, t6.order_count, ROUND(COALESCE(t7.orders_grand_total_eur, 0), 2) AS orders_grand_total_eur, ROUND(COALESCE(t8.matched_money_collected_eur, 0), 2) AS matched_money_collected_eur, ROUND(COALESCE(t8.matched_money_collected_eur, 0) / NULLIF(t7.orders_grand_total_eur, 0) * 100, 1) AS match_rate_pct t6.age_bucket AS age_bucket, t6.order_count AS order_count, COALESCE(t7.orders_grand_total_eur, 0) AS orders_grand_total_eur, COALESCE(t8.matched_money_collected_eur, 0) AS matched_money_collected_eur, COALESCE(t8.matched_money_collected_eur, 0) / NULLIF(t7.orders_grand_total_eur, 0) * 100 AS match_rate_pctFROM order_count_agg t6LEFT JOIN grand_total_agg t7 ON t7.age_bucket = t6.age_bucketLEFT JOIN payment_agg t8 ON t8.age_bucket = t6.age_bucketORDER BY CASE t6.age_bucket WHEN '0-3 days' THEN 1 WHEN '4-7 days' THEN 2 WHEN '8-14 days' THEN 3 WHEN '15-30 days' THEN 4 WHEN '31-60 days' THEN 5 WHEN '61-90 days' THEN 6 WHEN '91-180 days' THEN 7 WHEN '181-365 days' THEN 8 ELSE 9 END END NULLS LAST
WITH order_base AS ( SELECT o.id AS order_id, o.grand_total AS grand_total, o.currency_code AS currency_code, o.placed_at AS placed_at, (DATE '2025-12-31' - CAST(o.placed_at AS date)) AS age_days, CASE WHEN (DATE '2025-12-31' - CAST(o.placed_at AS date)) <= 3 THEN '0-3 days' WHEN (DATE '2025-12-31' - CAST(o.placed_at AS date)) <= 7 THEN '4-7 days' WHEN (DATE '2025-12-31' - CAST(o.placed_at AS date)) <= 14 THEN '8-14 days' WHEN (DATE '2025-12-31' - CAST(o.placed_at AS date)) <= 30 THEN '15-30 days' WHEN (DATE '2025-12-31' - CAST(o.placed_at AS date)) <= 60 THEN '31-60 days' WHEN (DATE '2025-12-31' - CAST(o.placed_at AS date)) <= 90 THEN '61-90 days' WHEN (DATE '2025-12-31' - CAST(o.placed_at AS date)) <= 180 THEN '91-180 days' WHEN (DATE '2025-12-31' - CAST(o.placed_at AS date)) <= 365 THEN '181-365 days' ELSE '366+ days' END AS age_bucket FROM orders o WHERE o.status <> 'cancelled' AND o.placed_at >= TIMESTAMP '2023-01-01' AND o.placed_at < TIMESTAMP '2026-01-01' ), order_count_agg AS ( SELECT t0.age_bucket AS age_bucket, COUNT(*) AS order_count FROM order_base t0 GROUP BY t0.age_bucket ), grand_total_agg AS ( SELECT t1.age_bucket AS age_bucket, SUM(CAST(t1.grand_total / t2.rate AS NUMERIC(18,6))) AS orders_grand_total_eur FROM order_base t1 JOIN exchange_rates t2 ON t2.base_code = 'EUR' AND t2.quote_code = t1.currency_code AND t2.as_of = CAST(t1.placed_at AS date) GROUP BY t1.age_bucket ), payment_agg AS ( SELECT t3.age_bucket AS age_bucket, SUM(CAST(t4.amount / t5.rate AS NUMERIC(18,6))) AS matched_money_collected_eur FROM order_base t3 JOIN payments t4 ON t4.order_id = t3.order_id AND t4.status IN ('captured', 'refunded', 'partially_refunded') JOIN exchange_rates t5 ON t5.base_code = 'EUR' AND t5.quote_code = t4.currency_code AND t5.as_of = CAST(t4.captured_at AS date) GROUP BY t3.age_bucket ) SELECT t6.age_bucket AS age_bucket, t6.order_count AS order_count, COALESCE(t7.orders_grand_total_eur, 0) AS orders_grand_total_eur, COALESCE(t8.matched_money_collected_eur, 0) AS matched_money_collected_eur, COALESCE(t8.matched_money_collected_eur, 0) / NULLIF(t7.orders_grand_total_eur, 0) * 100 AS match_rate_pct FROM order_count_agg t6 LEFT JOIN grand_total_agg t7 ON t7.age_bucket = t6.age_bucket LEFT JOIN payment_agg t8 ON t8.age_bucket = t6.age_bucket ORDER BY CASE t6.age_bucket WHEN '0-3 days' THEN 1 WHEN '4-7 days' THEN 2 WHEN '8-14 days' THEN 3 WHEN '15-30 days' THEN 4 WHEN '31-60 days' THEN 5 WHEN '61-90 days' THEN 6 WHEN '91-180 days' THEN 7 WHEN '181-365 days' THEN 8 ELSE 9 END NULLS LAST
The agents' own records, as the role play wrote them; only the personal data of the person who ran it is redacted
Report request from Yuki Tanaka (analyst for the Head of Data):
Title: Order-to-payment match rate by order age
Question: At the portfolio level, how much of the gap between order value and money collected is just timing (recent orders not yet settled) versus a permanent shortfall that age doesn't fix?
Grain: order age bucket, measured as (2025-12-31 minus orders.placed_at) in days
Columns:
- age_bucket
- order_count
- orders_grand_total_eur
- matched_money_collected_eur
- match_rate_pct
Details: Population: all orders with status not 'cancelled' and placed_at between 2023-01-01 and 2025-12-31. Bucket each order by its age in days as of 2025-12-31: '0-3 days', '4-7 days', '8-14 days', '15-30 days', '31-60 days', '61-90 days', '91-180 days', '181-365 days', '366+ days'. order_count = count of orders in the bucket. orders_grand_total_eur = sum of orders.grand_total converted to EUR via exchange_rates (base_code='EUR', quote_code=orders.currency_code, as_of=placed_at date). matched_money_collected_eur = sum of linked payments.amount (payments.order_id = orders.id) where payments.status in ('captured','refunded','partially_refunded'), converted to EUR via payments.currency_code and captured_at date (an order's full matched total if it has multiple such payments). match_rate_pct = matched_money_collected_eur / orders_grand_total_eur * 100, rounded to 1 decimal. Tables: orders, payments, exchange_rates. Order by age_bucket from youngest to oldest.
Check your query with the warehouse's dry_run tool, then hand it in with submit.dry_runWITH order_base AS ( SELECT o.id AS order_id, o.grand_total, o.currency_code, o.placed_at, (DATE '2025-12-31' - CAST(o.placed_at AS date)) AS age_days, CASE WHEN (DATE '2025-12-31' - CAST(o.placed_at AS date)) <= 3 THEN '0-3 days' WHEN (DATE '2025-12-31' - CAST(o.placed_at AS date)) <= 7 THEN '4-7 days' WHEN (DATE '2025-12-31' - CAST(o.placed_at AS date)) <= 14 THEN '8-14 days' WHEN (DATE '2025-12-31' - CAST(o.placed_at AS date)) <= 30 THEN '15-30 days' WHEN (DATE '2025-12-31' - CAST(o.placed_at AS date)) <= 60 THEN '31-60 days' WHEN (DATE '2025-12-31' - CAST(o.placed_at AS date)) <= 90 THEN '61-90 days' WHEN (DATE '2025-12-31' - CAST(o.placed_at AS date)) <= 180 THEN '91-180 days' WHEN (DATE '2025-12-31' - CAST(o.placed_at AS date)) <= 365 THEN '181-365 days' ELSE '366+ days' END AS age_bucket FROM orders o WHERE o.status <> 'cancelled' AND o.placed_at >= TIMESTAMP '2023-01-01' AND o.placed_at < TIMESTAMP '2026-01-01' ), order_count_agg AS ( SELECT t0.age_bucket, COUNT(*) AS order_count FROM order_base t0 GROUP BY t0.age_bucket ), grand_total_agg AS ( SELECT t1.age_bucket, SUM(t1.grand_total / t2.rate) AS orders_grand_total_eur FROM order_base t1 JOIN exchange_rates t2 ON t2.base_code = 'EUR' AND t2.quote_code = t1.currency_code AND t2.as_of = CAST(t1.placed_at AS date) GROUP BY t1.age_bucket ), payment_agg AS ( SELECT t3.age_bucket, SUM(t4.amount / t5.rate) AS matched_money_collected_eur FROM order_base t3 JOIN payments t4 ON t4.order_id = t3.order_id AND t4.status IN ('captured', 'refunded', 'partially_refunded') JOIN exchange_rates t5 ON t5.base_code = 'EUR' AND t5.quote_code = t4.currency_code AND t5.as_of = CAST(t4.captured_at AS date) GROUP BY t3.age_bucket ) SELECT t6.age_bucket, t6.order_count, ROUND(COALESCE(t7.orders_grand_total_eur, 0), 2) AS orders_grand_total_eur, ROUND(COALESCE(t8.matched_money_collected_eur, 0), 2) AS matched_money_collected_eur, ROUND(COALESCE(t8.matched_money_collected_eur, 0) / NULLIF(t7.orders_grand_total_eur, 0) * 100, 1) AS match_rate_pct FROM order_count_agg t6 LEFT JOIN grand_total_agg t7 ON t7.age_bucket = t6.age_bucket LEFT JOIN payment_agg t8 ON t8.age_bucket = t6.age_bucket ORDER BY CASE t6.age_bucket WHEN '0-3 days' THEN 1 WHEN '4-7 days' THEN 2 WHEN '8-14 days' THEN 3 WHEN '15-30 days' THEN 4 WHEN '31-60 days' THEN 5 WHEN '61-90 days' THEN 6 WHEN '91-180 days' THEN 7 WHEN '181-365 days' THEN 8 ELSE 9 END
STATUS: COMPILES (dry run on the empty schema, no data read) COLUMNS: age_bucket, order_count, orders_grand_total_eur, matched_money_collected_eur, match_rate_pct
submitWITH order_base AS ( SELECT o.id AS order_id, o.grand_total, o.currency_code, o.placed_at, (DATE '2025-12-31' - CAST(o.placed_at AS date)) AS age_days, CASE WHEN (DATE '2025-12-31' - CAST(o.placed_at AS date)) <= 3 THEN '0-3 days' WHEN (DATE '2025-12-31' - CAST(o.placed_at AS date)) <= 7 THEN '4-7 days' WHEN (DATE '2025-12-31' - CAST(o.placed_at AS date)) <= 14 THEN '8-14 days' WHEN (DATE '2025-12-31' - CAST(o.placed_at AS date)) <= 30 THEN '15-30 days' WHEN (DATE '2025-12-31' - CAST(o.placed_at AS date)) <= 60 THEN '31-60 days' WHEN (DATE '2025-12-31' - CAST(o.placed_at AS date)) <= 90 THEN '61-90 days' WHEN (DATE '2025-12-31' - CAST(o.placed_at AS date)) <= 180 THEN '91-180 days' WHEN (DATE '2025-12-31' - CAST(o.placed_at AS date)) <= 365 THEN '181-365 days' ELSE '366+ days' END AS age_bucket FROM orders o WHERE o.status <> 'cancelled' AND o.placed_at >= TIMESTAMP '2023-01-01' AND o.placed_at < TIMESTAMP '2026-01-01' ), order_count_agg AS ( SELECT t0.age_bucket, COUNT(*) AS order_count FROM order_base t0 GROUP BY t0.age_bucket ), grand_total_agg AS ( SELECT t1.age_bucket, SUM(t1.grand_total / t2.rate) AS orders_grand_total_eur FROM order_base t1 JOIN exchange_rates t2 ON t2.base_code = 'EUR' AND t2.quote_code = t1.currency_code AND t2.as_of = CAST(t1.placed_at AS date) GROUP BY t1.age_bucket ), payment_agg AS ( SELECT t3.age_bucket, SUM(t4.amount / t5.rate) AS matched_money_collected_eur FROM order_base t3 JOIN payments t4 ON t4.order_id = t3.order_id AND t4.status IN ('captured', 'refunded', 'partially_refunded') JOIN exchange_rates t5 ON t5.base_code = 'EUR' AND t5.quote_code = t4.currency_code AND t5.as_of = CAST(t4.captured_at AS date) GROUP BY t3.age_bucket ) SELECT t6.age_bucket, t6.order_count, ROUND(COALESCE(t7.orders_grand_total_eur, 0), 2) AS orders_grand_total_eur, ROUND(COALESCE(t8.matched_money_collected_eur, 0), 2) AS matched_money_collected_eur, ROUND(COALESCE(t8.matched_money_collected_eur, 0) / NULLIF(t7.orders_grand_total_eur, 0) * 100, 1) AS match_rate_pct FROM order_count_agg t6 LEFT JOIN grand_total_agg t7 ON t7.age_bucket = t6.age_bucket LEFT JOIN payment_agg t8 ON t8.age_bucket = t6.age_bucket ORDER BY CASE t6.age_bucket WHEN '0-3 days' THEN 1 WHEN '4-7 days' THEN 2 WHEN '8-14 days' THEN 3 WHEN '15-30 days' THEN 4 WHEN '31-60 days' THEN 5 WHEN '61-90 days' THEN 6 WHEN '91-180 days' THEN 7 WHEN '181-365 days' THEN 8 ELSE 9 END
Submitted to tonight's batch. Your work on this request is done.