DB Olympics

Queries · q250 · VP Customer Experience · week 2, night 3

Quarterly urgent-ticket performance, hotspot merchants vs comparison peer

“Does urgent-ticket resolution specifically get worse in the same quarters where each merchant's ticket-per-agent load spiked?”

Manager · asked the questionDaniel AchterbergVP Customer Experience. Former head of support at a marketplace. Merchants run their helpdesk on Tidewick, so their shoppers' experience is partly Tidewick's reputation.
Analyst · planned the reportMateo RuizComparative: likes year-over-year and peer comparisons across merchants and countries.
Engineer · wrote the SQLViktorOld-school warehouse engineer: explicit JOIN chains straight to the base tables, derived tables, GROUP BY with CASE pivots, few CTEs.

Fictional people at a fictional company, each played by a language-model agent. Who they are and what they are told

  1. Daniel Achterberg writes to Mateo Ruiz

    The manager, at the start of week 2

    Subject: Good progress — now I want to understand the "why" behind the hotspots

    Mateo — this is exactly the kind of baseline I needed, thank you. Two things from your memo and Mia's notes are sitting with me:

    First, the speed→satisfaction link is strong and now confirmed two ways (yours and Mia's numbers line up almost exactly), so I believe it. But I don't yet know if slowness causes the dissatisfaction or if the worst merchants are simply worse across the board (harder products, different shopper expectations, something else) and speed is just the symptom we can see. That matters a lot for what I recommend as the fix.

    Second, Mia's finding that three merchants account for a third of all tickets and the worst response times lines up with what you found independently — that's a strong signal, not a coincidence, and it's where I want to spend our next slots.

    Questions for this week:

    1. What's actually different about how Cragsley, Marrowby, and Studio Vrij run their support compared to the rest of the book — are they understaffed relative to their ticket volume, leaning on slower channels, getting hit with harder/more complex issues, or something else? I want to know if this is something we can fix with guidance or tooling, or if it's specific to each merchant's situation.
    2. Is the urgent-priority failure (missing resolution half the time) concentrated in those same few merchants, or is it a broader pattern across the book? If urgent tickets are failing everywhere, that's a different and more worrying problem than a handful of struggling merchants.
    3. Has Studio Vrij actually recovered since the spring breakdown, and what would it take to get ahead of the Nov/Dec pattern this year before it hits? I'd like to walk into December with a plan, not another postmortem.
    4. Before I take "respond faster" to merchants as the headline fix, can we check whether the slowest merchants differ in other ways (ticket mix, how support is staffed, shopper profile) that might also explain their low satisfaction? I don't want to oversimplify the story if something else is going on underneath.

    Let's prioritize #1 and #2 this week — those feel most actionable. #3 and #4 can wait if slots are tight.

    Thanks, Daniel

  2. Mateo Ruiz plans the night

    The analyst · week 2, night 3, request 1 of 3

    Night 2 results are decisive on complexity: Cragsley (1.36 tags/ticket, 3.46 messages/ticket) and Marrowby (1.43, 3.29) are actually at the LOW end of complexity among all 22 qualifying merchants - Studio Vrij is mid-pack (1.64, 3.71). So "harder issues" is refuted as an explanation; it really does look like a volume/staffing story. The SLA-target report also refutes the "unrealistic target" theory for the broad urgent-miss problem - every merchant evaluated uses the identical 8-hour urgent target, so the wide spread in miss rates (14%-100%) reflects genuine differences in execution speed, not target calibration. The quarterly load report is the key new finding: all three hotspots (and comparison peer Grind Collective) run a completely flat agent headcount all four quarters, but ticket volume surges hard in specific quarters - Cragsley and Marrowby both spike in Q4 (tickets-per-agent roughly doubles vs Q1-Q3), Studio Vrij spikes specifically in Q2 (the already-known fulfillment incident), and Grind Collective also spikes in Q4 even harder than Cragsley. That's a strong lead: nobody scales support staff for volume surges, and it's not unique to the three named merchants. I want to (1) confirm urgent-ticket performance specifically degrades in the same quarters where each merchant's load spiked (a within-merchant causal test), (2) check whether this "flat staffing through a seasonal/incident surge" pattern is common across the whole qualifying book, not just these four - that determines if it's a platform-level guidance opportunity, and (3) characterize whether the big three's inflated average (vs. median) urgent resolution time is a few catastrophic outlier tickets or generally slow handling, since that changes what the fix would look like (triage/escalation vs. core speed).

    Quarterly urgent-ticket performance, hotspot merchants vs comparison peer
    Question
    Does urgent-ticket resolution specifically get worse in the same quarters where each merchant's ticket-per-agent load spiked?
    One row per
    tenant and calendar quarter in 2025, for Cragsley Outfitters, Marrowby & Vane, Studio Vrij, and Grind Collective, urgent-priority tickets only
    Columns
    • merchant name
    • quarter (2025-Q1..Q4)
    • urgent tickets evaluated (sla_policy_id not null)
    • urgent miss rate (%)
    • avg actual resolution hours (urgent)
    • median actual resolution hours (urgent)
  3. Viktor writes the SQL

    The engineer · 1 dry run on the empty schema, then the submission

    Dry run 1 · STATUS: COMPILES (dry run on the empty schema, no data read)
    WITH q AS (
      SELECT 1 AS q_num, date '2025-01-01' AS q_start, date '2025-04-01' AS q_end
      UNION ALL SELECT 2, date '2025-04-01', date '2025-07-01'
      UNION ALL SELECT 3, date '2025-07-01', date '2025-10-01'
      UNION ALL SELECT 4, date '2025-10-01', date '2026-01-01'
    ),
    m AS (
      SELECT id, name FROM tenants WHERE name IN ('Cragsley Outfitters','Marrowby & Vane','Studio Vrij','Grind Collective')
    )
    SELECT
      m.name AS merchant_name,
      '2025-Q' || q.q_num AS quarter,
      count(tk.id) AS urgent_tickets_evaluated,
      round(
        100.0 * sum(
          case when tk.id is not null
                 and (tk.resolved_at is null or tk.resolved_at > tk.created_at + (sp.resolution_target_minutes || ' minutes')::interval)
               then 1 else 0 end
        )::numeric / nullif(count(tk.id), 0)
      , 1) AS urgent_miss_rate_pct,
      round(
        avg(case when tk.resolved_at is not null then extract(epoch from tk.resolved_at - tk.created_at) / 3600.0 end)::numeric
      , 1) AS avg_actual_resolution_hours,
      round(
        percentile_cont(0.5) within group (
          order by case when tk.resolved_at is not null then extract(epoch from tk.resolved_at - tk.created_at) / 3600.0 end
        )::numeric
      , 1) AS median_actual_resolution_hours
    FROM m
    CROSS JOIN q
    LEFT JOIN tickets tk
      ON tk.tenant_id = m.id
      AND tk.priority = 'urgent'
      AND tk.sla_policy_id IS NOT NULL
      AND tk.created_at >= q.q_start AND tk.created_at < q.q_end
    LEFT JOIN sla_policies sp ON sp.id = tk.sla_policy_id
    GROUP BY m.name, q.q_num
    ORDER BY m.name ASC, q.q_num ASC

    “Query compiles and returns the expected columns. Submitting.”

    Submitted · the SQL of the last dry run, unchanged · Submitted to tonight's batch. Your work on this request is done.

    “Submitted. The report builds a complete 4-tenant × 4-quarter (2025-Q1..Q4) grid via a cross join, left-joining urgent/evaluable tickets so quarters with zero evaluable urgent tickets still appear with explicit zero/NULL values rather than being dropped — satisfying the "expect 16 rows, note zero-ticket quarters" requirement directly in the data rather than as a caveat.”

  4. The report comes back overnight

    As Mateo Ruiz saw it, from the small dataset the agents worked on

    merchant_name       | quarter | urgent_tickets_evaluated | urgent_miss_rate_pct | avg_actual_resolution_hours | median_actual_resolution_hours
    --------------------+---------+--------------------------+----------------------+-----------------------------+-------------------------------
    Cragsley Outfitters | 2025-Q1 |                       14 |                 71.4 |                        30.0 |                           10.5
    Cragsley Outfitters | 2025-Q2 |                       13 |                 53.8 |                        18.0 |                           14.0
    Cragsley Outfitters | 2025-Q3 |                        8 |                 50.0 |                        23.1 |                            9.8
    Cragsley Outfitters | 2025-Q4 |                       15 |                 80.0 |                        36.6 |                           21.5
    Grind Collective    | 2025-Q1 |                        1 |                  0.0 |                         0.8 |                            0.8
    Grind Collective    | 2025-Q2 |                        1 |                  0.0 |                         0.9 |                            0.9
    Grind Collective    | 2025-Q3 |                        1 |                  0.0 |                         1.0 |                            1.0
    Grind Collective    | 2025-Q4 |                        3 |                 33.3 |                         7.0 |                            7.1
    Marrowby & Vane     | 2025-Q1 |                        8 |                 62.5 |                        43.6 |                           24.8
    Marrowby & Vane     | 2025-Q2 |                        2 |                 50.0 |                        16.9 |                           16.9
    Marrowby & Vane     | 2025-Q3 |                        6 |                 50.0 |                         8.8 |                            6.4
    Marrowby & Vane     | 2025-Q4 |                       17 |                 76.5 |                       139.5 |                           87.2
    Studio Vrij         | 2025-Q1 |                        4 |                 50.0 |                        17.4 |                           10.7
    Studio Vrij         | 2025-Q2 |                        2 |                100.0 |                       219.5 |                          219.5
    Studio Vrij         | 2025-Q3 |                        2 |                100.0 |                       109.0 |                          109.0
    Studio Vrij         | 2025-Q4 |                        3 |                 33.3 |                         3.9 |                            1.2
    (16 rows)
  5. What came of it

    The memo and the queries that followed; links found by title, so inferred

    Later queries that build on this report:

    It builds on:

  6. It becomes benchmark query q250

    Changed only where the benchmark needs the same result on every system

    • editAn interval built from text is PostgreSQL's; the standard's n * INTERVAL '1' MINUTE is not supported by MySQL, T-SQL or SQLite either, so adding minutes to a timestamp is a placeholder whose default is the standard form (docs/RUNNER.md, Dialects).
    • spellingcast (3×)
    • numericsdocs/NUMERICS.md: rounding removed; results keep full precision; bare NUMERIC cast that only served the rounding removed; intended fraction: integer numerator cast to NUMERIC(18,6) before the division
    • numericsdocs/NUMERICS.md: EXTRACT(EPOCH FROM (b - a)) cast to NUMERIC(18,6) (2×)
    • placeholderpercentile cont
    • placeholderseconds between (2×)
    • determinismthe standard leaves the place of NULLs in a sort to the implementation: NULLS LAST ascending, NULLS FIRST descending, as in the reference results (2×)
    The benchmark text, against the submitted SQL
    WITH q AS (  SELECT 1 AS q_num, date '2025-01-01' AS q_start, date '2025-04-01' AS q_end  UNION ALL SELECT 2, date '2025-04-01', date '2025-07-01'  UNION ALL SELECT 3, date '2025-07-01', date '2025-10-01'  UNION ALL SELECT 4, date '2025-10-01', date '2026-01-01'),m AS (  SELECT id, name FROM tenants WHERE name IN ('Cragsley Outfitters','Marrowby & Vane','Studio Vrij','Grind Collective'))SELECT  m.name AS merchant_name,  '2025-Q' || q.q_num AS quarter,  count(tk.id) AS urgent_tickets_evaluated,  round(    100.0 * sum(      case when tk.id is not null             and (tk.resolved_at is null or tk.resolved_at > tk.created_at + (sp.resolution_target_minutes || ' minutes')::interval)           then 1 else 0 end    )::numeric / nullif(count(tk.id), 0)  , 1) AS urgent_miss_rate_pct,  round(    avg(case when tk.resolved_at is not null then extract(epoch from tk.resolved_at - tk.created_at) / 3600.0 end)::numeric  , 1) AS avg_actual_resolution_hours,  round(    percentile_cont(0.5) within group (      order by case when tk.resolved_at is not null then extract(epoch from tk.resolved_at - tk.created_at) / 3600.0 end    )::numeric  , 1) AS median_actual_resolution_hours  100.0 * CAST(sum(    case when tk.id is not null           and (tk.resolved_at is null or tk.resolved_at > {{add_minutes(tk.created_at, sp.resolution_target_minutes)}})         then 1 else 0 end  ) AS numeric(18,6)) / nullif(count(tk.id), 0) AS urgent_miss_rate_pct,  avg(case when tk.resolved_at is not null then {{seconds_between(tk.created_at, tk.resolved_at)}} / 3600.0 end) AS avg_actual_resolution_hours,  {{percentile_cont(0.5, case when tk.resolved_at is not null then {{seconds_between(tk.created_at, tk.resolved_at)}} / 3600.0 end)}} AS median_actual_resolution_hoursFROM mCROSS JOIN qLEFT JOIN tickets tk  ON tk.tenant_id = m.id  AND tk.priority = 'urgent'  AND tk.sla_policy_id IS NOT NULL  AND tk.created_at >= q.q_start AND tk.created_at < q.q_endLEFT JOIN sla_policies sp ON sp.id = tk.sla_policy_idGROUP BY m.name, q.q_numORDER BY m.name ASC, q.q_num ASCORDER BY m.name ASC NULLS LAST, q.q_num ASC NULLS LAST
    WITH q AS (
      SELECT 1 AS q_num, date '2025-01-01' AS q_start, date '2025-04-01' AS q_end
      UNION ALL SELECT 2, date '2025-04-01', date '2025-07-01'
      UNION ALL SELECT 3, date '2025-07-01', date '2025-10-01'
      UNION ALL SELECT 4, date '2025-10-01', date '2026-01-01'
    ),
    m AS (
      SELECT id, name FROM tenants WHERE name IN ('Cragsley Outfitters','Marrowby & Vane','Studio Vrij','Grind Collective')
    )
    SELECT
      m.name AS merchant_name,
      '2025-Q' || q.q_num AS quarter,
      count(tk.id) AS urgent_tickets_evaluated,
      100.0 * CAST(sum(
        case when tk.id is not null
               and (tk.resolved_at is null or tk.resolved_at > tk.created_at + sp.resolution_target_minutes * INTERVAL '1' MINUTE)
             then 1 else 0 end
      ) AS numeric(18,6)) / nullif(count(tk.id), 0) AS urgent_miss_rate_pct,
      avg(case when tk.resolved_at is not null then CAST(EXTRACT(EPOCH FROM (tk.resolved_at - tk.created_at)) AS NUMERIC(18,6)) / 3600.0 end) AS avg_actual_resolution_hours,
      PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY case when tk.resolved_at is not null then CAST(EXTRACT(EPOCH FROM (tk.resolved_at - tk.created_at)) AS NUMERIC(18,6)) / 3600.0 end) AS median_actual_resolution_hours
    FROM m
    CROSS JOIN q
    LEFT JOIN tickets tk
      ON tk.tenant_id = m.id
      AND tk.priority = 'urgent'
      AND tk.sla_policy_id IS NOT NULL
      AND tk.created_at >= q.q_start AND tk.created_at < q.q_end
    LEFT JOIN sla_policies sp ON sp.id = tk.sla_policy_id
    GROUP BY m.name, q.q_num
    ORDER BY m.name ASC NULLS LAST, q.q_num ASC NULLS LAST
    1 ms10 ms100 msHyper19.2 msDuckDB9.5 msGizmoSQL15.2 msclickhouse_brew58.3 msFirebolt55.9 msDataFusionerrorClickHouse79.3 ms
    • DataFusionError during planning: Cannot get result type for temporal operation Int32 * Interval(MonthDayNano): Invalid argument error: Invalid interval arithmetic operation: Interval(MonthDayNano) * Interval(MonthDayNano)
  7. Everything, as recorded

    The agents' own records, as the role play wrote them; only the personal data of the person who ran it is redacted

    Viktor's whole session, 7 turns
    • The request, as Viktor received it
      Report request from Mateo Ruiz (analyst for the VP Customer Experience):
      
      Title: Quarterly urgent-ticket performance, hotspot merchants vs comparison peer
      Question: Does urgent-ticket resolution specifically get worse in the same quarters where each merchant's ticket-per-agent load spiked?
      Grain: one row per tenant and calendar quarter in 2025, for Cragsley Outfitters, Marrowby & Vane, Studio Vrij, and Grind Collective, urgent-priority tickets only
      Columns:
      - merchant name
      - quarter (2025-Q1..Q4)
      - urgent tickets evaluated (sla_policy_id not null)
      - urgent miss rate (%)
      - avg actual resolution hours (urgent)
      - median actual resolution hours (urgent)
      Details: Source: tickets (priority='urgent') joined to sla_policies via tickets.sla_policy_id, filtered to the four named tenants. Grain the quarter on tickets.created_at within 2025. Exclude tickets with null sla_policy_id from all columns (same 'evaluable' definition as prior urgent-SLA reports). 'Miss rate' = tickets missing the resolution target (resolved_at null as of 2025-12-31, or resolved_at later than created_at + resolution_target_minutes), as a percentage of evaluable urgent tickets that quarter, rounded to 1 decimal. avg/median actual resolution hours = extract(epoch from resolved_at - created_at)/3600 over evaluable urgent tickets with resolved_at not null that quarter, rounded to 1 decimal (show NULL if zero such tickets). Order by merchant name ascending, then quarter ascending. Expect 16 rows; note if any quarter has zero evaluable urgent tickets for a tenant.
      
      Check your query with the warehouse's dry_run tool, then hand it in with submit.
    • Viktor calls dry_run
      WITH q AS (
        SELECT 1 AS q_num, date '2025-01-01' AS q_start, date '2025-04-01' AS q_end
        UNION ALL SELECT 2, date '2025-04-01', date '2025-07-01'
        UNION ALL SELECT 3, date '2025-07-01', date '2025-10-01'
        UNION ALL SELECT 4, date '2025-10-01', date '2026-01-01'
      ),
      m AS (
        SELECT id, name FROM tenants WHERE name IN ('Cragsley Outfitters','Marrowby & Vane','Studio Vrij','Grind Collective')
      )
      SELECT
        m.name AS merchant_name,
        '2025-Q' || q.q_num AS quarter,
        count(tk.id) AS urgent_tickets_evaluated,
        round(
          100.0 * sum(
            case when tk.id is not null
                   and (tk.resolved_at is null or tk.resolved_at > tk.created_at + (sp.resolution_target_minutes || ' minutes')::interval)
                 then 1 else 0 end
          )::numeric / nullif(count(tk.id), 0)
        , 1) AS urgent_miss_rate_pct,
        round(
          avg(case when tk.resolved_at is not null then extract(epoch from tk.resolved_at - tk.created_at) / 3600.0 end)::numeric
        , 1) AS avg_actual_resolution_hours,
        round(
          percentile_cont(0.5) within group (
            order by case when tk.resolved_at is not null then extract(epoch from tk.resolved_at - tk.created_at) / 3600.0 end
          )::numeric
        , 1) AS median_actual_resolution_hours
      FROM m
      CROSS JOIN q
      LEFT JOIN tickets tk
        ON tk.tenant_id = m.id
        AND tk.priority = 'urgent'
        AND tk.sla_policy_id IS NOT NULL
        AND tk.created_at >= q.q_start AND tk.created_at < q.q_end
      LEFT JOIN sla_policies sp ON sp.id = tk.sla_policy_id
      GROUP BY m.name, q.q_num
      ORDER BY m.name ASC, q.q_num ASC
    • The warehouse answers
      STATUS: COMPILES (dry run on the empty schema, no data read)
      COLUMNS: merchant_name, quarter, urgent_tickets_evaluated, urgent_miss_rate_pct, avg_actual_resolution_hours, median_actual_resolution_hours
    • ViktorQuery compiles and returns the expected columns. Submitting.
    • Viktor calls submit
      WITH q AS (
        SELECT 1 AS q_num, date '2025-01-01' AS q_start, date '2025-04-01' AS q_end
        UNION ALL SELECT 2, date '2025-04-01', date '2025-07-01'
        UNION ALL SELECT 3, date '2025-07-01', date '2025-10-01'
        UNION ALL SELECT 4, date '2025-10-01', date '2026-01-01'
      ),
      m AS (
        SELECT id, name FROM tenants WHERE name IN ('Cragsley Outfitters','Marrowby & Vane','Studio Vrij','Grind Collective')
      )
      SELECT
        m.name AS merchant_name,
        '2025-Q' || q.q_num AS quarter,
        count(tk.id) AS urgent_tickets_evaluated,
        round(
          100.0 * sum(
            case when tk.id is not null
                   and (tk.resolved_at is null or tk.resolved_at > tk.created_at + (sp.resolution_target_minutes || ' minutes')::interval)
                 then 1 else 0 end
          )::numeric / nullif(count(tk.id), 0)
        , 1) AS urgent_miss_rate_pct,
        round(
          avg(case when tk.resolved_at is not null then extract(epoch from tk.resolved_at - tk.created_at) / 3600.0 end)::numeric
        , 1) AS avg_actual_resolution_hours,
        round(
          percentile_cont(0.5) within group (
            order by case when tk.resolved_at is not null then extract(epoch from tk.resolved_at - tk.created_at) / 3600.0 end
          )::numeric
        , 1) AS median_actual_resolution_hours
      FROM m
      CROSS JOIN q
      LEFT JOIN tickets tk
        ON tk.tenant_id = m.id
        AND tk.priority = 'urgent'
        AND tk.sla_policy_id IS NOT NULL
        AND tk.created_at >= q.q_start AND tk.created_at < q.q_end
      LEFT JOIN sla_policies sp ON sp.id = tk.sla_policy_id
      GROUP BY m.name, q.q_num
      ORDER BY m.name ASC, q.q_num ASC
    • The warehouse answers
      Submitted to tonight's batch. Your work on this request is done.
    • ViktorSubmitted. The report builds a complete 4-tenant × 4-quarter (2025-Q1..Q4) grid via a cross join, left-joining urgent/evaluable tickets so quarters with zero evaluable urgent tickets still appear with explicit zero/NULL values rather than being dropped — satisfying the "expect 16 rows, note zero-ticket quarters" requirement directly in the data rather than as a caveat.