Find Top 5 Customers for Beverages based on their total purchase value SQL - sql

Here is the link to the Data Set.
https://www.w3schools.com/sql/trysql.asp?filename=trysql_asc
I have been trying to solve this but couldn't find a way to get the total purchase value while grouping with the customer table

I would recommend using a Common Table Expression (CTE) as, in my experience, it helps with scalability/maintenance down the road and easily enables you to see what the data is under the hood if you wanted to simply run the CTE itself.
I join the Customer to the Order to get the OrderID
I join the Order to OrderDetails to get the ProductID and Order Quantity
I join the OrderDetails to Products to get the Price
I join the Categories to filter for just Beverages
All this is wrapped as a CTE (similar to a subquery), on top of which I can now aggregate at the Customer level and sequence by Order Value in a descending fashion.
with beverage_orders_cte as(
SELECT c.CustomerName, o.OrderID
, od.OrderDetailID, od.ProductID, od.Quantity
, p.ProductName, p.Price
, od.Quantity * p.Price as OrderVal
,cat.CategoryName FROM Customers c
inner join Orders o
on c.CustomerID = o.CustomerID
inner join OrderDetails od
on o.OrderID = od.OrderID
inner join Products p
on od.ProductID = p.ProductID
inner join Categories cat
on p.CategoryID = cat.CategoryID and cat.CategoryID = 1
)
select CustomerName, SUM(OrderVal) as Revenue
From beverage_orders_cte
Group by CustomerName
Order by Revenue desc
Limit 5
Hope this helps, good luck.

Something like that?
SELECT c.customerid,
Sum(p.price)
FROM customers AS c
INNER JOIN orders AS o
ON o.customerid = c.customerid
INNER JOIN orderdetails AS od
ON od.orderid = o.orderid
INNER JOIN products AS p
ON p.productid = od.productid
GROUP BY c.customerid
ORDER BY Sum(p.price) DESC
LIMIT 5
Just following on from your quantity comment...
SELECT c.customerid,
Sum(p.price),
Sum(p.price * od.quantity)
FROM customers AS c
INNER JOIN orders AS o
ON o.customerid = c.customerid
INNER JOIN orderdetails AS od
ON od.orderid = o.orderid
INNER JOIN products AS p
ON p.productid = od.productid
GROUP BY c.customerid
ORDER BY Sum(p.price) DESC
LIMIT 5

I think this is the best optimized code.
Please try with this.
SELECT CustomerID, Count(Quantity * Price) AS Total
FROM Orders, OrderDetails, Products
Where Orders.OrderID = OrderDetails.OrderID AND Products.ProductID = OrderDetails.ProductID
Group by CustomerID
ORDER BY Total DESC
LIMIT 5

Related

Save SQL Query Results in Variable

I have the following SQL Query, but it is very repetitive and so I am wondering if this can be cleaned up by storing the results of a SQL Query in a variable.
What I have is (which works, can be tested here)
SELECT ProductName FROM (
SELECT ProductName, SUM(Quantity) AS ProductQuantity FROM
(SELECT CustomerID FROM Customers AS C
WHERE Country = 'Germany') AS CG
INNER JOIN Orders AS O
ON CG.CustomerID = O.CustomerID
INNER JOIN OrderDetails AS OD
ON O.OrderID = OD.OrderID
INNER JOIN Products as P
ON OD.ProductID = P.ProductID
GROUP BY ProductName)
WHERE ProductQuantity = (
SELECT MAX(ProductQuantity) FROM (
SELECT ProductName, SUM(Quantity) AS ProductQuantity FROM
(SELECT CustomerID FROM Customers AS C
WHERE Country = 'Germany') AS CG
INNER JOIN Orders AS O
ON CG.CustomerID = O.CustomerID
INNER JOIN OrderDetails AS OD
ON O.OrderID = OD.OrderID
INNER JOIN Products as P
ON OD.ProductID = P.ProductID
GROUP BY ProductName))
And as you could tell the following code is repeated twice, is there any way I can do this without repeating this code:
(SELECT ProductName, SUM(Quantity) AS ProductQuantity FROM
(SELECT CustomerID FROM Customers AS C
WHERE Country = 'Germany') AS CG
INNER JOIN Orders AS O
ON CG.CustomerID = O.CustomerID
INNER JOIN OrderDetails AS OD
ON O.OrderID = OD.OrderID
INNER JOIN Products as P
ON OD.ProductID = P.ProductID
GROUP BY ProductName)
You can use CTE to avoid looping, maybe like this:
with cte as (
SELECT ProductName, SUM(Quantity) AS ProductQuantity FROM
(SELECT CustomerID FROM Customers AS C
WHERE Country = 'Germany') AS CG
INNER JOIN Orders AS O
ON CG.CustomerID = O.CustomerID
INNER JOIN OrderDetails AS OD
ON O.OrderID = OD.OrderID
INNER JOIN Products as P
ON OD.ProductID = P.ProductID
GROUP BY ProductName
)
SELECT ProductName
FROM cte
where ProductQuantity = (SELECT MAX(ProductQuantity) FROM cte)

