Reputation: 357
I have two tables. One consists of customers and another consists of products they have purchased:
Table customer
CustID, Name
1, Tom
2, Lisa
3, Fred
Table product
CustID, Item
1, Toaster
1, Breadbox
2, Toaster
3, Toaster
I would like to get all the customers that bought a Toaster, unless they also bought a breadbox.
So I have tried the following:
SELECT * FROM Customer
JOIN Product
ON Customer.CustID=Product.CustID
WHERE Product in
(SELECT
Item
FROM Product
WHERE (Item = 'Toaster' AND Item != 'Breadbox'));
And:
SELECT * FROM Customer
INNER Join Product
ON Customer.CustID=PRODUCT.CustID
WHERE Product.Item = 'Toaster'
AND Product.Item NOT IN ('Breadbox');
But both gives the same result, which includes Tom, that does already own a breadbox.
How can I make sure that only the customers that own a toaster, but do not own a breadbox gets listed?
Upvotes: 0
Views: 440
Reputation: 17915
select c.CustID, min(c.Name) as Name
from Customer c inner join Product p
on p.CustID = c.CustID
where p.Item in ('Toaster', 'Breadbox')
group by c.CustID
having
max(p.Item) = 'Toaster' and min(p.Item) = 'Toaster'
or
having
count(case when p.Item = 'Toaster' then 1 end) > 0
and count(case when p.Item = 'Breadbox' then 1 end) = 0
For the second case you don't strictly need the where
though it probably helps performance on large tables.
Upvotes: 0
Reputation: 56
This is my first post so forgive any missteps. There is a "many to one" relationship between the Customer and Product tables. To make the logical restrictions you want to apply, you would either need to aggregate the Product table or join the Product table twice. In effect, you are seeking to collapse the many to one relationship to a "one to one" relationship.
Here are some examples of where the Product table is joined twice.
SELECT DISTINCT
a.Name
FROM
Customer a
JOIN Product b ON a.CustID = b.CustID
LEFT JOIN Product c on a.CustID = c.CustID AND c.Item = 'Breadbox'
WHERE
b.Item = 'Toaster' AND
c.CustID IS NULL
or (slightly less efficient depending on indexes)
SELECT DISTINCT
a.Name
FROM
Customer a
JOIN Product b ON a.CustID = b.CustID
WHERE
b.Item = 'Toaster' AND
NOT EXISTS (SELECT 1 FROM Product c where a.CustID = c.CustID AND c.Item = 'Breadbox')
And, here is an example of where the Product table is joined once - possibly more complicated than you require.
SELECT
a.Name
FROM
Customer a
JOIN
(
SELECT
CustID,
SUM(case when Item = 'Toaster' then 1 else 0 end) sum_Toaster,
SUM(case when Item = 'Breadbox' then 1 else 0 end) sum_Breadbox
FROM
Product
WHERE
Item in ('Toaster','Breadbox')
GROUP BY
CustID
HAVING
SUM(case when Item = 'Toaster' then 1 else 0 end) > 0 AND
SUM(case when Item = 'Breadbox' then 1 else 0 end) = 0
) b ON a.CustID = b.CustID
Upvotes: 2
Reputation: 4383
SELECT distinct * FROM Customer
LEFT JOIN Product ON Customer.CustID=Product.CustID
WHERE Item = 'Toaster'
AND Customer.CustID NOT IN (
Select CustID FROM Product Where Item = 'Breadbox'
)
Upvotes: 1
Reputation: 331
SELECT * FROM Customer C
LEFT JOIN Product PT ON C.CustID = PT.CustID AND PT.Item = 'Toaster'
LEFT JOIN Product PB ON C.CustID = PB.CustID AND PB.Item = 'Breadbox'
WHERE PT.Item IS NOT NULL AND PB.Item IS NULL
Upvotes: 2
Reputation: 72175
Try this:
SELECT c.CustID, c.Name
FROM customer AS c
JOIN product AS p ON c.CustID = p.CustID
GROUP BY c.CustID, c.Name
HAVING SUM(p.Item = 'Toaster') >= 1 AND SUM(p.Item = 'Breadbox') = 0
Upvotes: 1