SQL - Advanced "forensic" queries - sql

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

Related

Find Top 5 Customers for Beverages based on their total purchase value 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

Returning total percentage within a group

Using this sample database: https://www.w3schools.com/sql/trysql.asp?filename=trysql_select_avg
I am trying to create a table that returns the percentage of orders per category, for each country. So it would look ideally something like this:
Country CategoryName Num_of_orders % of orders
Argentina Confections 1 0.50
Argentina Produce 1 0.50
Austria Beverages 5 0.17
Austria Condiments 6 0.21
So since Argentina has only had two orders, Confections will be 50%, and so will produce.
So far the closest I have gotten is returning a percentage of the total order count, but ignoring the countries.
SELECT c.country, ca.categoryname, count(o.orderid) as Num_of_orders,
count(*)/(select 1.0*count(*) from orders) as percentage
from orders as o
join customers as c on c.customerid = o.customerid
join orderdetails as od on od.orderid = o.orderid
join products as p on p.productid = od.productid
join categories as ca on ca.categoryid = p.categoryid
group by 1, 2
Any help would be appreciated.
I think you want something like the following, which selects the count by the country in the subquery, rather than the count of all orders.
SELECT c.country, ca.categoryname, count(o.orderid) as Num_of_orders,
count(*)/(select 1.0*count(*) from orders o2 join customers c2 on c2.customerid = o2.customerid join orderdetails od2 on od2.orderid = o2.orderid WHERE c2.country=c.country) as percentage
from orders as o
join customers as c on c.customerid = o.customerid
join orderdetails as od on od.orderid = o.orderid
join products as p on p.productid = od.productid
join categories as ca on ca.categoryid = p.categoryid
group by 1, 2
This version works on your sample database, but depending on what database your real query is being run against, you might consider counting over a partition instead of the subquery, or at least using a common table expression to avoid writing some of joins twice like I've done here.

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

Left Join in Oracle SQL

I was going through an example of LEFT JOIN on w3schools.com.
http://www.w3schools.com/sql/sql_join_left.asp
SELECT Customers.CustomerName, Orders.OrderID
FROM Customers
LEFT JOIN Orders
ON Customers.CustomerID=Orders.CustomerID
ORDER BY Customers.CustomerName;
The above query will return me all customers with No Orders as NULL Order ID+ All customers having Orders with their Order Ids
How should I modify this query so that it returns All Customers with No Orders + All Customers having Orders with Order date as '1996-09-18'
Thanks in advance.
If you want customers with no orders and those with a specific order date, then you want a WHERE clause:
SELECT c.CustomerName, o.OrderID
FROM Customers c LEFT JOIN
Orders o
ON c.CustomerID = o.CustomerID
WHERE (o.CustomerID is NULL) OR (o.OrderDate = DATE '1996-09-18)
ORDER BY c.CustomerName;
If you wanted all customers with their order on that date (if they have one), then you would move the condition to the ON clause:
SELECT c.CustomerName, o.OrderID
FROM Customers c LEFT JOIN
Orders o
ON c.CustomerID = o.CustomerID AND o.OrderDate = DATE '1996-09-18
ORDER BY c.CustomerName;
Note the difference: the first filters the customers. The second only affects what order gets shown (and NULL will often be shown).

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.