DB Olympics

Queries · q294 · VP Product · week 2, night 3

Daily cart conversion at Cragsley Outfitters, Nov 17 – Dec 1 2025

“Is there a visible conversion dip in the first few days after cart-reminder emails broke (Nov 24), before Black Friday/Cyber Monday demand likely swamps the signal?”

Manager · asked the questionJonas FeldVP Product. Joined from a mobile-first retailer. Deciding the 2026 roadmap.
Analyst · planned the reportEmeka ObiCustomer-centric: follows cohorts of shoppers or merchants over time.
Engineer · wrote the SQLInesThinks in window functions: ranks, running totals, LAG/LEAD for comparisons, percent-of-total.

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

  1. A business event lands on the VP Product's desk

    Week 1

    We are redesigning the shopper journey for 2026. We need to understand the whole path, from the first visit to checkout, delivery, returns and the next purchase, and where shoppers drop out and why.

  2. Jonas Feld writes to Emeka Obi

    The manager, at the start of week 2

    Subject: Good start — let's dig into the cart leak

    Emeka, this is exactly the kind of clarity I needed. The cart-stage bleed is the headline, and I agree we should lead with it — but a two-thirds drop-off that lingers for days, converting at under 20%, isn't something we can act on until we know why people stall. "Add cart recovery emails" is a guess until we understand the shape of the hesitation. That's my main ask for this week.

    I also want to flag one thing before we build a roadmap slide on repeat purchase and returns: the return-vs-repeat gap and the delivery/returns pattern are both flagged as not-yet-causal, confounded by vertical. I don't want to present an 8-point "returns hurt loyalty" number to the exec team if it's really just "apparel behaves differently." Let's firm that up before it goes in a deck, but it's not urgent this week.

    Questions for this week:

    1. What does a stalled cart actually contain, and does it differ from a fast-converting one? I'm thinking about basket size, price point, discount/coupon presence — anything that hints at why someone hesitates for hours or days instead of minutes.
    2. Is the cart bleed getting better, worse, or flat over the past year? If it's been stable at ~70% for two years, that's a structural product problem; if it's worsening recently, something changed (a checkout step, a fee, a release) that we should find and reverse.
    3. Of the carts that eventually do convert after sitting a while, is there a nudge already happening (a reminder, a return visit, a discount) that we could learn from and do more deliberately? I want to know if recovery is already happening organically before we invest in prompting it.
    4. On the subscription merchants' tracking gap — is this just a blind spot in how we instrument recurring orders, or is it telling us something about how those shoppers actually behave (e.g., they never browse again after the first signup)? A quick gut-check is enough this week; no need for a deep investigation yet.

    Keep the returns/delivery vertical-control work and the causal retention question on the list for whenever a slot frees up, but the cart story is what I want to walk into the roadmap review with real conviction on.

    Thanks, Emeka — strong week.

    Jonas

  3. Emeka Obi plans the night

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

    Reading last night's reports:

    Report 1 (Cragsley weekly conversion around the Nov 24 outage) undercuts the simple story: conversion didn't dip during the broken-reminder week — it hit 43.5%, the highest week in the whole window, up from a 29-41% range beforehand. The obvious explanation is that Nov 24-30, 2025 is Black Friday/Cyber Monday week — a massive demand spike that would swamp any negative effect from broken reminder emails. So this natural experiment is confounded by timing and can't cleanly tell us whether the reminders matter; I don't think it's worth forcing further at the weekly grain, but a daily cut around the exact outage start (before Black Friday really kicks in on Nov 28) might still show something, so I'll try that once.

    Report 2 (price tier vs. conversion) reinforces the Q1 story on a different cut: higher-priced carts (which are also higher-item-count: 1.3 items at <€50 vs 3.2 items at €200+) convert better and faster (51.4% / 44.1% within 1hr for €200+ vs 23.3% / 16.1% for <€50) — consistent with last night's finding that small, likely single-item, lower-value carts are where hesitation concentrates, not pricier "considered" purchases. Two independent cuts now agree, so I consider Q1 well-answered. Before I put "small single-item carts stall" in the memo as a general pattern, I want one quick robustness check that it's not just one merchant (e.g. Cragsley, which dominates volume) driving the aggregate.

    Report 3 (email nudge) shows a striking gap — 56.0% eventual conversion for carts whose user got an email vs. 8.1% for no-email — but it's likely confounded: 7,540 of the 12,280 "no_email" carts are guest carts that structurally can never receive an email (no user_id to target), and guests plausibly convert worse regardless of email. I want to split "no_email" into guest vs. logged-in before trusting this as evidence the nudge itself works, since the fair comparison is received_email vs. logged-in-but-not-emailed.

    Tonight's 3 requests: (1) the cleaner 3-way email-nudge split, (2) a daily drill-down at Cragsley right at the outage start to see if Black Friday is really masking a real dip, (3) a per-tenant robustness check that the single-item-cart-stalls pattern isn't just one merchant's effect.

    Daily cart conversion at Cragsley Outfitters, Nov 17 – Dec 1 2025
    Question
    Is there a visible conversion dip in the first few days after cart-reminder emails broke (Nov 24), before Black Friday/Cyber Monday demand likely swamps the signal?
    One row per
    calendar day of cart creation, Cragsley Outfitters only
    Columns
    • date
    • carts created
    • overall conversion pct
    • pct converted within 1 hour
  4. Ines 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)
    SELECT
      c.created_at::date AS cart_date,
      COUNT(*) AS carts_created,
      ROUND(100.0 * SUM(CASE WHEN c.status = 'converted' THEN 1 ELSE 0 END) / COUNT(*), 2) AS overall_conversion_pct,
      ROUND(100.0 * SUM(CASE WHEN c.status = 'converted' AND c.converted_at IS NOT NULL AND c.converted_at <= c.created_at + INTERVAL '1 hour' THEN 1 ELSE 0 END) / COUNT(*), 2) AS pct_converted_within_1_hour
    FROM carts c
    JOIN tenants t ON t.id = c.tenant_id
    WHERE t.name = 'Cragsley Outfitters'
      AND c.created_at >= DATE '2025-11-17'
      AND c.created_at < DATE '2025-12-02'
    GROUP BY c.created_at::date
    ORDER BY cart_date ASC

    “Query compiles and returns the expected columns.”

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

    “Submitted. The report computes, per calendar day of cart creation for Cragsley Outfitters (Nov 17–Dec 1, 2025): carts created, overall conversion % (status = 'converted'), and % converted within 1 hour of creation — ordered by date ascending, 15 rows total.”

  5. The report comes back overnight

    As Emeka Obi saw it, from the small dataset the agents worked on

    cart_date  | carts_created | overall_conversion_pct | pct_converted_within_1_hour
    -----------+---------------+------------------------+----------------------------
    2025-11-17 |            51 |                   45.1 |                       35.29
    2025-11-18 |            38 |                  52.63 |                       44.74
    2025-11-19 |            37 |                  37.84 |                       29.73
    2025-11-20 |            33 |                  36.36 |                        30.3
    2025-11-21 |            39 |                  41.03 |                        35.9
    2025-11-22 |            57 |                  36.84 |                       28.07
    2025-11-23 |            49 |                  38.78 |                       34.69
    2025-11-24 |            49 |                  36.73 |                       30.61
    2025-11-25 |            48 |                  31.25 |                       29.17
    2025-11-26 |            74 |                  43.24 |                       29.73
    2025-11-27 |            63 |                  47.62 |                       30.16
    2025-11-28 |           109 |                  43.12 |                        36.7
    2025-11-29 |            83 |                  48.19 |                       43.37
    2025-11-30 |            98 |                  46.94 |                       37.76
    2025-12-01 |           122 |                  45.08 |                       36.89
    (15 rows)
  6. 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:

  7. It becomes benchmark query q294

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

    • spellingcast (2×)
    • numericsdocs/NUMERICS.md: rounding removed; results keep full precision
    • determinismthe standard leaves the place of NULLs in a sort to the implementation: NULLS LAST ascending, NULLS FIRST descending, as in the reference results
    The benchmark text, against the submitted SQL
    SELECT  c.created_at::date AS cart_date,  CAST(c.created_at AS date) AS cart_date,  COUNT(*) AS carts_created,  ROUND(100.0 * SUM(CASE WHEN c.status = 'converted' THEN 1 ELSE 0 END) / COUNT(*), 2) AS overall_conversion_pct,  ROUND(100.0 * SUM(CASE WHEN c.status = 'converted' AND c.converted_at IS NOT NULL AND c.converted_at <= c.created_at + INTERVAL '1 hour' THEN 1 ELSE 0 END) / COUNT(*), 2) AS pct_converted_within_1_hour  100.0 * SUM(CASE WHEN c.status = 'converted' THEN 1 ELSE 0 END) / COUNT(*) AS overall_conversion_pct,  100.0 * SUM(CASE WHEN c.status = 'converted' AND c.converted_at IS NOT NULL AND c.converted_at <= c.created_at + INTERVAL '1 hour' THEN 1 ELSE 0 END) / COUNT(*) AS pct_converted_within_1_hourFROM carts cJOIN tenants t ON t.id = c.tenant_idWHERE t.name = 'Cragsley Outfitters'  AND c.created_at >= DATE '2025-11-17'  AND c.created_at < DATE '2025-12-02'GROUP BY c.created_at::dateORDER BY cart_date ASCGROUP BY CAST(c.created_at AS date)ORDER BY cart_date ASC NULLS LAST
    SELECT
      CAST(c.created_at AS date) AS cart_date,
      COUNT(*) AS carts_created,
      100.0 * SUM(CASE WHEN c.status = 'converted' THEN 1 ELSE 0 END) / COUNT(*) AS overall_conversion_pct,
      100.0 * SUM(CASE WHEN c.status = 'converted' AND c.converted_at IS NOT NULL AND c.converted_at <= c.created_at + INTERVAL '1 hour' THEN 1 ELSE 0 END) / COUNT(*) AS pct_converted_within_1_hour
    FROM carts c
    JOIN tenants t ON t.id = c.tenant_id
    WHERE t.name = 'Cragsley Outfitters'
      AND c.created_at >= DATE '2025-11-17'
      AND c.created_at < DATE '2025-12-02'
    GROUP BY CAST(c.created_at AS date)
    ORDER BY cart_date ASC NULLS LAST
    1 ms10 ms100 ms1 sHyper6.9 msDuckDB7.8 msGizmoSQL11.7 msclickhouse_brew267 msFirebolt44.8 msDataFusion77.5 msClickHouse27.9 ms
  8. 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

    Ines's whole session, 7 turns
    • The request, as Ines received it
      Report request from Emeka Obi (analyst for the VP Product):
      
      Title: Daily cart conversion at Cragsley Outfitters, Nov 17 – Dec 1 2025
      Question: Is there a visible conversion dip in the first few days after cart-reminder emails broke (Nov 24), before Black Friday/Cyber Monday demand likely swamps the signal?
      Grain: one row per calendar day of cart creation, Cragsley Outfitters only
      Columns:
      - date
      - carts created
      - overall conversion pct
      - pct converted within 1 hour
      Details: Tenant: Cragsley Outfitters. Population: carts with created_at on each day from 2025-11-17 through 2025-12-01 inclusive (15 days), bracketing one week before the outage (Nov 17-23) against the outage week and the days just after (Nov 24-Dec 1, including Black Friday Nov 28 and Cyber Monday Dec 1). Use the same overall_conversion_pct and pct_converted_within_1_hour definitions as the prior weekly report. Order rows by date ascending.
      
      Check your query with the warehouse's dry_run tool, then hand it in with submit.
    • Ines calls dry_run
      SELECT
        c.created_at::date AS cart_date,
        COUNT(*) AS carts_created,
        ROUND(100.0 * SUM(CASE WHEN c.status = 'converted' THEN 1 ELSE 0 END) / COUNT(*), 2) AS overall_conversion_pct,
        ROUND(100.0 * SUM(CASE WHEN c.status = 'converted' AND c.converted_at IS NOT NULL AND c.converted_at <= c.created_at + INTERVAL '1 hour' THEN 1 ELSE 0 END) / COUNT(*), 2) AS pct_converted_within_1_hour
      FROM carts c
      JOIN tenants t ON t.id = c.tenant_id
      WHERE t.name = 'Cragsley Outfitters'
        AND c.created_at >= DATE '2025-11-17'
        AND c.created_at < DATE '2025-12-02'
      GROUP BY c.created_at::date
      ORDER BY cart_date ASC
    • The warehouse answers
      STATUS: COMPILES (dry run on the empty schema, no data read)
      COLUMNS: cart_date, carts_created, overall_conversion_pct, pct_converted_within_1_hour
    • InesQuery compiles and returns the expected columns.
    • Ines calls submit
      SELECT
        c.created_at::date AS cart_date,
        COUNT(*) AS carts_created,
        ROUND(100.0 * SUM(CASE WHEN c.status = 'converted' THEN 1 ELSE 0 END) / COUNT(*), 2) AS overall_conversion_pct,
        ROUND(100.0 * SUM(CASE WHEN c.status = 'converted' AND c.converted_at IS NOT NULL AND c.converted_at <= c.created_at + INTERVAL '1 hour' THEN 1 ELSE 0 END) / COUNT(*), 2) AS pct_converted_within_1_hour
      FROM carts c
      JOIN tenants t ON t.id = c.tenant_id
      WHERE t.name = 'Cragsley Outfitters'
        AND c.created_at >= DATE '2025-11-17'
        AND c.created_at < DATE '2025-12-02'
      GROUP BY c.created_at::date
      ORDER BY cart_date ASC
    • The warehouse answers
      Submitted to tonight's batch. Your work on this request is done.
    • InesSubmitted. The report computes, per calendar day of cart creation for Cragsley Outfitters (Nov 17–Dec 1, 2025): carts created, overall conversion % (status = 'converted'), and % converted within 1 hour of creation — ordered by date ascending, 15 rows total.