Reputation: 4728
I need to find the customerIDs of all customers who have ordered a certain quantity of particular products. For example, I want to get all customerIDs who have ordered 2 or more cheese or 5 or more bread. This should give me 1,4 and 3
customerID product qty
1 cheese 3
1 bread 2
2 cheese 1
2 pizza 4
3 bread 10
3 tomato 4
4 cheese 2
It would be much easier if my table structure was altered to have a column for cheese, bread, pizza, bread and tomato. Unfortunately this isn't possible.
EDIT: The customerID/product combo is unique. I would also like to be able to search for when all conditions are met. So, where 3 or more cheese AND 2 or more bread.
Upvotes: 1
Views: 66
Reputation: 108370
EDIT My bad... I misread the specification. The examples below returns customers that had 'cheese' qty>=2 AND 'bread' qty >=5. The specification was for OR rather than AND.
Here's an example of one approach (out of several viable approaches) to returning the specified a resultset that wasn't specified... customers that had BOTH cheese qty>=2 AND bread qty>=5:
SELECT a.customer_id
FROM ( SELECT p.customer_id
FROM mytable p
WHERE p.product = 'cheese'
GROUP BY p.customer_id
HAVING SUM(p.qty) >= 2
) a
JOIN ( SELECT q.customer_id
FROM mytable q
WHERE q.product = 'bread'
GROUP BY q.customer_id
HAVING SUM(q.qty) >= 5
) b
ON b.customer_id = a.customer_id
I've assumed you wanted to check the qty
for a given product and given customer as the "total" qty, from all of the matching rows, not just checking for an individual row with qty over the specified value. If you only want to check qty on individual rows, then remove the predicate on the aggregate i.e. HAVING SUM(qty) >= n
, and add a predicate in the WHERE clause AND qty >= n
.
EDIT To get the result of customers that had EITHER, we could use a similar approach, getting the list of customers that had cheese qty>=1, and combining that with the list of customer that had bread qty>=5, and get a distinct list.
( SELECT p.customer_id
FROM mytable p
WHERE p.product = 'cheese'
GROUP BY p.customer_id
HAVING SUM(p.qty) >= 2
)
UNION
( SELECT q.customer_id
FROM mytable q
WHERE q.product = 'bread'
GROUP BY q.customer_id
HAVING SUM(q.qty) >= 5
)
There are some other approaches. You specifically asked about changing "rows" into "columns". The term we usually use to refer to that type of operation is "pivot" or "pivot table".
Here's an example of that approach:
SELECT t.customer_id
, SUM(IF(t.product= 'cheese' ,t.qty,NULL)) AS `qty_cheese`
, SUM(IF(t.product= 'bread' ,t.qty,NULL)) AS `qty_bread`
FROM mytable t
GROUP BY t.customer_id
HAVING `qty_cheese` >= 2
OR `qty_bread` >= 5
Again, this is checking the "total" qty for a given customer and given product. If you just want to check for the existence of a row that has qty >= n
, then replace the SUM()
aggregate function with MAX()
.
For each row returned from mytable, we're evaluating an expression, comparing product
to a specific value, and if it matches, we're returning the value from the qty
column.
We typically do this in the SELECT list, just because it allows us to debug the query, we can leave off the "HAVING" clause, and get the list of all customers, and see the qty
for each product.
It's not necessary that these expressions be in the SELECT list. These expressions could be moved to the HAVING clause.
SELECT t.customer_id
FROM mytable t
GROUP BY t.customer_id
HAVING SUM(IF(t.product= 'cheese' ,t.qty,NULL)) >= 2
OR SUM(IF(t.product= 'bread' ,t.qty,NULL)) >= 5
There are a couple of other viable approaches.
Upvotes: 1
Reputation: 26784
SELECT t.customerID,
SUM(CASE WHEN t.product='cheese' THEN quantity ELSE 0 END) as cheese,
SUM(CASE WHEN t.product='bread' THEN quantity ELSE 0 END) as bread
FROM t
GROUP BY t.customerID
HAVING cheese>=2
OR bread>=5;
Or move them to the HAVING clause for clarity
Upvotes: 2
Reputation:
select distinct customerID from Products
where (product = 'cheese' and qty >= 2) or (product = 'bread' and qty >= 5);
Upvotes: 2