{"id":"code-dev-sql-returns-group-by-having","audience":"developers","category":"coding","track":"coding","technique":"context","title":"Count returns per category without double-counted rows","free":false,"task":"Ops wants product categories with more than 3 returned items in Q3 2026 (July 1 through September 30). You know the returns logger sometimes writes the same order_item_id twice, so count distinct items, and products with no category must show as 'Uncategorized'. Columns exactly category and returned_items, highest first. Write one SQLite query in exactly 1 code block of at most 14 lines, with under 30 words of text.","materialLabel":"The schema and sample rows (attached to your prompt automatically)","material":"-- SQLite 3.45, file: warehouse.db  (Northwind Gear internal tools)\n\nCREATE TABLE products (\n  id        INTEGER PRIMARY KEY,\n  name      TEXT NOT NULL,\n  category  TEXT              -- NULL for older catalog items\n);\n\nCREATE TABLE order_items (\n  id          INTEGER PRIMARY KEY,\n  order_id    INTEGER NOT NULL,\n  product_id  INTEGER NOT NULL REFERENCES products(id),\n  qty         INTEGER NOT NULL\n);\n\nCREATE TABLE returns (\n  id             INTEGER PRIMARY KEY,\n  order_item_id  INTEGER NOT NULL REFERENCES order_items(id),\n  returned_at    TEXT NOT NULL,   -- ISO 8601, e.g. '2026-08-14 16:02:11'\n  reason         TEXT\n);\n\n-- sample: returns\n-- 1 | 501 | 2026-07-02 10:11:00 | too small\n-- 2 | 501 | 2026-07-02 10:11:00 | too small\n-- 3 | 502 | 2026-09-30 22:40:00 | damaged\n-- 4 | 503 | 2026-10-01 00:15:00 | changed mind\n-- 5 | 504 | 2026-06-30 23:59:00 | damaged\n\n-- sample: products\n-- 7  | Trail Jacket    | outerwear\n-- 8  | Wool Socks      | NULL\n-- 9  | Camp Mug        | kitchen\n\n-- query from last quarter's report (Oskar), for reference:\n-- SELECT p.category, COUNT(*) AS n\n-- FROM returns r\n-- JOIN order_items oi ON oi.id = r.order_item_id\n-- JOIN products p ON p.id = oi.product_id\n-- GROUP BY p.category;","rules":[{"type":"codeBlocks","count":1},{"type":"maxCodeLines","max":14},{"type":"maxProseWords","max":30}],"mistakes":[{"title":"Counting rows, not items","why":"The AI copies COUNT(*) from the old query, so the logger's duplicate rows inflate the numbers.","fix":"Add: \"The logger writes duplicates; count DISTINCT order_item_id.\"","checks":["r0"]},{"title":"Losing the NULL category","why":"Grouping by a NULL category gives an unlabeled row the report can't read.","fix":"Add: \"Show NULL categories as 'Uncategorized'. Columns exactly category, returned_items.\"","checks":["r1"]},{"title":"Fuzzy quarter edges","why":"Comparing to '2026-09-30' drops returns later that day, like the 22:40 one.","fix":"Add: \"returned_at >= '2026-07-01' AND returned_at < '2026-10-01'.\"","checks":["r2"]},{"title":"A query plus a tutorial","why":"The AI explains GROUP BY and HAVING and offers variants.","fix":"Add: \"One query in one code block, no alternatives, one sentence max.\"","checks":["codeBlocks","maxCodeLines","maxProseWords"]}],"promptHints":[{"label":"Mentions the duplicate rows","patterns":["distinct","duplicate","twice"]},{"label":"Handles missing categories","patterns":["uncategorized","null","coalesce"]},{"label":"Gives the date range","patterns":["q3","2026-07","july","2026-10-01"]},{"label":"Names the output columns","patterns":["returned_items","column"]},{"label":"Asks for one query only","patterns":["one query","single query","one code block","no alternatives"]}],"hint":"The data has quirks only you know about. Tell the AI how duplicates and missing categories should be handled, the exact date range and the columns you want.","rubricLabels":["Counts COUNT(DISTINCT r.order_item_id) rather than COUNT(*), so duplicate return rows for the same item count once","Shows NULL categories as 'Uncategorized' (e.g. COALESCE(p.category, 'Uncategorized')) and groups by that value, and the output columns are exactly category and returned_items","Keeps only returns from 2026-07-01 through 2026-09-30 (e.g. returned_at >= '2026-07-01' AND returned_at < '2026-10-01'), filters groups with HAVING ... > 3, and orders by returned_items descending"]}