I would like to write a query that prints a result set whose rows are customers and the columns are the items with a ‘Y’ or ‘N’ in each field depending on whether the customer has purchased that item.
In short, I have the following tables:
CustomerID | CustomerName |
---|---|
1 | Joe Bloggs |
2 | Jane Doe |
3 | John Smith |
ItemID | ItemName |
---|---|
1 | knife |
2 | fork |
3 | spoon |
PurchaseID | CustomerID | ItemID |
---|---|---|
1 | 1 | 1 |
2 | 2 | 1 |
3 | 2 | 2 |
4 | 3 | 3 |
I would like to write a query that gives the following result:
Customer | Knife | Fork | Spoon |
---|---|---|---|
Joe Bloggs | Y | N | N |
Jane Doe | Y | Y | N |
John Smith | N | N | Y |
I have written the following query which does the job
SELECT CustomerName, [appropriate case statements for the 'Y' and 'N'] FROM ct LEFT JOIN pt AS pt1 ON ct.customerID = pt1.customerID LEFT JOIN pt AS pt2 ON ct.customerID = pt2.customerID LEFT JOIN pt AS pt3 ON ct.customerID = pt3.customerID WHERE pt1.itemID = 1 AND pt2.itemID = 2 AND pt3.itemID = 3
The idea here is that I have to join another copy of the purchase table for each item. With a lot of items, this becomes extremely computationally costly.
I am wondering if there is any way I can get the same result without having to do a join for every item. Is there a way to do it with just one join? Any ideas are appreciated.
Advertisement
Answer
You want conditional aggregation:
SELECT c.CustomerName AS Customer, CASE WHEN COUNT(CASE WHEN i.ItemName = 'knife' THEN 1 END) > 0 THEN 'Y' ELSE 'N' END AS Knife, CASE WHEN COUNT(CASE WHEN i.ItemName = 'fork' THEN 1 END) > 0 THEN 'Y' ELSE 'N' END AS Fork, CASE WHEN COUNT(CASE WHEN i.ItemName = 'spoon' THEN 1 END) > 0 THEN 'Y' ELSE 'N' END AS Spoon FROM Customers c LEFT JOIN CustomerItem ci ON ci.CustomerID = c.CustomerID LEFT JOIN Items i ON i.ItemID = ci.ItemID GROUP BY c.CustomerName;