Queries · q057 · Head of Growth Marketing · week 1, night 3
“Among shoppers who actually place orders, do loyalty-program members order more often or spend more per order than non-members?”
Fictional people at a fictional company, each played by a language-model agent. Who they are and what they are told
The manager, at the start of week 1
Subject: What I need to get oriented this week
Hi Sofie,
Thrilled to be here — here's where my head's at. I know you've only got a handful of report slots a night, so I've ordered these by what I need most. Please push back if any of these are too broad or should split into more than one report.
What I need this for: I'm meeting the leadership team Friday and want a grounded, numbers-based view of growth marketing's current state before I propose anything. No need for polish — I just want to know what's really happening so I don't propose things that are already working, or miss things that are quietly broken.
Thanks for taking this on — let me know if you want me to cut this list down.
Chloé
The analyst · week 1, night 3, request 3 of 3
The Jan/Jul discount-rate report flagged that two B2B wholesalers (Castell Fasteners, OfficeHub Nordic) run discounts on 65-95% of orders even outside the spike months — that's almost certainly negotiated/contract pricing, not promotional codes. That's a real caveat to last night's 'discounted orders look healthier' finding (higher AOV, more items, fewer returns), since B2B bulk orders would naturally look that way regardless of discounting. Before I tell Chloé 'discounting is healthy,' I need to check it holds in consumer verticals, not just because B2B is mixed in. That's tonight's top priority. With that resolved, I'm moving on to orientation baselines for Q3 (email/campaign engagement) and Q4 (loyalty), since two nights remain and those questions haven't been touched yet. Direct-traffic concentration (Report 1 last night) came back as a straightforward big-merchant-volume story, not an anomaly, so I'm not spending a slot chasing it further.
The engineer · 1 dry run on the empty schema, then the submission
SELECT segment, users_count, orders_count_2025, avg_orders_per_user, avg_order_value_eur, total_revenue_eur FROM ( SELECT CASE WHEN EXISTS ( SELECT 1 FROM loyalty_accounts la WHERE la.user_id = o.user_id AND la.tenant_id = o.tenant_id ) THEN 'loyalty member' ELSE 'non-member' END AS segment, COUNT(DISTINCT o.user_id) AS users_count, COUNT(*) AS orders_count_2025, ROUND(COUNT(*)::numeric / COUNT(DISTINCT o.user_id), 2) AS avg_orders_per_user, ROUND(AVG(o.grand_total / COALESCE(er.rate, CASE WHEN o.currency_code = 'EUR' THEN 1 END))::numeric, 0) AS avg_order_value_eur, ROUND(SUM(o.grand_total / COALESCE(er.rate, CASE WHEN o.currency_code = 'EUR' THEN 1 END))::numeric, 0) AS total_revenue_eur FROM orders o LEFT JOIN exchange_rates er ON er.base_code = 'EUR' AND er.quote_code = o.currency_code AND er.as_of = CAST(o.placed_at AS DATE) WHERE o.placed_at >= DATE '2025-01-01' AND o.placed_at < DATE '2026-01-01' GROUP BY CASE WHEN EXISTS ( SELECT 1 FROM loyalty_accounts la WHERE la.user_id = o.user_id AND la.tenant_id = o.tenant_id ) THEN 'loyalty member' ELSE 'non-member' END ) seg ORDER BY CASE WHEN segment = 'loyalty member' THEN 0 ELSE 1 END
“Query compiles with the expected columns. Submitting.”
“Submitted. The report classifies every 2025 order-placing user as a loyalty member (any loyalty_accounts row for that user/tenant) or non-member, then compares order frequency and EUR-converted spend between the two segments, exactly as requested.”
As Sofie Nilsen saw it, from the small dataset the agents worked on
segment | users_count | orders_count_2025 | avg_orders_per_user | avg_order_value_eur | total_revenue_eur ---------------+-------------+-------------------+---------------------+---------------------+------------------ loyalty member | 862 | 2232 | 2.59 | 191 | 427047 non-member | 5297 | 19961 | 3.77 | 281 | 5618101 (2 rows)
The memo and the queries that followed; links found by title, so inferred
Later queries that build on this report:
Changed only where the benchmark needs the same result on every system
SELECT segment, users_count, orders_count_2025, avg_orders_per_user, avg_order_value_eur, total_revenue_eurFROM ( SELECT CASE WHEN EXISTS ( SELECT 1 FROM loyalty_accounts la WHERE la.user_id = o.user_id AND la.tenant_id = o.tenant_id ) THEN 'loyalty member' ELSE 'non-member' END AS segment, COUNT(DISTINCT o.user_id) AS users_count, COUNT(*) AS orders_count_2025, ROUND(COUNT(*)::numeric / COUNT(DISTINCT o.user_id), 2) AS avg_orders_per_user, ROUND(AVG(o.grand_total / COALESCE(er.rate, CASE WHEN o.currency_code = 'EUR' THEN 1 END))::numeric, 0) AS avg_order_value_eur, ROUND(SUM(o.grand_total / COALESCE(er.rate, CASE WHEN o.currency_code = 'EUR' THEN 1 END))::numeric, 0) AS total_revenue_eur CAST(COUNT(*) AS numeric(18,6)) / COUNT(DISTINCT o.user_id) AS avg_orders_per_user, AVG(CAST(o.grand_total / COALESCE(er.rate, CASE WHEN o.currency_code = 'EUR' THEN 1 END) AS NUMERIC(18,6))) AS avg_order_value_eur, SUM(CAST(o.grand_total / COALESCE(er.rate, CASE WHEN o.currency_code = 'EUR' THEN 1 END) AS NUMERIC(18,6))) AS total_revenue_eur FROM orders o LEFT JOIN exchange_rates er ON er.base_code = 'EUR' AND er.quote_code = o.currency_code AND er.as_of = CAST(o.placed_at AS DATE) WHERE o.placed_at >= DATE '2025-01-01' AND o.placed_at < DATE '2026-01-01' GROUP BY CASE WHEN EXISTS ( SELECT 1 FROM loyalty_accounts la WHERE la.user_id = o.user_id AND la.tenant_id = o.tenant_id ) THEN 'loyalty member' ELSE 'non-member' END) segORDER BY CASE WHEN segment = 'loyalty member' THEN 0 ELSE 1 ENDORDER BY CASE WHEN segment = 'loyalty member' THEN 0 ELSE 1 END NULLS LAST
SELECT segment, users_count, orders_count_2025, avg_orders_per_user, avg_order_value_eur, total_revenue_eur FROM ( SELECT CASE WHEN EXISTS ( SELECT 1 FROM loyalty_accounts la WHERE la.user_id = o.user_id AND la.tenant_id = o.tenant_id ) THEN 'loyalty member' ELSE 'non-member' END AS segment, COUNT(DISTINCT o.user_id) AS users_count, COUNT(*) AS orders_count_2025, CAST(COUNT(*) AS numeric(18,6)) / COUNT(DISTINCT o.user_id) AS avg_orders_per_user, AVG(CAST(o.grand_total / COALESCE(er.rate, CASE WHEN o.currency_code = 'EUR' THEN 1 END) AS NUMERIC(18,6))) AS avg_order_value_eur, SUM(CAST(o.grand_total / COALESCE(er.rate, CASE WHEN o.currency_code = 'EUR' THEN 1 END) AS NUMERIC(18,6))) AS total_revenue_eur FROM orders o LEFT JOIN exchange_rates er ON er.base_code = 'EUR' AND er.quote_code = o.currency_code AND er.as_of = CAST(o.placed_at AS DATE) WHERE o.placed_at >= DATE '2025-01-01' AND o.placed_at < DATE '2026-01-01' GROUP BY CASE WHEN EXISTS ( SELECT 1 FROM loyalty_accounts la WHERE la.user_id = o.user_id AND la.tenant_id = o.tenant_id ) THEN 'loyalty member' ELSE 'non-member' END ) seg ORDER BY CASE WHEN segment = 'loyalty member' THEN 0 ELSE 1 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 Sofie Nilsen (analyst for the Head of Growth Marketing): Title: Do loyalty program members behave differently from other shoppers? Question: Among shoppers who actually place orders, do loyalty-program members order more often or spend more per order than non-members? Grain: one row per shopper segment: 'loyalty member' vs 'non-member' Columns: - segment - 'loyalty member' (user_id appears in loyalty_accounts) or 'non-member' - users_count - count of distinct users in that segment who placed at least one order with placed_at in 2025 - orders_count_2025 - count of those users' orders placed_at in 2025 - avg_orders_per_user - orders_count_2025 / users_count, rounded to 2 decimals - avg_order_value_eur - average orders.grand_total converted to EUR across that segment's 2025 orders, rounded to 0 decimals - total_revenue_eur - sum of orders.grand_total converted to EUR for that segment's 2025 orders, rounded to 0 decimals Details: Source: orders (placed_at in 2025, all tenants) left joined to loyalty_accounts on user_id (and tenant_id) to classify each ordering user as a member (has any loyalty_accounts row) or non-member. Restrict to users who placed at least one 2025 order so the comparison is among active shoppers, not all signups. Convert grand_total to EUR via exchange_rates as in prior money reports. Exactly 2 output rows, ordered 'loyalty member' then 'non-member'. Check your query with the warehouse's dry_run tool, then hand it in with submit.
dry_runSELECT segment, users_count, orders_count_2025, avg_orders_per_user, avg_order_value_eur, total_revenue_eur FROM ( SELECT CASE WHEN EXISTS ( SELECT 1 FROM loyalty_accounts la WHERE la.user_id = o.user_id AND la.tenant_id = o.tenant_id ) THEN 'loyalty member' ELSE 'non-member' END AS segment, COUNT(DISTINCT o.user_id) AS users_count, COUNT(*) AS orders_count_2025, ROUND(COUNT(*)::numeric / COUNT(DISTINCT o.user_id), 2) AS avg_orders_per_user, ROUND(AVG(o.grand_total / COALESCE(er.rate, CASE WHEN o.currency_code = 'EUR' THEN 1 END))::numeric, 0) AS avg_order_value_eur, ROUND(SUM(o.grand_total / COALESCE(er.rate, CASE WHEN o.currency_code = 'EUR' THEN 1 END))::numeric, 0) AS total_revenue_eur FROM orders o LEFT JOIN exchange_rates er ON er.base_code = 'EUR' AND er.quote_code = o.currency_code AND er.as_of = CAST(o.placed_at AS DATE) WHERE o.placed_at >= DATE '2025-01-01' AND o.placed_at < DATE '2026-01-01' GROUP BY CASE WHEN EXISTS ( SELECT 1 FROM loyalty_accounts la WHERE la.user_id = o.user_id AND la.tenant_id = o.tenant_id ) THEN 'loyalty member' ELSE 'non-member' END ) seg ORDER BY CASE WHEN segment = 'loyalty member' THEN 0 ELSE 1 END
STATUS: COMPILES (dry run on the empty schema, no data read) COLUMNS: segment, users_count, orders_count_2025, avg_orders_per_user, avg_order_value_eur, total_revenue_eur
submitSELECT segment, users_count, orders_count_2025, avg_orders_per_user, avg_order_value_eur, total_revenue_eur FROM ( SELECT CASE WHEN EXISTS ( SELECT 1 FROM loyalty_accounts la WHERE la.user_id = o.user_id AND la.tenant_id = o.tenant_id ) THEN 'loyalty member' ELSE 'non-member' END AS segment, COUNT(DISTINCT o.user_id) AS users_count, COUNT(*) AS orders_count_2025, ROUND(COUNT(*)::numeric / COUNT(DISTINCT o.user_id), 2) AS avg_orders_per_user, ROUND(AVG(o.grand_total / COALESCE(er.rate, CASE WHEN o.currency_code = 'EUR' THEN 1 END))::numeric, 0) AS avg_order_value_eur, ROUND(SUM(o.grand_total / COALESCE(er.rate, CASE WHEN o.currency_code = 'EUR' THEN 1 END))::numeric, 0) AS total_revenue_eur FROM orders o LEFT JOIN exchange_rates er ON er.base_code = 'EUR' AND er.quote_code = o.currency_code AND er.as_of = CAST(o.placed_at AS DATE) WHERE o.placed_at >= DATE '2025-01-01' AND o.placed_at < DATE '2026-01-01' GROUP BY CASE WHEN EXISTS ( SELECT 1 FROM loyalty_accounts la WHERE la.user_id = o.user_id AND la.tenant_id = o.tenant_id ) THEN 'loyalty member' ELSE 'non-member' END ) seg ORDER BY CASE WHEN segment = 'loyalty member' THEN 0 ELSE 1 END
Submitted to tonight's batch. Your work on this request is done.