Field manual · SQL traps
A raw count alone rewards whoever shows up most; a rate with no minimum sample rewards whoever shows up least. You need both at once.
Any ranking of "the best", "the most suspicious" or "the worst store": if the result is a proportion, the first question is what it's being divided by.
Wrong: a count presented as if it were a rate
SELECT store, count(*) AS refunds
FROM refunds
GROUP BY store
ORDER BY refunds DESC;
-- the store with the most sales looks the worst, even if it refunds less than anyone
Right: refunds over that store's total orders
SELECT o.store,
count(r.order_id) * 1.0 / count(o.order_id) AS refund_rate,
count(o.order_id) AS orders
FROM orders o
LEFT JOIN refunds r ON r.order_id = o.order_id
GROUP BY o.store
HAVING count(o.order_id) >= 30 -- without this, a store with 2 orders and
-- 1 refund "wins" at 50%
ORDER BY refund_rate DESC;
The count points at whoever sells the most; a rate with no minimum points at whoever sells the least, because with few orders any extreme proportion is easy to hit. A HAVING count(*) >= N throws out samples too small for the rate to mean anything, and it's worth always comparing against the overall rate: if 5% of all orders get refunded, 12 out of 80 (15%) stands out; 1 out of 2 says nothing, even though it's "50%".
This trap — and four more — is hiding inside a real case file, with real data.
Play the first case free →