How can I combine two queries into one

Using this query:
select C.CustomerID, P.ProductName, count(*) as Ordered
from Customers as C
join Orders as O on C.CustomerID = O.CustomerID
join [Order Details] as OD on O.OrderID = OD.OrderID
join Products as P on OD.ProductID = P.ProductID
group by C.CustomerID, P.ProductName
I can select customer's bought product's name and Ordered
CustomerID|ProductName |Ordered
--------------------------------
ANTON |Alice Mutton|1
BERGS |Alice Mutton|1
BLONP |Alice Mutton|1
BOLID |Alice Mutton|1
BONAP |Alice Mutton|1
And using this:
select P.ProductName, count(*) as Ordered
from Products as P
join [Order Details] as OD on P.ProductID = OD.ProductID
group by P.ProductName
I can select how many times each product has been ordered:
ProductName |Ordered
-------------------------
Alice Mutton |37
Aniseed Syrup |12
Boston Crab Meat |41
Camembert Pierrot|51
Carnarvon Tigers |27
Now, I want to combine theese two queries. I want to see how much each product was ordered by single Customer and what is total amount of orders of this product. How can I do that in one query?
Are you looking for something like this?:
select a.*, b.ordered from (
select C.CustomerID, P.ProductName, count(*) as Ordered
from Customers as C
join Orders as O on C.CustomerID = O.CustomerID
join [Order Details] as OD on O.OrderID = OD.OrderID
join Products as P on OD.ProductID = P.ProductID
group by C.CustomerID, P.ProductName)a
left join
(select P.ProductName, count(*) as Ordered
from Products as P
join [Order Details] as OD on P.ProductID = OD.ProductID
group by P.ProductName)b
on a.ProductName=b.ProductName
To this:
select C.CustomerID, P.ProductName, count(*) as Ordered
from Customers as C
join Orders as O on C.CustomerID = O.CustomerID
join [Order Details] as OD on O.OrderID = OD.OrderID
join Products as P on OD.ProductID = P.ProductID
group by C.CustomerID, P.ProductName
add something like this:
union
select sum(0) as CustomerID, P.ProductName, count(*) as Ordered
from Orders as O
join [Order Details] as OD on O.OrderID = OD.OrderID
join Products as P on OD.ProductID = P.ProductID
group by P.ProductName
Basically, you can put the items for the total orders and the customer orders into temp tables and then join them, as shown below. I chose Name to join on, but that may need to be adjusted, it is hard to tell.
I like these more than sub queries, I think they are typically faster, but you could do a sub query in the join as well. Similar concept. This probably wont give you the final result you're looking for, but hopefully it will give you a good start.
select P.ProductName, count(*) as Ordered
into #totalOrdered
from Products as P
join [Order Details] as OD on P.ProductID = OD.ProductID
group by P.ProductName
select C.CustomerID, P.ProductName, count(*) as Ordered
into #CustomerOrders
from Customers as C
join Orders as O on C.CustomerID = O.CustomerID
join [Order Details] as OD on O.OrderID = OD.OrderID
join Products as P on OD.ProductID = P.ProductID
group by C.CustomerID, P.ProductName
select *
From #CustomerOrders c
join #totalOrdered o on c.ProductName = o.ProductName

2 Questions on Northwind SQL

