Reputation: 77
I have the following three tables:
Customer
id (PK)
name
Product
id(PK)
name
price
Transaction
id(PK)
customer_id(FK ref Customer.id)
product_id(FK ref Product.id)
date_created
I'm running the following query which gives me the the amount of products used per customer:
SELECT p.id, p.name, Count(p.id), p.price
FROM transaction t, product p, customer c
WHERE p.id = t.product_id
AND c.id = t.customer_id
AND c.id = 1
group by p.price, p.name, p.id
ORDER BY p.name ASC;
This works, except that it gives me ONLY the products which the customer has used with their respective count. What I want is the list of ALL the products with a count of 0 for those products which the customer hasn't used. I'm having trouble trying to figure this out since I'm using the Transaction table as a joining table between the customers and the products, and the way I'm finding the count of each product per customer is by a count of records (as you can see in the SQL code above).
I could do:
SELECT *
FROM product
WHERE product.name NOT IN (SELECT p.name
FROM transaction t, product p, customer c
WHERE p.id = t.product_id
AND c.id = t.customer_id
AND c.id = 1);
and then somehow programmatically assign 0 to the count in my resultset, but then I'll end up with two lists, one that contains the name of the product, the count of each product, and the price per product that the customer has used, and another list that will contain all the product names, a count of 0, and the price for each product that the customer has not used.
This could be a solution, but it seems far from practical since id be dealing with two list and having to programmatically assign values instead of dealing with one list that would contain all the data that I need, which would affect ordering, etc.
I'm wondering if this is possible or if there's another way to look at this problem.
I hope this is clear enough. I'd appreciate any insight into this!
Mauricio B.
Upvotes: 1
Views: 1021
Reputation: 267
You have to do two select queries and join them:
select prodcust.custname, prodcust.prodname, prodcust.prodId,
isnull(tranCount,0) as count, prodcust.price
from
(
select p.name as prodname, p.id as prodID, c.name as custname, c.id as
custid, p.price
from product p, customer c
)
as prodcust
left join
(
select p.price, p.id, c.id as custid, count(t.id) as tranCount
from Transaction t
inner join customer c
on c.id = t.customer_id
inner join product p
on t.product_id = p.id
group by p.price, p.id, c.id
) as trans
on prodcust.custid = trans.custid
and prodcust.prodID = trans.id
order by custname, prodname
Upvotes: 0
Reputation: 37472
This should work in most DBMS. It groups the transaction table for all the customers products and gets their count. This is then left joined to the products. As it is a left join, all products will be included. For those products the customer hasn't used the joined columns will be NULL
. So will be the count. With the coalesce()
function returning the first argument not null, we get 0 as count for those and the actual count for the others. If your actual DBMS doesn't support coalesce()
you might have to replace that with the respective DBMS specific function.
SELECT p.id,
p.name,
coalesce(t.count, 0) count,
p.price
FROM product p
LEFT JOIN (SELECT t.product_id,
count(*) count
FROM transaction t
WHERE t.customer_id = 1
GROUP BY t.product_id) t
ON t.product_id = p.id
ORDER BY p.name ASC;
Upvotes: 1
Reputation: 1269773
Never use commas in the FROM
clause. Always use proper, explicit, standard JOIN
syntax.
Your problem is specifically that you need a left join
:
select p.id, p.name, Count(t.id), p.price
from product p left join
transaction t
on p.id = t.product_id and
t.customer_id = 1
group by p.price, p.name, p.id
order by p.name asc;
Note: you do not need the customer table. The id is in the transaction table.
Upvotes: 1