Queries · q039 · VP Customer Experience · week 1, night 1
“What kinds of problems are shoppers contacting merchants about most, and has that mix shifted in the last 6 months versus the prior 6?”
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: Where I'd like to start — support & shopper experience
Hi Mateo,
Good to have you on this with me. I'm still building my map of the business, so I want to keep this week broad — I'd rather get a solid first read on a few big questions than a deep dive on one. Here's what's on my mind, roughly in priority order:
What I need these for: I'm meeting merchant success and the exec team soon and want a credible, numbers-backed view of shopper experience — not just war stories. I also want to start spotting the two or three things that, if we fixed them, would move the needle most broadly across merchants, rather than chasing individual complaints.
Given the slot constraints, let's prioritize #1 through #3 this week if we can't fit all five — those feel most urgent to me. Happy to talk through any of this before you lock in requests.
Thanks, Daniel
The analyst · week 1, night 1, request 3 of 3
Daniel's top priority is #1 (response/resolution trend, and variance by merchant), so I'm spending 2 of tonight's 3 slots there: a platform-wide monthly trend (to see direction of travel) and a per-merchant snapshot for the latest full year (to see spread across merchants, since he likes peer comparisons). The third slot starts #2 (where load comes from) with a tag-based mix comparing the last two half-years to catch any shift. I'm deliberately holding off on YoY columns inside the trend report itself since the 36-month series already shows year-over-year pattern, and saving width for the core time metrics. Returns/refunds (#3), satisfaction (#4) and reviews (#5) are next on my list for later nights.
The engineer · 1 dry run on the empty schema, then the submission
SELECT stn.name AS tag_name, COALESCE(a.cnt,0) AS tickets_last_6mo, COALESCE(b.cnt,0) AS tickets_prior_6mo, ROUND(COALESCE(a.cnt,0) * 100.0 / NULLIF(totals.total_a,0), 1) AS share_last_6mo_pct, ROUND(COALESCE(b.cnt,0) * 100.0 / NULLIF(totals.total_b,0), 1) AS share_prior_6mo_pct, ROUND( COALESCE(a.cnt,0) * 100.0 / NULLIF(totals.total_a,0) - COALESCE(b.cnt,0) * 100.0 / NULLIF(totals.total_b,0) , 1) AS share_change_pp, ROUND(a.avg_res_hours, 1) AS avg_resolution_hours_last_6mo FROM (SELECT DISTINCT name FROM support_tags) stn LEFT JOIN ( SELECT st.name AS name, COUNT(*) AS cnt, AVG(EXTRACT(EPOCH FROM (t.resolved_at - t.created_at)) / 3600.0) AS avg_res_hours FROM ticket_tags_map ttm JOIN tickets t ON t.id = ttm.ticket_id JOIN support_tags st ON st.id = ttm.support_tag_id WHERE t.created_at >= DATE '2025-07-01' AND t.created_at < DATE '2026-01-01' GROUP BY st.name ) a ON a.name = stn.name LEFT JOIN ( SELECT st.name AS name, COUNT(*) AS cnt FROM ticket_tags_map ttm JOIN tickets t ON t.id = ttm.ticket_id JOIN support_tags st ON st.id = ttm.support_tag_id WHERE t.created_at >= DATE '2025-01-01' AND t.created_at < DATE '2025-07-01' GROUP BY st.name ) b ON b.name = stn.name CROSS JOIN ( SELECT (SELECT COUNT(*) FROM ticket_tags_map ttm JOIN tickets t ON t.id = ttm.ticket_id WHERE t.created_at >= DATE '2025-07-01' AND t.created_at < DATE '2026-01-01') AS total_a, (SELECT COUNT(*) FROM ticket_tags_map ttm JOIN tickets t ON t.id = ttm.ticket_id WHERE t.created_at >= DATE '2025-01-01' AND t.created_at < DATE '2025-07-01') AS total_b ) totals WHERE COALESCE(a.cnt,0) > 0 OR COALESCE(b.cnt,0) > 0 ORDER BY tickets_last_6mo DESC, tag_name ASC LIMIT 50
“Submitted. Summary of what the report does: - Grain: one row per support tag name (merged across tenants, since tag names are only unique per-tenant). - Metrics: ticket-tag counts for Jul–Dec 2025 and Jan–Jun 2025, each period's share of that period's tagged ticket-tag rows, the share change in points, and average resolution hours (period A only, over resolved tickets). - Filter: only tags with activity in at least one of the two periods are shown, ordered by recent volume descending. - Caveat flagged in the note: multi-tag tickets inflate counts (a ticket with 2 tags counts once per tag), so the tag totals won't sum to total ticket volume — consistent with what the request asked for.”
As Mateo Ruiz saw it, from the small dataset the agents worked on
tag_name | tickets_last_6mo | tickets_prior_6mo | share_last_6mo_pct | share_prior_6mo_pct | share_change_pp | avg_resolution_hours_last_6mo ------------------+------------------+-------------------+--------------------+---------------------+-----------------+------------------------------ delivery | 817 | 378 | 34.7 | 28.6 | 6.1 | 45.5 where-is-my-order | 726 | 331 | 30.8 | 25.1 | 5.8 | 47.8 complaint | 265 | 199 | 11.2 | 15.1 | -3.8 | 27.7 return | 237 | 173 | 10.1 | 13.1 | -3.0 | 32.4 product-question | 138 | 109 | 5.9 | 8.3 | -2.4 | 49.9 damaged | 126 | 84 | 5.3 | 6.4 | -1.0 | 35.8 refund | 36 | 29 | 1.5 | 2.2 | -0.7 | 24.0 payment | 11 | 9 | 0.5 | 0.7 | -0.2 | 29.6 account | 0 | 9 | 0.0 | 0.7 | -0.7 | NULL (9 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 stn.name AS tag_name, COALESCE(a.cnt,0) AS tickets_last_6mo, COALESCE(b.cnt,0) AS tickets_prior_6mo, ROUND(COALESCE(a.cnt,0) * 100.0 / NULLIF(totals.total_a,0), 1) AS share_last_6mo_pct, ROUND(COALESCE(b.cnt,0) * 100.0 / NULLIF(totals.total_b,0), 1) AS share_prior_6mo_pct, ROUND( COALESCE(a.cnt,0) * 100.0 / NULLIF(totals.total_a,0) - COALESCE(b.cnt,0) * 100.0 / NULLIF(totals.total_b,0) , 1) AS share_change_pp, ROUND(a.avg_res_hours, 1) AS avg_resolution_hours_last_6mo COALESCE(a.cnt,0) * 100.0 / NULLIF(totals.total_a,0) AS share_last_6mo_pct, COALESCE(b.cnt,0) * 100.0 / NULLIF(totals.total_b,0) AS share_prior_6mo_pct, COALESCE(a.cnt,0) * 100.0 / NULLIF(totals.total_a,0) - COALESCE(b.cnt,0) * 100.0 / NULLIF(totals.total_b,0) AS share_change_pp, a.avg_res_hours AS avg_resolution_hours_last_6moFROM (SELECT DISTINCT name FROM support_tags) stnLEFT JOIN ( SELECT st.name AS name, COUNT(*) AS cnt, AVG(EXTRACT(EPOCH FROM (t.resolved_at - t.created_at)) / 3600.0) AS avg_res_hours AVG({{seconds_between(t.created_at, t.resolved_at)}} / 3600.0) AS avg_res_hours FROM ticket_tags_map ttm JOIN tickets t ON t.id = ttm.ticket_id JOIN support_tags st ON st.id = ttm.support_tag_id WHERE t.created_at >= DATE '2025-07-01' AND t.created_at < DATE '2026-01-01' GROUP BY st.name) a ON a.name = stn.nameLEFT JOIN ( SELECT st.name AS name, COUNT(*) AS cnt FROM ticket_tags_map ttm JOIN tickets t ON t.id = ttm.ticket_id JOIN support_tags st ON st.id = ttm.support_tag_id WHERE t.created_at >= DATE '2025-01-01' AND t.created_at < DATE '2025-07-01' GROUP BY st.name) b ON b.name = stn.nameCROSS JOIN ( SELECT (SELECT COUNT(*) FROM ticket_tags_map ttm JOIN tickets t ON t.id = ttm.ticket_id WHERE t.created_at >= DATE '2025-07-01' AND t.created_at < DATE '2026-01-01') AS total_a, (SELECT COUNT(*) FROM ticket_tags_map ttm JOIN tickets t ON t.id = ttm.ticket_id WHERE t.created_at >= DATE '2025-01-01' AND t.created_at < DATE '2025-07-01') AS total_b) totalsWHERE COALESCE(a.cnt,0) > 0 OR COALESCE(b.cnt,0) > 0ORDER BY tickets_last_6mo DESC, tag_name ASCLIMIT 50ORDER BY tickets_last_6mo DESC NULLS FIRST, tag_name ASC NULLS LAST{{fetch_first(50)}}
SELECT stn.name AS tag_name, COALESCE(a.cnt,0) AS tickets_last_6mo, COALESCE(b.cnt,0) AS tickets_prior_6mo, COALESCE(a.cnt,0) * 100.0 / NULLIF(totals.total_a,0) AS share_last_6mo_pct, COALESCE(b.cnt,0) * 100.0 / NULLIF(totals.total_b,0) AS share_prior_6mo_pct, COALESCE(a.cnt,0) * 100.0 / NULLIF(totals.total_a,0) - COALESCE(b.cnt,0) * 100.0 / NULLIF(totals.total_b,0) AS share_change_pp, a.avg_res_hours AS avg_resolution_hours_last_6mo FROM (SELECT DISTINCT name FROM support_tags) stn LEFT JOIN ( SELECT st.name AS name, COUNT(*) AS cnt, AVG(CAST(EXTRACT(EPOCH FROM (t.resolved_at - t.created_at)) AS NUMERIC(18,6)) / 3600.0) AS avg_res_hours FROM ticket_tags_map ttm JOIN tickets t ON t.id = ttm.ticket_id JOIN support_tags st ON st.id = ttm.support_tag_id WHERE t.created_at >= DATE '2025-07-01' AND t.created_at < DATE '2026-01-01' GROUP BY st.name ) a ON a.name = stn.name LEFT JOIN ( SELECT st.name AS name, COUNT(*) AS cnt FROM ticket_tags_map ttm JOIN tickets t ON t.id = ttm.ticket_id JOIN support_tags st ON st.id = ttm.support_tag_id WHERE t.created_at >= DATE '2025-01-01' AND t.created_at < DATE '2025-07-01' GROUP BY st.name ) b ON b.name = stn.name CROSS JOIN ( SELECT (SELECT COUNT(*) FROM ticket_tags_map ttm JOIN tickets t ON t.id = ttm.ticket_id WHERE t.created_at >= DATE '2025-07-01' AND t.created_at < DATE '2026-01-01') AS total_a, (SELECT COUNT(*) FROM ticket_tags_map ttm JOIN tickets t ON t.id = ttm.ticket_id WHERE t.created_at >= DATE '2025-01-01' AND t.created_at < DATE '2025-07-01') AS total_b ) totals WHERE COALESCE(a.cnt,0) > 0 OR COALESCE(b.cnt,0) > 0 ORDER BY tickets_last_6mo DESC NULLS FIRST, tag_name ASC NULLS LAST FETCH FIRST 50 ROWS ONLY
SELECT stn.name AS tag_name, COALESCE(a.cnt,0) AS tickets_last_6mo, COALESCE(b.cnt,0) AS tickets_prior_6mo, COALESCE(a.cnt,0) * 100.0 / NULLIF(totals.total_a,0) AS share_last_6mo_pct, COALESCE(b.cnt,0) * 100.0 / NULLIF(totals.total_b,0) AS share_prior_6mo_pct, COALESCE(a.cnt,0) * 100.0 / NULLIF(totals.total_a,0) - COALESCE(b.cnt,0) * 100.0 / NULLIF(totals.total_b,0) AS share_change_pp, a.avg_res_hours AS avg_resolution_hours_last_6mo FROM (SELECT DISTINCT name FROM support_tags) stn LEFT JOIN ( SELECT st.name AS name, COUNT(*) AS cnt, AVG(CAST(EXTRACT(EPOCH FROM (t.resolved_at - t.created_at)) AS NUMERIC(18,6)) / 3600.0) AS avg_res_hours FROM ticket_tags_map ttm JOIN tickets t ON t.id = ttm.ticket_id JOIN support_tags st ON st.id = ttm.support_tag_id WHERE t.created_at >= DATE '2025-07-01' AND t.created_at < DATE '2026-01-01' GROUP BY st.name ) a ON a.name = stn.name LEFT JOIN ( SELECT st.name AS name, COUNT(*) AS cnt FROM ticket_tags_map ttm JOIN tickets t ON t.id = ttm.ticket_id JOIN support_tags st ON st.id = ttm.support_tag_id WHERE t.created_at >= DATE '2025-01-01' AND t.created_at < DATE '2025-07-01' GROUP BY st.name ) b ON b.name = stn.name CROSS JOIN ( SELECT (SELECT COUNT(*) FROM ticket_tags_map ttm JOIN tickets t ON t.id = ttm.ticket_id WHERE t.created_at >= DATE '2025-07-01' AND t.created_at < DATE '2026-01-01') AS total_a, (SELECT COUNT(*) FROM ticket_tags_map ttm JOIN tickets t ON t.id = ttm.ticket_id WHERE t.created_at >= DATE '2025-01-01' AND t.created_at < DATE '2025-07-01') AS total_b ) totals WHERE COALESCE(a.cnt,0) > 0 OR COALESCE(b.cnt,0) > 0 ORDER BY tickets_last_6mo DESC NULLS FIRST, tag_name ASC NULLS LAST LIMIT 50
SELECT stn.name AS tag_name, COALESCE(a.cnt,0) AS tickets_last_6mo, COALESCE(b.cnt,0) AS tickets_prior_6mo, COALESCE(a.cnt,0) * 100.0 / NULLIF(totals.total_a,0) AS share_last_6mo_pct, COALESCE(b.cnt,0) * 100.0 / NULLIF(totals.total_b,0) AS share_prior_6mo_pct, COALESCE(a.cnt,0) * 100.0 / NULLIF(totals.total_a,0) - COALESCE(b.cnt,0) * 100.0 / NULLIF(totals.total_b,0) AS share_change_pp, a.avg_res_hours AS avg_resolution_hours_last_6mo FROM (SELECT DISTINCT name FROM support_tags) stn LEFT JOIN ( SELECT st.name AS name, COUNT(*) AS cnt, AVG(CAST(divideDecimal(toDecimal128(dateDiff('microsecond', t.created_at, t.resolved_at), 0), toDecimal128(1000000, 0), 6) AS Nullable(Decimal(18, 6))) / 3600.0) AS avg_res_hours FROM ticket_tags_map ttm JOIN tickets t ON t.id = ttm.ticket_id JOIN support_tags st ON st.id = ttm.support_tag_id WHERE t.created_at >= DATE '2025-07-01' AND t.created_at < DATE '2026-01-01' GROUP BY st.name ) a ON a.name = stn.name LEFT JOIN ( SELECT st.name AS name, COUNT(*) AS cnt FROM ticket_tags_map ttm JOIN tickets t ON t.id = ttm.ticket_id JOIN support_tags st ON st.id = ttm.support_tag_id WHERE t.created_at >= DATE '2025-01-01' AND t.created_at < DATE '2025-07-01' GROUP BY st.name ) b ON b.name = stn.name CROSS JOIN ( SELECT (SELECT COUNT(*) FROM ticket_tags_map ttm JOIN tickets t ON t.id = ttm.ticket_id WHERE t.created_at >= DATE '2025-07-01' AND t.created_at < DATE '2026-01-01') AS total_a, (SELECT COUNT(*) FROM ticket_tags_map ttm JOIN tickets t ON t.id = ttm.ticket_id WHERE t.created_at >= DATE '2025-01-01' AND t.created_at < DATE '2025-07-01') AS total_b ) totals WHERE COALESCE(a.cnt,0) > 0 OR COALESCE(b.cnt,0) > 0 ORDER BY tickets_last_6mo DESC NULLS FIRST, tag_name ASC NULLS LAST FETCH FIRST 50 ROWS ONLY
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 Mateo Ruiz (analyst for the VP Customer Experience): Title: Ticket topic mix and recent shift Question: What kinds of problems are shoppers contacting merchants about most, and has that mix shifted in the last 6 months versus the prior 6? Grain: one row per support tag (platform-wide, tag name) Columns: - tag name - tickets, last 6 months (Jul-Dec 2025) - tickets, prior 6 months (Jan-Jun 2025) - share of tickets, last 6 months (%) - share of tickets, prior 6 months (%) - share change (percentage points) - avg resolution hours, last 6 months Details: Source: tickets joined to ticket_tags_map and support_tags (group by support_tags.name across all tenants, since we want the platform-level theme). A ticket can carry multiple tags; count each ticket once per tag it carries (so a ticket with 2 tags contributes to both tag rows - note this in the report so totals don't have to sum to ticket volume). Period A = tickets.created_at between 2025-07-01 and 2025-12-31 inclusive; period B = 2025-01-01 to 2025-06-30 inclusive. Share of tickets = tag's ticket count in that period / total tagged-ticket-tag-rows in that period, as a percentage rounded to 1 decimal. Share change = share in period A minus share in period B, in percentage points, rounded to 1 decimal. avg resolution hours (period A only) = extract(epoch from resolved_at - created_at)/3600 averaged over that tag's resolved tickets in period A, rounded to 1 decimal. Order by tickets in the last 6 months descending, tie-break by tag name ascending. Report total distinct tag count returned (expect well under 50 rows). Check your query with the warehouse's dry_run tool, then hand it in with submit.
dry_runSELECT stn.name AS tag_name, COALESCE(a.cnt,0) AS tickets_last_6mo, COALESCE(b.cnt,0) AS tickets_prior_6mo, ROUND(COALESCE(a.cnt,0) * 100.0 / NULLIF(totals.total_a,0), 1) AS share_last_6mo_pct, ROUND(COALESCE(b.cnt,0) * 100.0 / NULLIF(totals.total_b,0), 1) AS share_prior_6mo_pct, ROUND( COALESCE(a.cnt,0) * 100.0 / NULLIF(totals.total_a,0) - COALESCE(b.cnt,0) * 100.0 / NULLIF(totals.total_b,0) , 1) AS share_change_pp, ROUND(a.avg_res_hours, 1) AS avg_resolution_hours_last_6mo FROM (SELECT DISTINCT name FROM support_tags) stn LEFT JOIN ( SELECT st.name AS name, COUNT(*) AS cnt, AVG(EXTRACT(EPOCH FROM (t.resolved_at - t.created_at)) / 3600.0) AS avg_res_hours FROM ticket_tags_map ttm JOIN tickets t ON t.id = ttm.ticket_id JOIN support_tags st ON st.id = ttm.support_tag_id WHERE t.created_at >= DATE '2025-07-01' AND t.created_at < DATE '2026-01-01' GROUP BY st.name ) a ON a.name = stn.name LEFT JOIN ( SELECT st.name AS name, COUNT(*) AS cnt FROM ticket_tags_map ttm JOIN tickets t ON t.id = ttm.ticket_id JOIN support_tags st ON st.id = ttm.support_tag_id WHERE t.created_at >= DATE '2025-01-01' AND t.created_at < DATE '2025-07-01' GROUP BY st.name ) b ON b.name = stn.name CROSS JOIN ( SELECT (SELECT COUNT(*) FROM ticket_tags_map ttm JOIN tickets t ON t.id = ttm.ticket_id WHERE t.created_at >= DATE '2025-07-01' AND t.created_at < DATE '2026-01-01') AS total_a, (SELECT COUNT(*) FROM ticket_tags_map ttm JOIN tickets t ON t.id = ttm.ticket_id WHERE t.created_at >= DATE '2025-01-01' AND t.created_at < DATE '2025-07-01') AS total_b ) totals WHERE COALESCE(a.cnt,0) > 0 OR COALESCE(b.cnt,0) > 0 ORDER BY tickets_last_6mo DESC, tag_name ASC LIMIT 50
STATUS: COMPILES (dry run on the empty schema, no data read) COLUMNS: tag_name, tickets_last_6mo, tickets_prior_6mo, share_last_6mo_pct, share_prior_6mo_pct, share_change_pp, avg_resolution_hours_last_6mo
submitSELECT stn.name AS tag_name, COALESCE(a.cnt,0) AS tickets_last_6mo, COALESCE(b.cnt,0) AS tickets_prior_6mo, ROUND(COALESCE(a.cnt,0) * 100.0 / NULLIF(totals.total_a,0), 1) AS share_last_6mo_pct, ROUND(COALESCE(b.cnt,0) * 100.0 / NULLIF(totals.total_b,0), 1) AS share_prior_6mo_pct, ROUND( COALESCE(a.cnt,0) * 100.0 / NULLIF(totals.total_a,0) - COALESCE(b.cnt,0) * 100.0 / NULLIF(totals.total_b,0) , 1) AS share_change_pp, ROUND(a.avg_res_hours, 1) AS avg_resolution_hours_last_6mo FROM (SELECT DISTINCT name FROM support_tags) stn LEFT JOIN ( SELECT st.name AS name, COUNT(*) AS cnt, AVG(EXTRACT(EPOCH FROM (t.resolved_at - t.created_at)) / 3600.0) AS avg_res_hours FROM ticket_tags_map ttm JOIN tickets t ON t.id = ttm.ticket_id JOIN support_tags st ON st.id = ttm.support_tag_id WHERE t.created_at >= DATE '2025-07-01' AND t.created_at < DATE '2026-01-01' GROUP BY st.name ) a ON a.name = stn.name LEFT JOIN ( SELECT st.name AS name, COUNT(*) AS cnt FROM ticket_tags_map ttm JOIN tickets t ON t.id = ttm.ticket_id JOIN support_tags st ON st.id = ttm.support_tag_id WHERE t.created_at >= DATE '2025-01-01' AND t.created_at < DATE '2025-07-01' GROUP BY st.name ) b ON b.name = stn.name CROSS JOIN ( SELECT (SELECT COUNT(*) FROM ticket_tags_map ttm JOIN tickets t ON t.id = ttm.ticket_id WHERE t.created_at >= DATE '2025-07-01' AND t.created_at < DATE '2026-01-01') AS total_a, (SELECT COUNT(*) FROM ticket_tags_map ttm JOIN tickets t ON t.id = ttm.ticket_id WHERE t.created_at >= DATE '2025-01-01' AND t.created_at < DATE '2025-07-01') AS total_b ) totals WHERE COALESCE(a.cnt,0) > 0 OR COALESCE(b.cnt,0) > 0 ORDER BY tickets_last_6mo DESC, tag_name ASC LIMIT 50
Submitted to tonight's batch. Your work on this request is done.