I have 2 questions about the Northwind SQL Server sample database that I don't know how to solve
Show CustomerID for all customers who have at least three different products, but never use both products in the same category.
Code I tried for this question:
SELECT
CustomerID, p.ProductID,ProductName, CategoryID
FROM
(Orders o
JOIN
[Order Details] od ON o.OrderID = od.OrderID)
JOIN
Products p ON od.ProductID = p.ProductID
Show CustomerID for customers who have orders from all categories.
I've been stuck on these queries for hours, please help guys!
This is link for Northwind sample database: https://northwinddatabase.codeplex.com/
For #2, you could use something like this:
SELECT
c.CustomerID, COUNT(DISTINCT p.CategoryID)
FROM
dbo.Customers c
INNER JOIN
dbo.Orders o ON o.CustomerID = c.CustomerID
INNER JOIN
dbo.[Order Details] od ON od.OrderID = o.OrderID
INNER JOIN
dbo.Products p ON p.ProductID = od.ProductID
GROUP BY
c.CustomerID
HAVING
COUNT(DISTINCT p.CategoryID) = (SELECT COUNT(*) FROM dbo.Categories)
This works for question #1:
SELECT c.CustomerID,
od.productid,
p.ProductName,
COUNT(od.productid),
ct.Category
FROM [Order Details] od
INNER JOIN [dbo].[Products] p on od.ProductID = p.ProductID
INNER JOIN [dbo].[Categories] ct on p.CategoryID = c.CategoryID
INNER JOIN [dbo].[Orders] o on od.OrderID = o.OrderID
INNER JOIN [dbo].[Customers] c on o.CustomerID = c.CustomerID
GROUP BY od.productid,
ct.CategoryID,
p.ProductName,
c.CustomerID
HAVING COUNT(od.productid) > 3
ORDER BY COUNT(od.productid) desc
;

SQL Server : How to Select Sum Amount Spent for the Most Expensive Item by a Customer - Northwind DB

Actually question tells all; Lots of customers has many orders with for many items; I'm trying to display the total amount spent for the most expensive item ordered by that customers through the all orders given by that customer. I'm using Northwind DB and tables like Customers, Orders, Order Details, Products. I've the query below, I've tried to limit it by an aggregate function but SQL does not allow it on where clause. Any help?
select
p.ProductName,
c.ContactName,
od.ProductID,
MAX(od.UnitPrice)
SUM(od.UnitPrice*od.Quantity) as Total
from
Customers c
join
Orders o ON c.CustomerID = o.CustomerID
join
[Order Details] od on od.OrderID = o.OrderID
join
Products p on od.ProductID = p.ProductID
where
c.CustomerID in
group by
c.ContactName, p.ProductName, od.Quantity, od.ProductID
order by
MAX(od.UnitPrice) desc
I think the easiest way to solve this is by using a window function to get the highest priced product. The following query uses row_number() for this purpose:
select p.ProductName, c.ContactName, od.ProductID,
MAX(od.UnitPrice)
SUM(od.UnitPrice*od.Quantity) as Total
from Customers c join
(select od.*, o.CustomerId,
row_number() over (partition by o.CustomerId
order by od.UnitPrice desc) as seqnum
from [Order Details] od join
Orders o
on od.OrderId = o.OrderId
) od
on od.CustomerId = c.CustomerId and seqnum = 1 join
Products p
on od.ProductID = p.ProductID
group by c.ContactName, p.ProductName, od.ProductID
order by MAX(od.UnitPrice) desc;
Note that the joins have been rearranged a bit. You need the customer id to define the highest priced product in the subquery, so the subquery has the join to orders. You don't need the join in the outer query.

SQL - Advanced "forensic" queries

I want to find out which one of the customers has purchased the largest total amount of items, with no date range
These are the columns I have:
customers.customerid
orders.orderid
orderdetails.productid
orderdetails.quantity
But I'm stuck here:
Start with customerid
Pull the orderid's from the customerid (using a join I assume)
Pull the quantity and individual productid's (another join? lol)
select * from orders returns 196 records.
select distinct customerid from orders returns 74 records, so that means 74 'distinct' customers placed orders
How to complete this?
First, find out how many items each customer ordered. To do that, you have to join the order detail and orders table:
select o.customerid, sum(od.quantity) as nbr_items
from orderdetail od
inner join orders o
on o.orderid = od.orderid
group by o.customerid
But if you need the customer name as well, which is fairly likely, you would have to join the customer table, too:
select o.customerid, sum(od.quantity) as nbr_items
from orderdetail od
inner join orders o
on o.orderid = od.orderid
inner join customer c
on c.customerid = o.customerid
group by o.customerid
But of course, you asked specifically for the customer with the MOST TOTAL AMOUNT of items, so sort descending by that total:
select o.customerid, sum(od.quantity) as nbr_items
from orderdetail od
inner join orders o
on o.orderid = od.orderid
inner join customer c
on c.customerid = o.customerid
group by o.customerid
order by 2 DESC
And use the TOP N function to get the first one only:
select top 1 o.customerid, sum(od.quantity) as nbr_items
from orderdetail od
inner join orders o
on o.orderid = od.orderid
inner join customer c
on c.customerid = o.customerid
group by o.customerid
order by 2 DESC