DB Olympics

Queries · q351 · Head of Merchandising · week 2, night 2

Image-Count Buckets: Product Age, Status, and Vertical Mix

“Are zero-image product listings systematically different from one-plus-image listings (older, more likely draft/archived, or concentrated in certain verticals), rather than the image count itself being the driver?”

Manager · asked the questionFreya LindgrenHead of Merchandising. Came from a fashion marketplace's catalog team. Responsible for helping merchants present their products well.
Analyst · planned the reportZara AhmedExploratory: looks at the raw detail of an unfamiliar area before aggregating it.
Engineer · wrote the SQLBeaWrites SQL the way the company's BI tool generates it: verbose fully qualified aliases (t0, t1, ...), a subquery per measure joined back on the grain keys, COALESCE everywhere.

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

  1. Freya Lindgren writes to Zara Ahmed

    The manager, at the start of week 2

    Subject: Good start — let's dig into the risk areas

    Hi Zara,

    This is really useful, and a few things jumped out that I want to understand better before Friday.

    The Cragsley Outfitters finding worries me most — our #2 merchant by revenue with real quality concentration, and some of those poorly-rated items are still selling briskly. That combination (strong sales, weak ratings, sizable product count) feels like the kind of thing that erodes trust quietly until it doesn't. I'd like to understand it better: is this concentrated in certain kinds of products, has it been building over time, and does it look like it's about to hit sales or is demand just lagging behind reputation?

    Second, the December rating dip needs a clearer story. You flagged it as needing a second month to confirm — I agree, but I'd also like to understand whether there's something explainable behind it (seasonal returns, a particular product push, shipping delays around the holidays) rather than just watching and waiting. If Fennel & Flint and Wiesgrund Wohnen are worth a check-in, I want to go into that conversation knowing roughly what happened.

    Third, I was surprised that going from zero to one image makes such a big difference but more images after that don't. Before I turn that into a merchant talking point, I want to know if we're actually looking at a real presentation effect or just a stand-in for something else — are the zero-image listings systematically different in some way, like older, or a different kind of product?

    Finally, on bundles: you said it's white space but we can't test performance yet because usage is too rare. I'd like to understand it a bit more anecdotally before we design a pilot — among the handful of merchants that do use bundles, is there anything that distinguishes them, even descriptively, from the ones that don't?

    Thanks again — this is exactly the kind of clarity I needed.

    Freya

  2. Zara Ahmed plans the night

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

    Night 1 findings on Cragsley: the poor ratings concentrate in soft-goods categories (Loungewear 66.7%/40.0% poor, Outerwear 50.0%/28.6%, Shirts 50.0%, Tops/Trousers 37-40%) while Boots, Knitwear, Scarves & Hats, Sandals are clean (0-17%) — looks like a fit/fabric-quality issue in specific garment types, not catalog-wide. Note: category names repeat (Tops, Loungewear, Outerwear, Trousers, Knitwear each appear twice) — likely duplicate category records, a data-quality wrinkle worth flagging separately but not urgent. The rating trend shows this is NOT new: pct_1_2_star spikes every December (25.0% in 2023, 22.1% in 2024, 25.7% in 2025) alongside every November/December order surge — a recurring seasonal pattern, not a 2025-specific crisis, which tempers how I should frame the Dec 2025 dip for Cragsley specifically. The sales-trajectory report shows the poor-tier's absolute revenue did grow (896→6,177 EUR/month) but its share of Cragsley's total shrank from ~23% to ~6% as the rest of the catalog scaled much faster — so it's 'stagnating while the business grows around it,' not yet a demand collapse. That closes out the Cragsley thread well enough for the memo. Tonight I'm moving to the other three asks: (1) test the shipping/carrier hypothesis for the Dec rating dip by looking at delivery performance alongside review sentiment for Fennel & Flint and Wiesgrund Wohnen specifically, since COO's team flagged an Albion Parcel carrier breakdown in exactly this window; (2) test whether zero-image products are systematically older/draft/different-vertical rather than just under-photographed, by looking at age, status and vertical mix by image bucket without restricting to active products only (the earlier report did, which could have hidden this); (3) get a raw, merchant-level profile of the handful of merchants that actually use bundles, to look for descriptive patterns before even thinking about a pilot design.

    Image-Count Buckets: Product Age, Status, and Vertical Mix
    Question
    Are zero-image product listings systematically different from one-plus-image listings (older, more likely draft/archived, or concentrated in certain verticals), rather than the image count itself being the driver?
    One row per
    product image-count bucket (0, 1, 2-3, 4-6, 7+), across ALL products regardless of status (not just active ones, to test the status hypothesis)
    Columns
    • image_count_bucket
    • product_count
    • avg_days_since_created
    • pct_status_draft
    • pct_status_archived
    • pct_status_active
    • pct_apparel_footwear_industry
    • pct_b2b_industry (Office Supplies + Industrial Supplies combined)
    • avg_variants_per_product
  3. Bea 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 product_base AS (
      SELECT
        t0.id AS product_id,
        t0.tenant_id AS tenant_id,
        t0.status AS status,
        t0.created_at AS created_at,
        CASE
          WHEN COALESCE(t1.image_count, 0) = 0 THEN '0'
          WHEN COALESCE(t1.image_count, 0) = 1 THEN '1'
          WHEN COALESCE(t1.image_count, 0) BETWEEN 2 AND 3 THEN '2-3'
          WHEN COALESCE(t1.image_count, 0) BETWEEN 4 AND 6 THEN '4-6'
          ELSE '7+'
        END AS image_count_bucket
      FROM products t0
      LEFT JOIN (
        SELECT
          t1a.product_id AS product_id,
          COUNT(*) AS image_count
        FROM product_media t1a
        WHERE t1a.kind = 'image'
        GROUP BY t1a.product_id
      ) t1 ON t1.product_id = t0.id
    ),
    dim_bucket AS (
      SELECT DISTINCT t2.image_count_bucket AS image_count_bucket
      FROM product_base t2
    ),
    m_count AS (
      SELECT
        t3.image_count_bucket AS image_count_bucket,
        COUNT(*) AS product_count
      FROM product_base t3
      GROUP BY t3.image_count_bucket
    ),
    m_age AS (
      SELECT
        t4.image_count_bucket AS image_count_bucket,
        AVG(CAST(DATE '2025-12-31' - CAST(t4.created_at AS date) AS numeric)) AS avg_days_since_created
      FROM product_base t4
      GROUP BY t4.image_count_bucket
    ),
    m_status AS (
      SELECT
        t5.image_count_bucket AS image_count_bucket,
        100.0 * SUM(CASE WHEN t5.status = 'draft' THEN 1 ELSE 0 END) / COUNT(*) AS pct_status_draft,
        100.0 * SUM(CASE WHEN t5.status = 'archived' THEN 1 ELSE 0 END) / COUNT(*) AS pct_status_archived,
        100.0 * SUM(CASE WHEN t5.status = 'active' THEN 1 ELSE 0 END) / COUNT(*) AS pct_status_active
      FROM product_base t5
      GROUP BY t5.image_count_bucket
    ),
    m_industry AS (
      SELECT
        t6.image_count_bucket AS image_count_bucket,
        100.0 * SUM(CASE WHEN LOWER(t7.industry) LIKE '%apparel%' OR LOWER(t7.industry) LIKE '%footwear%' THEN 1 ELSE 0 END) / COUNT(*) AS pct_apparel_footwear_industry,
        100.0 * SUM(CASE WHEN LOWER(t7.industry) LIKE '%office%' OR LOWER(t7.industry) LIKE '%industrial%' THEN 1 ELSE 0 END) / COUNT(*) AS pct_b2b_industry
      FROM product_base t6
      JOIN tenants t7 ON t7.id = t6.tenant_id
      GROUP BY t6.image_count_bucket
    ),
    m_variants AS (
      SELECT
        t8.image_count_bucket AS image_count_bucket,
        AVG(COALESCE(t9.variant_count, 0)) AS avg_variants_per_product
      FROM product_base t8
      LEFT JOIN (
        SELECT
          t9a.product_id AS product_id,
          COUNT(*) AS variant_count
        FROM product_variants t9a
        GROUP BY t9a.product_id
      ) t9 ON t9.product_id = t8.product_id
      GROUP BY t8.image_count_bucket
    )
    SELECT
      t10.image_count_bucket AS image_count_bucket,
      COALESCE(t11.product_count, 0) AS product_count,
      ROUND(COALESCE(t12.avg_days_since_created, 0), 1) AS avg_days_since_created,
      ROUND(COALESCE(t13.pct_status_draft, 0), 1) AS pct_status_draft,
      ROUND(COALESCE(t13.pct_status_archived, 0), 1) AS pct_status_archived,
      ROUND(COALESCE(t13.pct_status_active, 0), 1) AS pct_status_active,
      ROUND(COALESCE(t14.pct_apparel_footwear_industry, 0), 1) AS pct_apparel_footwear_industry,
      ROUND(COALESCE(t14.pct_b2b_industry, 0), 1) AS pct_b2b_industry,
      ROUND(COALESCE(t15.avg_variants_per_product, 0), 1) AS avg_variants_per_product
    FROM dim_bucket t10
    LEFT JOIN m_count t11 ON t11.image_count_bucket = t10.image_count_bucket
    LEFT JOIN m_age t12 ON t12.image_count_bucket = t10.image_count_bucket
    LEFT JOIN m_status t13 ON t13.image_count_bucket = t10.image_count_bucket
    LEFT JOIN m_industry t14 ON t14.image_count_bucket = t10.image_count_bucket
    LEFT JOIN m_variants t15 ON t15.image_count_bucket = t10.image_count_bucket
    ORDER BY
      CASE t10.image_count_bucket
        WHEN '0' THEN 1
        WHEN '1' THEN 2
        WHEN '2-3' THEN 3
        WHEN '4-6' THEN 4
        WHEN '7+' THEN 5
      END

    “Query compiles cleanly with the expected columns. Submitting now.”

    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 five image-count buckets (0, 1, 2-3, 4-6, 7+) across all products regardless of status/deletion, with product age, status mix, industry mix, and average variants per product computed per bucket. One caveat noted to the analyst: since tenants.industry is free-text rather than a coded field, the apparel/footwear and B2B industry shares are derived via keyword matching, which should be verified against actual values if results look off.”

  4. The report comes back overnight

    As Zara Ahmed saw it, from the small dataset the agents worked on

    image_count_bucket | product_count | avg_days_since_created | pct_status_draft | pct_status_archived | pct_status_active | pct_apparel_footwear_industry | pct_b2b_industry | avg_variants_per_product
    -------------------+---------------+------------------------+------------------+---------------------+-------------------+-------------------------------+------------------+-------------------------
    0                  |            85 |                 1136.1 |              0.0 |                 0.0 |             100.0 |                          34.1 |              0.0 |                      3.4
    1                  |          1951 |                 1039.9 |              3.0 |                56.0 |              41.0 |                          26.7 |              7.8 |                      5.5
    2-3                |         10226 |                 1048.1 |              2.8 |                58.2 |              39.0 |                          26.5 |              7.7 |                      5.4
    4-6                |          8118 |                 1042.6 |              3.0 |                57.8 |              39.2 |                          26.9 |              7.7 |                      5.5
    7+                 |           669 |                 1032.0 |              3.3 |                58.0 |              38.7 |                          27.5 |              8.5 |                      5.5
    (5 rows)
  5. What came of it

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

    It builds on:

  6. It becomes benchmark query q351

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

    • numericsdocs/NUMERICS.md: bare NUMERIC cast that only served the rounding removed; AVG over an integer computed as a decimal: AVG(CAST(x AS NUMERIC(18,6)))
    • numericsdocs/NUMERICS.md: AVG over an integer computed as a decimal: AVG(CAST(x AS NUMERIC(18,6)))
    • 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
    WITH product_base AS (  SELECT    t0.id AS product_id,    t0.tenant_id AS tenant_id,    t0.status AS status,    t0.created_at AS created_at,    CASE      WHEN COALESCE(t1.image_count, 0) = 0 THEN '0'      WHEN COALESCE(t1.image_count, 0) = 1 THEN '1'      WHEN COALESCE(t1.image_count, 0) BETWEEN 2 AND 3 THEN '2-3'      WHEN COALESCE(t1.image_count, 0) BETWEEN 4 AND 6 THEN '4-6'      ELSE '7+'    END AS image_count_bucket  FROM products t0  LEFT JOIN (    SELECT      t1a.product_id AS product_id,      COUNT(*) AS image_count    FROM product_media t1a    WHERE t1a.kind = 'image'    GROUP BY t1a.product_id  ) t1 ON t1.product_id = t0.id),dim_bucket AS (  SELECT DISTINCT t2.image_count_bucket AS image_count_bucket  FROM product_base t2),m_count AS (  SELECT    t3.image_count_bucket AS image_count_bucket,    COUNT(*) AS product_count  FROM product_base t3  GROUP BY t3.image_count_bucket),m_age AS (  SELECT    t4.image_count_bucket AS image_count_bucket,    AVG(CAST(DATE '2025-12-31' - CAST(t4.created_at AS date) AS numeric)) AS avg_days_since_created    AVG(CAST(DATE '2025-12-31' - CAST(t4.created_at AS date) AS numeric(18,6))) AS avg_days_since_created  FROM product_base t4  GROUP BY t4.image_count_bucket),m_status AS (  SELECT    t5.image_count_bucket AS image_count_bucket,    100.0 * SUM(CASE WHEN t5.status = 'draft' THEN 1 ELSE 0 END) / COUNT(*) AS pct_status_draft,    100.0 * SUM(CASE WHEN t5.status = 'archived' THEN 1 ELSE 0 END) / COUNT(*) AS pct_status_archived,    100.0 * SUM(CASE WHEN t5.status = 'active' THEN 1 ELSE 0 END) / COUNT(*) AS pct_status_active  FROM product_base t5  GROUP BY t5.image_count_bucket),m_industry AS (  SELECT    t6.image_count_bucket AS image_count_bucket,    100.0 * SUM(CASE WHEN LOWER(t7.industry) LIKE '%apparel%' OR LOWER(t7.industry) LIKE '%footwear%' THEN 1 ELSE 0 END) / COUNT(*) AS pct_apparel_footwear_industry,    100.0 * SUM(CASE WHEN LOWER(t7.industry) LIKE '%office%' OR LOWER(t7.industry) LIKE '%industrial%' THEN 1 ELSE 0 END) / COUNT(*) AS pct_b2b_industry  FROM product_base t6  JOIN tenants t7 ON t7.id = t6.tenant_id  GROUP BY t6.image_count_bucket),m_variants AS (  SELECT    t8.image_count_bucket AS image_count_bucket,    AVG(COALESCE(t9.variant_count, 0)) AS avg_variants_per_product    AVG(CAST(COALESCE(t9.variant_count, 0) AS NUMERIC(18,6))) AS avg_variants_per_product  FROM product_base t8  LEFT JOIN (    SELECT      t9a.product_id AS product_id,      COUNT(*) AS variant_count    FROM product_variants t9a    GROUP BY t9a.product_id  ) t9 ON t9.product_id = t8.product_id  GROUP BY t8.image_count_bucket)SELECT  t10.image_count_bucket AS image_count_bucket,  COALESCE(t11.product_count, 0) AS product_count,  ROUND(COALESCE(t12.avg_days_since_created, 0), 1) AS avg_days_since_created,  ROUND(COALESCE(t13.pct_status_draft, 0), 1) AS pct_status_draft,  ROUND(COALESCE(t13.pct_status_archived, 0), 1) AS pct_status_archived,  ROUND(COALESCE(t13.pct_status_active, 0), 1) AS pct_status_active,  ROUND(COALESCE(t14.pct_apparel_footwear_industry, 0), 1) AS pct_apparel_footwear_industry,  ROUND(COALESCE(t14.pct_b2b_industry, 0), 1) AS pct_b2b_industry,  ROUND(COALESCE(t15.avg_variants_per_product, 0), 1) AS avg_variants_per_product  COALESCE(t12.avg_days_since_created, 0) AS avg_days_since_created,  COALESCE(t13.pct_status_draft, 0) AS pct_status_draft,  COALESCE(t13.pct_status_archived, 0) AS pct_status_archived,  COALESCE(t13.pct_status_active, 0) AS pct_status_active,  COALESCE(t14.pct_apparel_footwear_industry, 0) AS pct_apparel_footwear_industry,  COALESCE(t14.pct_b2b_industry, 0) AS pct_b2b_industry,  COALESCE(t15.avg_variants_per_product, 0) AS avg_variants_per_productFROM dim_bucket t10LEFT JOIN m_count t11 ON t11.image_count_bucket = t10.image_count_bucketLEFT JOIN m_age t12 ON t12.image_count_bucket = t10.image_count_bucketLEFT JOIN m_status t13 ON t13.image_count_bucket = t10.image_count_bucketLEFT JOIN m_industry t14 ON t14.image_count_bucket = t10.image_count_bucketLEFT JOIN m_variants t15 ON t15.image_count_bucket = t10.image_count_bucketORDER BY  CASE t10.image_count_bucket    WHEN '0' THEN 1    WHEN '1' THEN 2    WHEN '2-3' THEN 3    WHEN '4-6' THEN 4    WHEN '7+' THEN 5  END  END NULLS LAST
    WITH product_base AS (
      SELECT
        t0.id AS product_id,
        t0.tenant_id AS tenant_id,
        t0.status AS status,
        t0.created_at AS created_at,
        CASE
          WHEN COALESCE(t1.image_count, 0) = 0 THEN '0'
          WHEN COALESCE(t1.image_count, 0) = 1 THEN '1'
          WHEN COALESCE(t1.image_count, 0) BETWEEN 2 AND 3 THEN '2-3'
          WHEN COALESCE(t1.image_count, 0) BETWEEN 4 AND 6 THEN '4-6'
          ELSE '7+'
        END AS image_count_bucket
      FROM products t0
      LEFT JOIN (
        SELECT
          t1a.product_id AS product_id,
          COUNT(*) AS image_count
        FROM product_media t1a
        WHERE t1a.kind = 'image'
        GROUP BY t1a.product_id
      ) t1 ON t1.product_id = t0.id
    ),
    dim_bucket AS (
      SELECT DISTINCT t2.image_count_bucket AS image_count_bucket
      FROM product_base t2
    ),
    m_count AS (
      SELECT
        t3.image_count_bucket AS image_count_bucket,
        COUNT(*) AS product_count
      FROM product_base t3
      GROUP BY t3.image_count_bucket
    ),
    m_age AS (
      SELECT
        t4.image_count_bucket AS image_count_bucket,
        AVG(CAST(DATE '2025-12-31' - CAST(t4.created_at AS date) AS numeric(18,6))) AS avg_days_since_created
      FROM product_base t4
      GROUP BY t4.image_count_bucket
    ),
    m_status AS (
      SELECT
        t5.image_count_bucket AS image_count_bucket,
        100.0 * SUM(CASE WHEN t5.status = 'draft' THEN 1 ELSE 0 END) / COUNT(*) AS pct_status_draft,
        100.0 * SUM(CASE WHEN t5.status = 'archived' THEN 1 ELSE 0 END) / COUNT(*) AS pct_status_archived,
        100.0 * SUM(CASE WHEN t5.status = 'active' THEN 1 ELSE 0 END) / COUNT(*) AS pct_status_active
      FROM product_base t5
      GROUP BY t5.image_count_bucket
    ),
    m_industry AS (
      SELECT
        t6.image_count_bucket AS image_count_bucket,
        100.0 * SUM(CASE WHEN LOWER(t7.industry) LIKE '%apparel%' OR LOWER(t7.industry) LIKE '%footwear%' THEN 1 ELSE 0 END) / COUNT(*) AS pct_apparel_footwear_industry,
        100.0 * SUM(CASE WHEN LOWER(t7.industry) LIKE '%office%' OR LOWER(t7.industry) LIKE '%industrial%' THEN 1 ELSE 0 END) / COUNT(*) AS pct_b2b_industry
      FROM product_base t6
      JOIN tenants t7 ON t7.id = t6.tenant_id
      GROUP BY t6.image_count_bucket
    ),
    m_variants AS (
      SELECT
        t8.image_count_bucket AS image_count_bucket,
        AVG(CAST(COALESCE(t9.variant_count, 0) AS NUMERIC(18,6))) AS avg_variants_per_product
      FROM product_base t8
      LEFT JOIN (
        SELECT
          t9a.product_id AS product_id,
          COUNT(*) AS variant_count
        FROM product_variants t9a
        GROUP BY t9a.product_id
      ) t9 ON t9.product_id = t8.product_id
      GROUP BY t8.image_count_bucket
    )
    SELECT
      t10.image_count_bucket AS image_count_bucket,
      COALESCE(t11.product_count, 0) AS product_count,
      COALESCE(t12.avg_days_since_created, 0) AS avg_days_since_created,
      COALESCE(t13.pct_status_draft, 0) AS pct_status_draft,
      COALESCE(t13.pct_status_archived, 0) AS pct_status_archived,
      COALESCE(t13.pct_status_active, 0) AS pct_status_active,
      COALESCE(t14.pct_apparel_footwear_industry, 0) AS pct_apparel_footwear_industry,
      COALESCE(t14.pct_b2b_industry, 0) AS pct_b2b_industry,
      COALESCE(t15.avg_variants_per_product, 0) AS avg_variants_per_product
    FROM dim_bucket t10
    LEFT JOIN m_count t11 ON t11.image_count_bucket = t10.image_count_bucket
    LEFT JOIN m_age t12 ON t12.image_count_bucket = t10.image_count_bucket
    LEFT JOIN m_status t13 ON t13.image_count_bucket = t10.image_count_bucket
    LEFT JOIN m_industry t14 ON t14.image_count_bucket = t10.image_count_bucket
    LEFT JOIN m_variants t15 ON t15.image_count_bucket = t10.image_count_bucket
    ORDER BY
      CASE t10.image_count_bucket
        WHEN '0' THEN 1
        WHEN '1' THEN 2
        WHEN '2-3' THEN 3
        WHEN '4-6' THEN 4
        WHEN '7+' THEN 5
      END NULLS LAST
    10 ms100 ms1 sHyper34.1 msDuckDB50.4 msGizmoSQL56.4 msclickhouse_brew201 msFirebolt121 msDataFusion391 msClickHouse255 ms
  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

    Bea's whole session, 7 turns
    • The request, as Bea received it
      Report request from Zara Ahmed (analyst for the Head of Merchandising):
      
      Title: Image-Count Buckets: Product Age, Status, and Vertical Mix
      Question: Are zero-image product listings systematically different from one-plus-image listings (older, more likely draft/archived, or concentrated in certain verticals), rather than the image count itself being the driver?
      Grain: one row per product image-count bucket (0, 1, 2-3, 4-6, 7+), across ALL products regardless of status (not just active ones, to test the status hypothesis)
      Columns:
      - image_count_bucket
      - product_count
      - avg_days_since_created
      - pct_status_draft
      - pct_status_archived
      - pct_status_active
      - pct_apparel_footwear_industry
      - pct_b2b_industry (Office Supplies + Industrial Supplies combined)
      - avg_variants_per_product
      Details: Tables: products (all statuses, all deleted_at values included this time), product_media (kind='image', counted per product to form the bucket), tenants (for industry), product_variants. avg_days_since_created = average of (2025-12-31 minus products.created_at) in days. pct_status_draft/archived/active = share of products in that bucket with that products.status value. pct_apparel_footwear_industry and pct_b2b_industry = share of products in the bucket belonging to tenants with that industry. avg_variants_per_product = average count of product_variants (any status) per product in the bucket. Round averages to 1 decimal, percentages to 1 decimal. Order by image_count_bucket in natural ascending order (0, 1, 2-3, 4-6, 7+).
      
      Check your query with the warehouse's dry_run tool, then hand it in with submit.
    • Bea calls dry_run
      WITH product_base AS (
        SELECT
          t0.id AS product_id,
          t0.tenant_id AS tenant_id,
          t0.status AS status,
          t0.created_at AS created_at,
          CASE
            WHEN COALESCE(t1.image_count, 0) = 0 THEN '0'
            WHEN COALESCE(t1.image_count, 0) = 1 THEN '1'
            WHEN COALESCE(t1.image_count, 0) BETWEEN 2 AND 3 THEN '2-3'
            WHEN COALESCE(t1.image_count, 0) BETWEEN 4 AND 6 THEN '4-6'
            ELSE '7+'
          END AS image_count_bucket
        FROM products t0
        LEFT JOIN (
          SELECT
            t1a.product_id AS product_id,
            COUNT(*) AS image_count
          FROM product_media t1a
          WHERE t1a.kind = 'image'
          GROUP BY t1a.product_id
        ) t1 ON t1.product_id = t0.id
      ),
      dim_bucket AS (
        SELECT DISTINCT t2.image_count_bucket AS image_count_bucket
        FROM product_base t2
      ),
      m_count AS (
        SELECT
          t3.image_count_bucket AS image_count_bucket,
          COUNT(*) AS product_count
        FROM product_base t3
        GROUP BY t3.image_count_bucket
      ),
      m_age AS (
        SELECT
          t4.image_count_bucket AS image_count_bucket,
          AVG(CAST(DATE '2025-12-31' - CAST(t4.created_at AS date) AS numeric)) AS avg_days_since_created
        FROM product_base t4
        GROUP BY t4.image_count_bucket
      ),
      m_status AS (
        SELECT
          t5.image_count_bucket AS image_count_bucket,
          100.0 * SUM(CASE WHEN t5.status = 'draft' THEN 1 ELSE 0 END) / COUNT(*) AS pct_status_draft,
          100.0 * SUM(CASE WHEN t5.status = 'archived' THEN 1 ELSE 0 END) / COUNT(*) AS pct_status_archived,
          100.0 * SUM(CASE WHEN t5.status = 'active' THEN 1 ELSE 0 END) / COUNT(*) AS pct_status_active
        FROM product_base t5
        GROUP BY t5.image_count_bucket
      ),
      m_industry AS (
        SELECT
          t6.image_count_bucket AS image_count_bucket,
          100.0 * SUM(CASE WHEN LOWER(t7.industry) LIKE '%apparel%' OR LOWER(t7.industry) LIKE '%footwear%' THEN 1 ELSE 0 END) / COUNT(*) AS pct_apparel_footwear_industry,
          100.0 * SUM(CASE WHEN LOWER(t7.industry) LIKE '%office%' OR LOWER(t7.industry) LIKE '%industrial%' THEN 1 ELSE 0 END) / COUNT(*) AS pct_b2b_industry
        FROM product_base t6
        JOIN tenants t7 ON t7.id = t6.tenant_id
        GROUP BY t6.image_count_bucket
      ),
      m_variants AS (
        SELECT
          t8.image_count_bucket AS image_count_bucket,
          AVG(COALESCE(t9.variant_count, 0)) AS avg_variants_per_product
        FROM product_base t8
        LEFT JOIN (
          SELECT
            t9a.product_id AS product_id,
            COUNT(*) AS variant_count
          FROM product_variants t9a
          GROUP BY t9a.product_id
        ) t9 ON t9.product_id = t8.product_id
        GROUP BY t8.image_count_bucket
      )
      SELECT
        t10.image_count_bucket AS image_count_bucket,
        COALESCE(t11.product_count, 0) AS product_count,
        ROUND(COALESCE(t12.avg_days_since_created, 0), 1) AS avg_days_since_created,
        ROUND(COALESCE(t13.pct_status_draft, 0), 1) AS pct_status_draft,
        ROUND(COALESCE(t13.pct_status_archived, 0), 1) AS pct_status_archived,
        ROUND(COALESCE(t13.pct_status_active, 0), 1) AS pct_status_active,
        ROUND(COALESCE(t14.pct_apparel_footwear_industry, 0), 1) AS pct_apparel_footwear_industry,
        ROUND(COALESCE(t14.pct_b2b_industry, 0), 1) AS pct_b2b_industry,
        ROUND(COALESCE(t15.avg_variants_per_product, 0), 1) AS avg_variants_per_product
      FROM dim_bucket t10
      LEFT JOIN m_count t11 ON t11.image_count_bucket = t10.image_count_bucket
      LEFT JOIN m_age t12 ON t12.image_count_bucket = t10.image_count_bucket
      LEFT JOIN m_status t13 ON t13.image_count_bucket = t10.image_count_bucket
      LEFT JOIN m_industry t14 ON t14.image_count_bucket = t10.image_count_bucket
      LEFT JOIN m_variants t15 ON t15.image_count_bucket = t10.image_count_bucket
      ORDER BY
        CASE t10.image_count_bucket
          WHEN '0' THEN 1
          WHEN '1' THEN 2
          WHEN '2-3' THEN 3
          WHEN '4-6' THEN 4
          WHEN '7+' THEN 5
        END
    • The warehouse answers
      STATUS: COMPILES (dry run on the empty schema, no data read)
      COLUMNS: image_count_bucket, product_count, avg_days_since_created, pct_status_draft, pct_status_archived, pct_status_active, pct_apparel_footwear_industry, pct_b2b_industry, avg_variants_per_product
    • BeaQuery compiles cleanly with the expected columns. Submitting now.
    • Bea calls submit
      WITH product_base AS (
        SELECT
          t0.id AS product_id,
          t0.tenant_id AS tenant_id,
          t0.status AS status,
          t0.created_at AS created_at,
          CASE
            WHEN COALESCE(t1.image_count, 0) = 0 THEN '0'
            WHEN COALESCE(t1.image_count, 0) = 1 THEN '1'
            WHEN COALESCE(t1.image_count, 0) BETWEEN 2 AND 3 THEN '2-3'
            WHEN COALESCE(t1.image_count, 0) BETWEEN 4 AND 6 THEN '4-6'
            ELSE '7+'
          END AS image_count_bucket
        FROM products t0
        LEFT JOIN (
          SELECT
            t1a.product_id AS product_id,
            COUNT(*) AS image_count
          FROM product_media t1a
          WHERE t1a.kind = 'image'
          GROUP BY t1a.product_id
        ) t1 ON t1.product_id = t0.id
      ),
      dim_bucket AS (
        SELECT DISTINCT t2.image_count_bucket AS image_count_bucket
        FROM product_base t2
      ),
      m_count AS (
        SELECT
          t3.image_count_bucket AS image_count_bucket,
          COUNT(*) AS product_count
        FROM product_base t3
        GROUP BY t3.image_count_bucket
      ),
      m_age AS (
        SELECT
          t4.image_count_bucket AS image_count_bucket,
          AVG(CAST(DATE '2025-12-31' - CAST(t4.created_at AS date) AS numeric)) AS avg_days_since_created
        FROM product_base t4
        GROUP BY t4.image_count_bucket
      ),
      m_status AS (
        SELECT
          t5.image_count_bucket AS image_count_bucket,
          100.0 * SUM(CASE WHEN t5.status = 'draft' THEN 1 ELSE 0 END) / COUNT(*) AS pct_status_draft,
          100.0 * SUM(CASE WHEN t5.status = 'archived' THEN 1 ELSE 0 END) / COUNT(*) AS pct_status_archived,
          100.0 * SUM(CASE WHEN t5.status = 'active' THEN 1 ELSE 0 END) / COUNT(*) AS pct_status_active
        FROM product_base t5
        GROUP BY t5.image_count_bucket
      ),
      m_industry AS (
        SELECT
          t6.image_count_bucket AS image_count_bucket,
          100.0 * SUM(CASE WHEN LOWER(t7.industry) LIKE '%apparel%' OR LOWER(t7.industry) LIKE '%footwear%' THEN 1 ELSE 0 END) / COUNT(*) AS pct_apparel_footwear_industry,
          100.0 * SUM(CASE WHEN LOWER(t7.industry) LIKE '%office%' OR LOWER(t7.industry) LIKE '%industrial%' THEN 1 ELSE 0 END) / COUNT(*) AS pct_b2b_industry
        FROM product_base t6
        JOIN tenants t7 ON t7.id = t6.tenant_id
        GROUP BY t6.image_count_bucket
      ),
      m_variants AS (
        SELECT
          t8.image_count_bucket AS image_count_bucket,
          AVG(COALESCE(t9.variant_count, 0)) AS avg_variants_per_product
        FROM product_base t8
        LEFT JOIN (
          SELECT
            t9a.product_id AS product_id,
            COUNT(*) AS variant_count
          FROM product_variants t9a
          GROUP BY t9a.product_id
        ) t9 ON t9.product_id = t8.product_id
        GROUP BY t8.image_count_bucket
      )
      SELECT
        t10.image_count_bucket AS image_count_bucket,
        COALESCE(t11.product_count, 0) AS product_count,
        ROUND(COALESCE(t12.avg_days_since_created, 0), 1) AS avg_days_since_created,
        ROUND(COALESCE(t13.pct_status_draft, 0), 1) AS pct_status_draft,
        ROUND(COALESCE(t13.pct_status_archived, 0), 1) AS pct_status_archived,
        ROUND(COALESCE(t13.pct_status_active, 0), 1) AS pct_status_active,
        ROUND(COALESCE(t14.pct_apparel_footwear_industry, 0), 1) AS pct_apparel_footwear_industry,
        ROUND(COALESCE(t14.pct_b2b_industry, 0), 1) AS pct_b2b_industry,
        ROUND(COALESCE(t15.avg_variants_per_product, 0), 1) AS avg_variants_per_product
      FROM dim_bucket t10
      LEFT JOIN m_count t11 ON t11.image_count_bucket = t10.image_count_bucket
      LEFT JOIN m_age t12 ON t12.image_count_bucket = t10.image_count_bucket
      LEFT JOIN m_status t13 ON t13.image_count_bucket = t10.image_count_bucket
      LEFT JOIN m_industry t14 ON t14.image_count_bucket = t10.image_count_bucket
      LEFT JOIN m_variants t15 ON t15.image_count_bucket = t10.image_count_bucket
      ORDER BY
        CASE t10.image_count_bucket
          WHEN '0' THEN 1
          WHEN '1' THEN 2
          WHEN '2-3' THEN 3
          WHEN '4-6' THEN 4
          WHEN '7+' THEN 5
        END
    • The warehouse answers
      Submitted to tonight's batch. Your work on this request is done.
    • BeaSubmitted. The report builds five image-count buckets (0, 1, 2-3, 4-6, 7+) across all products regardless of status/deletion, with product age, status mix, industry mix, and average variants per product computed per bucket. One caveat noted to the analyst: since `tenants.industry` is free-text rather than a coded field, the apparel/footwear and B2B industry shares are derived via keyword matching, which should be verified against actual values if results look off.