Selecting customers with multiple purchases - sql

teble relationship
I have 4 tables for keeping track of purchases by clients. My goal is to select the names of clients that purchased 2 specific products (e.g. book and pencils)
The query which I thought of (and which is obviously incorrect) is:
FROM customer c
join purchase p on c.customer_id = p.customer_id
join product pr on p.product_id = pr.product_id
WHERE EXISTS (
SELECT 1
FROM Purchase
WHERE p.customer_id = c.customer_id
AND pr.product_name = 'Book')
AND EXISTS (
SELECT 1
FROM Purchase
WHERE p.customer_id = c.customer_id
AND pr.product_name = 'Pencils')
Which return nothing, when I know that there is at least 1 customer than fits the criteria.
Thank you in advance!

You can use in, group by, and having to fetch all customer_id which bought both pencil and book. The having is there to ensure both items actually bought.
with
relevant_purchases as
(
select customer_id
from purchase pu
left join product pr
on pu.product_id = pr.product_id
where product_name in ('book', 'pencil')
group by customer_id
having count(distinct pu.product_id) = 2
)
select *
from customer c
where exists (
select *
from relevant_purchases rp
where c.customer_id = rp.customer_id
);
Fiddle: https://www.db-fiddle.com/f/vdpUWSse7gcLi1CmnNrRgs/1

Related

Customer product sale query incorrect result in sql server 2016

My DDL looks like below:
CREATE TABLE CUSTOMER
(
ID INT PRIMARY KEY,
CUSTOMER_NAME VARCHAR(50),
CITY_ID INT,
)
CREATE TABLE product
(
id int,
sku VARCHAR(50),
product_name varchar(100),
stock_qty int
)
create table Invoice
(
id int,
invoice_number varchar(100),
customer_id int
)
CREATE TABLE Invoice_item
(
id int,
invoice_id int,
product_id int,
quantity decimal(5,2),
price decimal(5,2),
line_total_price decimal(5,2)
)
I am trying to get sales details of all customer and products
output should return all customer even customer without invoices and
also all product even those product that were not sold.
I need to print customer even not have invoice and even those product
that were not sold
than customer and product as NA and quantity as o
Code i have written:
SELECT ISNULL(c.customer_name,'N/A')AS customer_name,ISNULL(p.product_name,'N/A') AS product_name,
sum(ISNULL(invitm.quantity,'0')) as quantity
FROM customer as c left outer join product as p
on c.id = p.id
left outer join invoice as inv on c.id = inv.id
left outer join invoice_item as invitm on c.id = invitm.id
group by c.customer_name,p.product_name
But this is giving incorrect result. am i doing any mistake with join. please share your suggestion
If you want all customer and product combinations, then I would suggest:
select c.customer_name, p.product_name,
coalesce(sum(ii.quantity), 0) as quantity
from customer c cross join
product p left join
invoice i
on c.id = i.customer_id left join
invoice_item ii
on ii.invoice_id = i.id and ii.product_id = p.id
group by c.customer_name, p.product_name;
If you want all customer/product combinations that exist and then extras for the customers and products that don't exist, I would suggest union all:
select c.customer_name, p.product_name,
coalesce(sum(ii.quantity), 0) as quantity
from invoice i join
customer c
on c.id = i.customer_id join
invoice_item ii
on ii.invoice_id = i.id join
product p
on ii.product_id = p.id
group by c.customer_name, p.product_name
union all
select c.customer_name, null, 0
from customer c
where not exists (select 1 from invoice i where i.customer_id = c.id)
union all
select null, p.product_name, 0
from product p
where not exists (select 1 from invoice_item ii where ii.product_id = p.id);
Maybe like this (I have not checked syntax) - all your joins were totally wrong; you need to join on corresponding fields (foreign keys), not on id-s:
SELECT ISNULL(c.customer_name,'N/A')AS customer_name,ISNULL(p.product_name,'N/A') AS product_name,
sum(ISNULL(invitm.quantity,'0')) as quantity
FROM customer as c full outer join product as p
on c.product_id = p.id
left outer join invoice as inv on c.id = inv.customer_id
left outer join invoice_item as invitm on invitm.invoice_id = inv.id and invitm.product_id = p.id
group by c.customer_name,p.product_name
This is actually a interview question. Below query might be correct:
SELECT c.customer_name,
p.product_name,
Coalesce((ii.quantity), 0) AS quantity
FROM customer c
LEFT JOIN invoice i
on c.id = i.customer_id
LEFT JOIN invoice_item ii
ON ii.invoice_id = i.id
LEFT JOIN product p
ON ii.product_id = p.id
ORDER BY c.customerid,
p.product_id,
ii.id
UNION
SELECT 'N/A',
p.product_name,
0
FROM products p ORDER p.id

