Brightlane's growth team is analysing repeat-purchase behaviour to build a loyalty programme.
Write a query to return the customer ID and order count for every customer with more than three orders on record.
Assumptions:
- The
orderstable contains every order Brightlane has processed. - The threshold is on the per-customer count of orders — three orders does not qualify; four or more does.
- The condition applies to the per-customer count, not to individual orders.
Output:
- One row per qualifying customer, with columns
customer_idandorder_count.
Schema · ecommerce 5 tables
Run previews · Check grades
Write a query, then run it to see results here.
Worked solution Try it yourself first
SELECT
customer_id,
COUNT(*) AS order_count
FROM
orders
GROUP BY
customer_id
HAVING
COUNT(*) > 3 The shape
"More than three orders" is a per-customer condition, not a per-row one. GROUP BY customer_id builds the per-customer counts; HAVING COUNT(*) > 3 keeps only the groups that clear the threshold. The result is one row per qualifying customer — customer_id 17 with 5 orders, customer_id 51 with 4, and so on.
Clause by clause
SELECT customer_id, COUNT(*) AS order_countreturns the grouping column with the count for each surviving group.FROM ordersis the source set: every order Brightlane has processed.GROUP BY customer_idcollapses the order rows into one row per customer. After this clause, each row in the working set represents one customer with their order count attached.HAVING COUNT(*) > 3filters those per-customer rows. Customers with three or fewer orders drop out; four or more survive.
Why this and not WHERE COUNT(*) > 3
WHERE runs before grouping happens, when the only thing in scope is an individual order row. At that point COUNT(*) has no meaning — there is no group yet to count, and PostgreSQL raises an error. HAVING runs after GROUP BY has produced the per-customer groups, which is the only moment the per-customer count exists.
You practiced filtering on an aggregate result with HAVING. The recurring rule: WHERE filters rows before they reach the aggregate; HAVING filters groups after the aggregate has computed — "more than three orders" is a group-level condition, so it belongs in HAVING.