Queries · q170 · Head of Lifecycle Marketing · week 1, night 4
“Is the elevated refund rate among loyalty members a broad pattern across verticals, or is it concentrated in one or two verticals that happen to have naturally high returns, such as apparel?”
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
Is the loyalty and referral programme worth it? Do members and referred customers behave differently from others, and what does the programme cost?
The manager, at the start of week 1
Subject: Loyalty & referral programme — what I need for Friday's memo
Hi Ingrid,
Welcome aboard — thank you for jumping on this in week one. Tobias and I need to give the leadership team a joint, evidence-based view on whether the loyalty and referral programme is paying for itself. The deliverable is your Friday memo, which Tobias and I will use to decide whether we expand the programme, leave it as is, or rework it. Given the slot budget, let's prioritize breadth of the right questions over depth on any one — we can always follow up next week.
Here's what I'm trying to answer, in my own words:
Given we only have a few report slots a night, I'd rank these in order 3, 1, 2 as the must-haves for Friday; 4 and 5 are nice-to-haves if slots allow. Please loop in whatever Tobias's analyst is already pulling on the finance side so we're not duplicating report requests.
Thanks, Ingrid — happy to talk through any of this before you start drafting requests.
Best, Aisha
Subject: Finance angle on the loyalty/referral review
Hi Ingrid,
A few things I'd like us to cover as you scope this:
Happy to look at a first cut together before it goes wider.
Tobias
The analyst · week 1, night 4, request 2 of 3
Two of last night's reports came back collapsed to a single row - which is itself informative, not just noise. Report 1 found zero member orders with a matched points-redemption transaction, so the 'reward-driven returns' hypothesis couldn't be tested via order_id linkage (either redemptions don't record order_id, or genuinely none exist in this join); I'm treating that specific mechanism as unconfirmed rather than ruled out, and won't push further on it given limited slots. Report 3 found literally zero loyalty-enrolled customers among B2B wholesaler tenants - before reporting 'the programme isn't offered/adopted in B2B' to Tobias as fact, I want to confirm it's a real pattern (the program simply isn't live there) rather than an artifact of the engineer's fuzzy industry-name matching. Report 2 confirms loyalty penetration plateaued at roughly 15 to 16 percent of customers for two years and referral penetration is still under 1 percent after three years - participation is small and flat, capping any upside regardless of behavior quality. For this final night I'm spending slots on: (1) a clean vertical-level view of where loyalty/referral even exist and are adopted - this resolves the B2B ambiguity and shows whether participation is concentrated in a few verticals; (2) checking whether the member refund-rate penalty is a generalizable pattern or an artifact of one or two verticals' return-prone mix, since that materially changes how we would advise fixing it; (3) a single consolidated unit-economics table (points cost versus revenue, per customer, member versus non-member) to give Tobias and Aisha the bottom-line ROI number the memo needs. This closes out the must-haves (1, 2, 3) with a robustness check and gives a clean answer on 3, 4 and 5 enough to write a defensible Friday memo.
The engineer · 1 dry run on the empty schema, then the submission
WITH eligible_users AS ( SELECT DISTINCT o.user_id, o.tenant_id, t.industry FROM orders o JOIN tenants t ON t.id = o.tenant_id JOIN users u ON u.id = o.user_id AND u.tenant_id = o.tenant_id WHERE o.placed_at >= DATE '2023-01-01' AND o.placed_at < DATE '2026-01-01' AND u.is_staff = false ), loyalty_flags AS ( SELECT DISTINCT tenant_id, user_id FROM loyalty_accounts ), user_segment AS ( SELECT eu.tenant_id, eu.user_id, eu.industry, CASE WHEN lf.user_id IS NOT NULL THEN 'loyalty member' ELSE 'non-member' END AS segment FROM eligible_users eu LEFT JOIN loyalty_flags lf ON lf.tenant_id = eu.tenant_id AND lf.user_id = eu.user_id ), all_orders_for_users AS ( SELECT o.id AS order_id, o.tenant_id, o.user_id, o.status, o.placed_at, o.currency_code, o.grand_total FROM orders o JOIN user_segment us ON us.tenant_id = o.tenant_id AND us.user_id = o.user_id ), refund_flags AS ( SELECT ao.order_id, CASE WHEN EXISTS ( SELECT 1 FROM payments p WHERE p.order_id = ao.order_id AND p.amount_refunded > 0 ) OR EXISTS ( SELECT 1 FROM order_returns r WHERE r.order_id = ao.order_id ) THEN 1 ELSE 0 END AS is_refunded FROM all_orders_for_users ao ), order_value_eur AS ( SELECT ao.order_id, ao.tenant_id, ao.user_id, ao.grand_total / er.rate AS order_value_eur FROM all_orders_for_users ao LEFT JOIN exchange_rates er ON er.base_code = 'EUR' AND er.quote_code = ao.currency_code AND er.as_of = CAST(ao.placed_at AS DATE) WHERE ao.status <> 'cancelled' ), per_user_stats AS ( SELECT us.tenant_id, us.user_id, us.industry, us.segment, COUNT(ao.order_id) AS lifetime_order_count FROM user_segment us LEFT JOIN all_orders_for_users ao ON ao.tenant_id = us.tenant_id AND ao.user_id = us.user_id GROUP BY us.tenant_id, us.user_id, us.industry, us.segment ), industry_segment_counts AS ( SELECT industry, segment, COUNT(DISTINCT user_id) AS customer_count, AVG(lifetime_order_count) AS avg_lifetime_orders FROM per_user_stats GROUP BY industry, segment ), order_value_agg AS ( SELECT us.industry, us.segment, AVG(ov.order_value_eur) AS avg_order_value_eur FROM order_value_eur ov JOIN user_segment us ON us.tenant_id = ov.tenant_id AND us.user_id = ov.user_id GROUP BY us.industry, us.segment ), refund_agg AS ( SELECT us.industry, us.segment, AVG(CAST(rf.is_refunded AS DECIMAL)) * 100 AS refund_rate_pct FROM refund_flags rf JOIN all_orders_for_users ao ON ao.order_id = rf.order_id JOIN user_segment us ON us.tenant_id = ao.tenant_id AND us.user_id = ao.user_id GROUP BY us.industry, us.segment ), qualifying_industries AS ( SELECT industry FROM industry_segment_counts GROUP BY industry HAVING COUNT(DISTINCT segment) = 2 AND MIN(customer_count) >= 20 ) SELECT isc.industry, isc.segment, isc.customer_count, ROUND(isc.avg_lifetime_orders, 2) AS avg_lifetime_orders, ROUND(ova.avg_order_value_eur, 2) AS avg_order_value_eur, ROUND(ra.refund_rate_pct, 2) AS refund_rate_pct FROM industry_segment_counts isc JOIN qualifying_industries qi ON qi.industry = isc.industry LEFT JOIN order_value_agg ova ON ova.industry = isc.industry AND ova.segment = isc.segment LEFT JOIN refund_agg ra ON ra.industry = isc.industry AND ra.segment = isc.segment ORDER BY isc.industry, (CASE WHEN isc.segment = 'loyalty member' THEN 0 ELSE 1 END)
“Query compiles. Submitting.”
“Submitted. Summary of what the query does:
- Population & segment: users with is_staff=false who placed ≥1 order between 2023-01-01 and 2025-12-31, split into "loyalty member" (any row in loyalty_accounts) vs "non-member", grouped by tenants.industry.
- avg_lifetime_orders: average count of all orders (any status) per customer in that industry/segment.
- avg_order_value_eur: average grand_total of non-cancelled orders, converted to EUR using the exchange_rates row for the order's placed_at date.
- refund_rate_pct: share of orders (any status) with either a payment showing amount_refunded > 0 or a linked order_returns row.
- Filter: only industries where both segments have ≥20 customers are kept, per the request's "20 in both segments" threshold.
Noted in the submit note: industries failing that threshold simply won't appear in the output (rather than adding an exclusion-count column, which would violate the single industry×segment grain).”
As Ingrid Solberg saw it, from the small dataset the agents worked on
industry | segment | customer_count | avg_lifetime_orders | avg_order_value_eur | refund_rate_pct ---------------------+----------------+----------------+---------------------+---------------------+---------------- Apparel & Footwear | loyalty member | 953 | 3.77 | 167.82 | 38.37 Apparel & Footwear | non-member | 3411 | 3.93 | 199.87 | 38.83 Beauty | loyalty member | 57 | 3.79 | 96.5 | 5.09 Beauty | non-member | 656 | 4.68 | 99.81 | 6.52 Consumer Electronics | loyalty member | 42 | 2.48 | 691.94 | 15.38 Consumer Electronics | non-member | 434 | 2.78 | 834.72 | 12.95 Home & Living | loyalty member | 42 | 2.93 | 134.78 | 13.01 Home & Living | non-member | 1228 | 3.2 | 158.78 | 14.95 Sports & Outdoor | loyalty member | 206 | 3.59 | 352.23 | 22.7 Sports & Outdoor | non-member | 541 | 3.02 | 355.82 | 24.05 (10 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 eligible_users AS ( SELECT DISTINCT o.user_id, o.tenant_id, t.industry o.user_id AS user_id, o.tenant_id AS tenant_id, t.industry AS industry FROM orders o JOIN tenants t ON t.id = o.tenant_id JOIN users u ON u.id = o.user_id AND u.tenant_id = o.tenant_id WHERE o.placed_at >= DATE '2023-01-01' AND o.placed_at < DATE '2026-01-01' AND u.is_staff = false),loyalty_flags AS ( SELECT DISTINCT tenant_id, user_id FROM loyalty_accounts),user_segment AS ( SELECT eu.tenant_id, eu.user_id, eu.industry, eu.tenant_id AS tenant_id, eu.user_id AS user_id, eu.industry AS industry, CASE WHEN lf.user_id IS NOT NULL THEN 'loyalty member' ELSE 'non-member' END AS segment FROM eligible_users eu LEFT JOIN loyalty_flags lf ON lf.tenant_id = eu.tenant_id AND lf.user_id = eu.user_id),all_orders_for_users AS ( SELECT o.id AS order_id, o.tenant_id, o.user_id, o.status, o.placed_at, o.currency_code, o.grand_total o.tenant_id AS tenant_id, o.user_id AS user_id, o.status AS status, o.placed_at AS placed_at, o.currency_code AS currency_code, o.grand_total AS grand_total FROM orders o JOIN user_segment us ON us.tenant_id = o.tenant_id AND us.user_id = o.user_id),refund_flags AS ( SELECT ao.order_id, ao.order_id AS order_id, CASE WHEN EXISTS ( SELECT 1 FROM payments p WHERE p.order_id = ao.order_id AND p.amount_refunded > 0 ) OR EXISTS ( SELECT 1 FROM order_returns r WHERE r.order_id = ao.order_id ) THEN 1 ELSE 0 END AS is_refunded FROM all_orders_for_users ao),order_value_eur AS ( SELECT ao.order_id, ao.tenant_id, ao.user_id, ao.grand_total / er.rate AS order_value_eur ao.order_id AS order_id, ao.tenant_id AS tenant_id, ao.user_id AS user_id, CAST(ao.grand_total / er.rate AS NUMERIC(18,6)) AS order_value_eur FROM all_orders_for_users ao LEFT JOIN exchange_rates er ON er.base_code = 'EUR' AND er.quote_code = ao.currency_code AND er.as_of = CAST(ao.placed_at AS DATE) WHERE ao.status <> 'cancelled'),per_user_stats AS ( SELECT us.tenant_id, us.user_id, us.industry, us.segment, us.tenant_id AS tenant_id, us.user_id AS user_id, us.industry AS industry, us.segment AS segment, COUNT(ao.order_id) AS lifetime_order_count FROM user_segment us LEFT JOIN all_orders_for_users ao ON ao.tenant_id = us.tenant_id AND ao.user_id = us.user_id GROUP BY us.tenant_id, us.user_id, us.industry, us.segment),industry_segment_counts AS ( SELECT industry, segment, COUNT(DISTINCT user_id) AS customer_count, AVG(lifetime_order_count) AS avg_lifetime_orders AVG(CAST(lifetime_order_count AS NUMERIC(18,6))) AS avg_lifetime_orders FROM per_user_stats GROUP BY industry, segment),order_value_agg AS ( SELECT us.industry, us.segment, us.industry AS industry, us.segment AS segment, AVG(ov.order_value_eur) AS avg_order_value_eur FROM order_value_eur ov JOIN user_segment us ON us.tenant_id = ov.tenant_id AND us.user_id = ov.user_id GROUP BY us.industry, us.segment),refund_agg AS ( SELECT us.industry, us.segment, AVG(CAST(rf.is_refunded AS DECIMAL)) * 100 AS refund_rate_pct us.industry AS industry, us.segment AS segment, AVG(CAST(rf.is_refunded AS DECIMAL(18,6))) * 100 AS refund_rate_pct FROM refund_flags rf JOIN all_orders_for_users ao ON ao.order_id = rf.order_id JOIN user_segment us ON us.tenant_id = ao.tenant_id AND us.user_id = ao.user_id GROUP BY us.industry, us.segment),qualifying_industries AS ( SELECT industry FROM industry_segment_counts GROUP BY industry HAVING COUNT(DISTINCT segment) = 2 AND MIN(customer_count) >= 20)SELECT isc.industry, isc.segment, isc.customer_count, ROUND(isc.avg_lifetime_orders, 2) AS avg_lifetime_orders, ROUND(ova.avg_order_value_eur, 2) AS avg_order_value_eur, ROUND(ra.refund_rate_pct, 2) AS refund_rate_pct isc.industry AS industry, isc.segment AS segment, isc.customer_count AS customer_count, isc.avg_lifetime_orders AS avg_lifetime_orders, ova.avg_order_value_eur AS avg_order_value_eur, ra.refund_rate_pct AS refund_rate_pctFROM industry_segment_counts iscJOIN qualifying_industries qi ON qi.industry = isc.industryLEFT JOIN order_value_agg ova ON ova.industry = isc.industry AND ova.segment = isc.segmentLEFT JOIN refund_agg ra ON ra.industry = isc.industry AND ra.segment = isc.segmentORDER BY isc.industry, (CASE WHEN isc.segment = 'loyalty member' THEN 0 ELSE 1 END)ORDER BY isc.industry NULLS LAST, (CASE WHEN isc.segment = 'loyalty member' THEN 0 ELSE 1 END) NULLS LAST
WITH eligible_users AS ( SELECT DISTINCT o.user_id AS user_id, o.tenant_id AS tenant_id, t.industry AS industry FROM orders o JOIN tenants t ON t.id = o.tenant_id JOIN users u ON u.id = o.user_id AND u.tenant_id = o.tenant_id WHERE o.placed_at >= DATE '2023-01-01' AND o.placed_at < DATE '2026-01-01' AND u.is_staff = false ), loyalty_flags AS ( SELECT DISTINCT tenant_id, user_id FROM loyalty_accounts ), user_segment AS ( SELECT eu.tenant_id AS tenant_id, eu.user_id AS user_id, eu.industry AS industry, CASE WHEN lf.user_id IS NOT NULL THEN 'loyalty member' ELSE 'non-member' END AS segment FROM eligible_users eu LEFT JOIN loyalty_flags lf ON lf.tenant_id = eu.tenant_id AND lf.user_id = eu.user_id ), all_orders_for_users AS ( SELECT o.id AS order_id, o.tenant_id AS tenant_id, o.user_id AS user_id, o.status AS status, o.placed_at AS placed_at, o.currency_code AS currency_code, o.grand_total AS grand_total FROM orders o JOIN user_segment us ON us.tenant_id = o.tenant_id AND us.user_id = o.user_id ), refund_flags AS ( SELECT ao.order_id AS order_id, CASE WHEN EXISTS ( SELECT 1 FROM payments p WHERE p.order_id = ao.order_id AND p.amount_refunded > 0 ) OR EXISTS ( SELECT 1 FROM order_returns r WHERE r.order_id = ao.order_id ) THEN 1 ELSE 0 END AS is_refunded FROM all_orders_for_users ao ), order_value_eur AS ( SELECT ao.order_id AS order_id, ao.tenant_id AS tenant_id, ao.user_id AS user_id, CAST(ao.grand_total / er.rate AS NUMERIC(18,6)) AS order_value_eur FROM all_orders_for_users ao LEFT JOIN exchange_rates er ON er.base_code = 'EUR' AND er.quote_code = ao.currency_code AND er.as_of = CAST(ao.placed_at AS DATE) WHERE ao.status <> 'cancelled' ), per_user_stats AS ( SELECT us.tenant_id AS tenant_id, us.user_id AS user_id, us.industry AS industry, us.segment AS segment, COUNT(ao.order_id) AS lifetime_order_count FROM user_segment us LEFT JOIN all_orders_for_users ao ON ao.tenant_id = us.tenant_id AND ao.user_id = us.user_id GROUP BY us.tenant_id, us.user_id, us.industry, us.segment ), industry_segment_counts AS ( SELECT industry, segment, COUNT(DISTINCT user_id) AS customer_count, AVG(CAST(lifetime_order_count AS NUMERIC(18,6))) AS avg_lifetime_orders FROM per_user_stats GROUP BY industry, segment ), order_value_agg AS ( SELECT us.industry AS industry, us.segment AS segment, AVG(ov.order_value_eur) AS avg_order_value_eur FROM order_value_eur ov JOIN user_segment us ON us.tenant_id = ov.tenant_id AND us.user_id = ov.user_id GROUP BY us.industry, us.segment ), refund_agg AS ( SELECT us.industry AS industry, us.segment AS segment, AVG(CAST(rf.is_refunded AS DECIMAL(18,6))) * 100 AS refund_rate_pct FROM refund_flags rf JOIN all_orders_for_users ao ON ao.order_id = rf.order_id JOIN user_segment us ON us.tenant_id = ao.tenant_id AND us.user_id = ao.user_id GROUP BY us.industry, us.segment ), qualifying_industries AS ( SELECT industry FROM industry_segment_counts GROUP BY industry HAVING COUNT(DISTINCT segment) = 2 AND MIN(customer_count) >= 20 ) SELECT isc.industry AS industry, isc.segment AS segment, isc.customer_count AS customer_count, isc.avg_lifetime_orders AS avg_lifetime_orders, ova.avg_order_value_eur AS avg_order_value_eur, ra.refund_rate_pct AS refund_rate_pct FROM industry_segment_counts isc JOIN qualifying_industries qi ON qi.industry = isc.industry LEFT JOIN order_value_agg ova ON ova.industry = isc.industry AND ova.segment = isc.segment LEFT JOIN refund_agg ra ON ra.industry = isc.industry AND ra.segment = isc.segment ORDER BY isc.industry NULLS LAST, (CASE WHEN isc.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 Ingrid Solberg (analyst for the Head of Lifecycle Marketing):
Title: Loyalty member refund rate by merchant vertical
Question: Is the elevated refund rate among loyalty members a broad pattern across verticals, or is it concentrated in one or two verticals that happen to have naturally high returns, such as apparel?
Grain: one row per combination of tenant industry and loyalty segment (member vs non-member)
Columns:
- industry
- segment
- customer_count
- avg_lifetime_orders
- avg_order_value_eur
- refund_rate_pct
Details: Same population and definitions as the earlier 'Loyalty member vs non-member behavior by account tenure' report: users (is_staff = false) with at least one order 2023-01-01 to 2025-12-31, grouped by tenants.industry and by segment ('loyalty member' if present in loyalty_accounts, else 'non-member'). avg_order_value_eur uses non-cancelled orders, grand_total converted to EUR via exchange_rates (rate as_of placed_at). refund_rate_pct = share of orders (any status) with payments.amount_refunded greater than 0 OR an order_returns row. Only include industries with at least 20 customers in both segments to keep the comparison meaningful; note in the report how many industries were excluded for low volume. Order by industry, then segment ('loyalty member' before 'non-member').
Check your query with the warehouse's dry_run tool, then hand it in with submit.dry_runWITH eligible_users AS ( SELECT DISTINCT o.user_id, o.tenant_id, t.industry FROM orders o JOIN tenants t ON t.id = o.tenant_id JOIN users u ON u.id = o.user_id AND u.tenant_id = o.tenant_id WHERE o.placed_at >= DATE '2023-01-01' AND o.placed_at < DATE '2026-01-01' AND u.is_staff = false ), loyalty_flags AS ( SELECT DISTINCT tenant_id, user_id FROM loyalty_accounts ), user_segment AS ( SELECT eu.tenant_id, eu.user_id, eu.industry, CASE WHEN lf.user_id IS NOT NULL THEN 'loyalty member' ELSE 'non-member' END AS segment FROM eligible_users eu LEFT JOIN loyalty_flags lf ON lf.tenant_id = eu.tenant_id AND lf.user_id = eu.user_id ), all_orders_for_users AS ( SELECT o.id AS order_id, o.tenant_id, o.user_id, o.status, o.placed_at, o.currency_code, o.grand_total FROM orders o JOIN user_segment us ON us.tenant_id = o.tenant_id AND us.user_id = o.user_id ), refund_flags AS ( SELECT ao.order_id, CASE WHEN EXISTS ( SELECT 1 FROM payments p WHERE p.order_id = ao.order_id AND p.amount_refunded > 0 ) OR EXISTS ( SELECT 1 FROM order_returns r WHERE r.order_id = ao.order_id ) THEN 1 ELSE 0 END AS is_refunded FROM all_orders_for_users ao ), order_value_eur AS ( SELECT ao.order_id, ao.tenant_id, ao.user_id, ao.grand_total / er.rate AS order_value_eur FROM all_orders_for_users ao LEFT JOIN exchange_rates er ON er.base_code = 'EUR' AND er.quote_code = ao.currency_code AND er.as_of = CAST(ao.placed_at AS DATE) WHERE ao.status <> 'cancelled' ), per_user_stats AS ( SELECT us.tenant_id, us.user_id, us.industry, us.segment, COUNT(ao.order_id) AS lifetime_order_count FROM user_segment us LEFT JOIN all_orders_for_users ao ON ao.tenant_id = us.tenant_id AND ao.user_id = us.user_id GROUP BY us.tenant_id, us.user_id, us.industry, us.segment ), industry_segment_counts AS ( SELECT industry, segment, COUNT(DISTINCT user_id) AS customer_count, AVG(lifetime_order_count) AS avg_lifetime_orders FROM per_user_stats GROUP BY industry, segment ), order_value_agg AS ( SELECT us.industry, us.segment, AVG(ov.order_value_eur) AS avg_order_value_eur FROM order_value_eur ov JOIN user_segment us ON us.tenant_id = ov.tenant_id AND us.user_id = ov.user_id GROUP BY us.industry, us.segment ), refund_agg AS ( SELECT us.industry, us.segment, AVG(CAST(rf.is_refunded AS DECIMAL)) * 100 AS refund_rate_pct FROM refund_flags rf JOIN all_orders_for_users ao ON ao.order_id = rf.order_id JOIN user_segment us ON us.tenant_id = ao.tenant_id AND us.user_id = ao.user_id GROUP BY us.industry, us.segment ), qualifying_industries AS ( SELECT industry FROM industry_segment_counts GROUP BY industry HAVING COUNT(DISTINCT segment) = 2 AND MIN(customer_count) >= 20 ) SELECT isc.industry, isc.segment, isc.customer_count, ROUND(isc.avg_lifetime_orders, 2) AS avg_lifetime_orders, ROUND(ova.avg_order_value_eur, 2) AS avg_order_value_eur, ROUND(ra.refund_rate_pct, 2) AS refund_rate_pct FROM industry_segment_counts isc JOIN qualifying_industries qi ON qi.industry = isc.industry LEFT JOIN order_value_agg ova ON ova.industry = isc.industry AND ova.segment = isc.segment LEFT JOIN refund_agg ra ON ra.industry = isc.industry AND ra.segment = isc.segment ORDER BY isc.industry, (CASE WHEN isc.segment = 'loyalty member' THEN 0 ELSE 1 END)
STATUS: COMPILES (dry run on the empty schema, no data read) COLUMNS: industry, segment, customer_count, avg_lifetime_orders, avg_order_value_eur, refund_rate_pct
submitWITH eligible_users AS ( SELECT DISTINCT o.user_id, o.tenant_id, t.industry FROM orders o JOIN tenants t ON t.id = o.tenant_id JOIN users u ON u.id = o.user_id AND u.tenant_id = o.tenant_id WHERE o.placed_at >= DATE '2023-01-01' AND o.placed_at < DATE '2026-01-01' AND u.is_staff = false ), loyalty_flags AS ( SELECT DISTINCT tenant_id, user_id FROM loyalty_accounts ), user_segment AS ( SELECT eu.tenant_id, eu.user_id, eu.industry, CASE WHEN lf.user_id IS NOT NULL THEN 'loyalty member' ELSE 'non-member' END AS segment FROM eligible_users eu LEFT JOIN loyalty_flags lf ON lf.tenant_id = eu.tenant_id AND lf.user_id = eu.user_id ), all_orders_for_users AS ( SELECT o.id AS order_id, o.tenant_id, o.user_id, o.status, o.placed_at, o.currency_code, o.grand_total FROM orders o JOIN user_segment us ON us.tenant_id = o.tenant_id AND us.user_id = o.user_id ), refund_flags AS ( SELECT ao.order_id, CASE WHEN EXISTS ( SELECT 1 FROM payments p WHERE p.order_id = ao.order_id AND p.amount_refunded > 0 ) OR EXISTS ( SELECT 1 FROM order_returns r WHERE r.order_id = ao.order_id ) THEN 1 ELSE 0 END AS is_refunded FROM all_orders_for_users ao ), order_value_eur AS ( SELECT ao.order_id, ao.tenant_id, ao.user_id, ao.grand_total / er.rate AS order_value_eur FROM all_orders_for_users ao LEFT JOIN exchange_rates er ON er.base_code = 'EUR' AND er.quote_code = ao.currency_code AND er.as_of = CAST(ao.placed_at AS DATE) WHERE ao.status <> 'cancelled' ), per_user_stats AS ( SELECT us.tenant_id, us.user_id, us.industry, us.segment, COUNT(ao.order_id) AS lifetime_order_count FROM user_segment us LEFT JOIN all_orders_for_users ao ON ao.tenant_id = us.tenant_id AND ao.user_id = us.user_id GROUP BY us.tenant_id, us.user_id, us.industry, us.segment ), industry_segment_counts AS ( SELECT industry, segment, COUNT(DISTINCT user_id) AS customer_count, AVG(lifetime_order_count) AS avg_lifetime_orders FROM per_user_stats GROUP BY industry, segment ), order_value_agg AS ( SELECT us.industry, us.segment, AVG(ov.order_value_eur) AS avg_order_value_eur FROM order_value_eur ov JOIN user_segment us ON us.tenant_id = ov.tenant_id AND us.user_id = ov.user_id GROUP BY us.industry, us.segment ), refund_agg AS ( SELECT us.industry, us.segment, AVG(CAST(rf.is_refunded AS DECIMAL)) * 100 AS refund_rate_pct FROM refund_flags rf JOIN all_orders_for_users ao ON ao.order_id = rf.order_id JOIN user_segment us ON us.tenant_id = ao.tenant_id AND us.user_id = ao.user_id GROUP BY us.industry, us.segment ), qualifying_industries AS ( SELECT industry FROM industry_segment_counts GROUP BY industry HAVING COUNT(DISTINCT segment) = 2 AND MIN(customer_count) >= 20 ) SELECT isc.industry, isc.segment, isc.customer_count, ROUND(isc.avg_lifetime_orders, 2) AS avg_lifetime_orders, ROUND(ova.avg_order_value_eur, 2) AS avg_order_value_eur, ROUND(ra.refund_rate_pct, 2) AS refund_rate_pct FROM industry_segment_counts isc JOIN qualifying_industries qi ON qi.industry = isc.industry LEFT JOIN order_value_agg ova ON ova.industry = isc.industry AND ova.segment = isc.segment LEFT JOIN refund_agg ra ON ra.industry = isc.industry AND ra.segment = isc.segment ORDER BY isc.industry, (CASE WHEN isc.segment = 'loyalty member' THEN 0 ELSE 1 END)
Submitted to tonight's batch. Your work on this request is done.