Joining 2 Many-To-Many Relationship Tables

Problem:
Find the net balance of the total order amount and total payments for each customer.
There are 4 tables involved: OrderDetails, Orders, Payments and Customer.
The total amount order = quantity order * price each [in OrderDetails]
The total payment = sum of different payments for the same order.
Customers are linked to Payments and Orders with CustomerNumber. Orders are linked to OrderDetails with OrderNumber.
I tried joining the 4 tables with the INNER JOIN function.
SELECT
c.customername,
SUM(od.quantityordered * od.priceeach) - SUM(p.amount) AS Net_Balance
FROM
(
(
orderdetails od
INNER JOIN orders o ON od.ordernumber = o.ordernumber
)
INNER JOIN customers c ON o.customernumber = c.customernumber
)
INNER JOIN payments p ON c.customernumber = p.customernumber
GROUP BY c.customername;
The expected results should be 0 for almost every customers.
However, what I have got is the total amount order and total payment are multiplied by some constants. Specifically, the total payment shown is multiplied by the number of payments for each order.
Anybody have any ideas to save my life?
This is a typical issue when dealing with N-M relationships. To solve it, one solution is to move the aggregation to subqueries:
SELECT c.customername, o.amt - p.amt AS Net_Balance
FROM customers c
INNER JOIN (
SELECT ord.customernumber, SUM(det.quantityordered * det.priceeach) as amt
FROM orders ord
INNER JOIN orderdetails det ON ord.ordernumber = det.ordernumber
GROUP BY ord.customernumber
) o ON o.customernumber = c.customernumber
INNER JOIN (
SELECT customernumber, SUM(amount) as amt
FROM payments
GROUP BY customernumber
) p ON p.customernumber = c.customernumber
SELECT c.customername, SUM(od.quantityordered*od.priceeach) as ordersum , SUM(p.amount) as paymentsum'
What's the result of the two columns ?
Is it what you want?

Best way of excluding customers with a specific order

I'm running a pretty basic query. The issue is I'm trying to get results with all of our customers who never ordered product Y. The problem is, if I use a simple WHERE ProductColumn <> 'Product Y', it doesn't work because almost all of our customers have ordered other products.
Basically, I'm wondering how I could exclude on the customer level (instead of the order level) - if a customer has ordered Product Y, I don't want them showing up in my results at all.
Thanks.
You are probably looking for EXISTS().
If I want to find customers who have placed orders:
SELECT c.*
FROM customers c
WHERE EXISTS (
SELECT 1
FROM orders o
WHERE o.customerid = c.customerID
AND productID = 'Y'
)
If I want to find customers who have not placed orders:
SELECT c.*
FROM customers c
WHERE NOT EXISTS (
SELECT 1
FROM orders o
WHERE o.customerid = c.customerID
AND productID = 'Y'
)
Try this:
select * from customers c
where not exists(select 1 from customers
where cutomer_id = c.customer_id
and productcolumn = 'product y')
This assumes you have 'customer_id' column (or at least some id column in your table).
Simple LEFT JOIN should work:
SELECT c.*
FROM customers c
LEFT OUTER JOIN orders o
ON o.customerid = c.customerID
WHERE o.ProductColumn <> 'Product Y'

I need my query to return where it will return where an item was purchased by more than 3 customers. However, my query keeps returning Null

I have confirmed that there is instances where an item was purchased more than 3 times by 3 SEPERATE customers. However, my code keeps returning Null. I have tried several different variations of the code below, but I get either Null or an error message stating "Ambiguous column name 'Customer_ID'."
I have also tried aliasing it, but with no luck. Where am I going wrong?
SELECT First_Name, Last_Name, Country, Address, State, Zip, Product_Name
FROM Orders
JOIN Customer ON Orders.Customer_ID = Customer.Customer_ID
JOIN Amazon_Inventory ON Orders.Inventory_ID = Amazon_Inventory.Inventory_ID
JOIN Shipment ON Amazon_Inventory.Shipment_ID = Shipment.Shipment_ID
JOIN Product ON Shipment.Product_ID = Product.Product_ID
JOIN Product_Listing ON Product.Listing_ID = Product_Listing.Listing_ID
WHERE ORDER_ID IN
(SELECT Customer_ID, Inventory_ID
FROM Orders
GROUP BY Customer_ID, Inventory_ID
HAVING COUNT (Order_ID) >3 AND COUNT (INVENTORY_ID) >3);
The error is on IN subquery,you can't use IN to compare multiple fields.
If I understand correct you might expect this.
You could provide some sample data and expect result.I will edit my answer.
SELECT First_Name, Last_Name, Country, Address, State, Zip, Product_Name
FROM Orders
JOIN Customer ON Orders.Customer_ID = Customer.Customer_ID
JOIN Amazon_Inventory ON Orders.Inventory_ID = Amazon_Inventory.Inventory_ID
JOIN Shipment ON Amazon_Inventory.Shipment_ID = Shipment.Shipment_ID
JOIN Product ON Shipment.Product_ID = Product.Product_ID
JOIN Product_Listing ON Product.Listing_ID = Product_Listing.Listing_ID
WHERE Customer.Customer_ID IN
(SELECT Customer_ID
FROM Orders
GROUP BY Customer_ID
HAVING COUNT (1) >3 );
You are almost there. I would suggest you to use alias to your tables (that would solve the ambiguous errors), and also, you are using an IN referring to a SELECT with more than 1 column being selected (A tip: I would use an EXISTS instead of a IN whenever possible - but that's not the case - or at least it doesn't seems like the case with the info provided).
So, using aliases I would end up with something like this:
SELECT
*
/* Place appropriate alias in each field selected => First_Name, Last_Name, Country, Address, State, Zip, Product_Name */
FROM Orders AS OR
JOIN Customer AS CU ON Orders.Customer_ID = Customer.Customer_ID
JOIN Amazon_Inventory AS AI ON Orders.Inventory_ID = Amazon_Inventory.Inventory_ID
JOIN Shipment AS SH ON Amazon_Inventory.Shipment_ID = Shipment.Shipment_ID
JOIN Product AS PR ON Shipment.Product_ID = Product.Product_ID
JOIN Product_Listing AS PL ON Product.Listing_ID = Product_Listing.Listing_ID
WHERE
EXISTS
(SELECT 1 FROM Orders OREX
WHERE OREX.Inventory_ID = OR.Inventory_ID
GROUP BY OREX.Customer_ID, OREX.Inventory_ID
HAVING COUNT(1)>3);
I'm thinking something like this:
SELECT
c.Customer_ID, i.Inventory_ID,
c.First_Name, c.Last_Name, c.Country, c.Address, c.State, c.Zip,
p.Product_Name
FROM Orders o
JOIN Customer c ON o.Customer_ID = c.Customer_ID
JOIN Amazon_Inventory i ON o.Inventory_ID = i.Inventory_ID
JOIN Shipment s ON i.Shipment_ID = s.Shipment_ID
JOIN Product p ON s.Product_ID = p.Product_ID
JOIN Product_Listing l ON p.Listing_ID = l.Listing_ID
)
WHERE Inventory_ID IN (
SELECT Inventory_ID
FROM Orders JOIN Customer ON Orders.Customer_ID = Customer.Customer_ID
GROUP BY Inventory_ID
HAVING COUNT (DISTINCT Customer_ID) > 3
);

Fetch data from more than one tables using Group By

I am using three tables in a PostgreSql database as:
Customer(Id, Name, City),
Product(Id, Name, Price),
Orders(Customer_Id, Product_Id, Date)
and I want to execute a query to get from them "the customers that have have ordered at least two different products alnong with the products". The query I write is:
select c.*, p.*
from customer c
join orders o on o.customer_id = c.id
join product p on p.id = o.product_id
group by (c.id)
having count(distinct o.product_id)>=2
It throws the error:
"column "p.id" must appear in the GROUP BY clause or be used in an aggregate function
LINE 1: select c.*, p.*".
However if I remove the the p.* from select statement (assuming that I one does not want the products, only the customers), it runs fine. How can I get the products as well?
Update: Having ordered two or more products, a customer must appear on the output as many times as its product he has ordered. I want as output a table with 5 columns:
Cust ID | Cust Name | Cust City | Prod ID | Prod Name | Prod Price
Is it possible in SQL given that group by should be used? Shoul it be used on more than one columns on different tables?
Try this out :
SELECT distinct c.* ,p.*
FROM Customer c
JOIN
(SELECT o.customer_id cid
FROM Product P
JOIN Orders o
ON p.id= o.product_id
GROUP BY o.customer_id
HAVING COUNT(distinct o.product_id)>=2) cp
ON c.id =cp.cid
JOIN Orders o
on c.id=o.customer_id
JOIN Product p
ON o.product_id =p.id
I hope it solves your problem.
I think you can use following query for this question -
SELECT C1.*, p1.*
FROM Customer C1
JOIN Orders O1 ON O1.Customer_Id = C1.Id
JOIN Product P1 ON P1.Id = O1.Product_Id
WHERE C1.Id IN (SELECT c.Id
FROM Customer c
JOIN Orders o ON o.Customer_Id = c.Id
GROUP BY (c.Id)
HAVING COUNT(DISTINCT o.Product_Id) >= 2)