Returning total percentage within a group - sql

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.

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

Stuck doing a common table expression to calculate total orders for each product category using NORTHWND

Guys so as part of my job as a Data Support Analyst I am training up to become a software developer, my mentor gave me a group of test statements and this one seems way more advanced than anything I have done previously. The question is...
*
6) Write a subquery or common table expression to calculate total
orders for each product category. that will Write a query that will
bring back each product name, the total number of orders made for the
product and join to the subquery or CTE to calculate the percentage
of sales that product represents of its product category. For example,
if Category of Soaps has 2 products; Blue Soup and Red Soap, blue soup
had 40 orders and red soaps had 10 orders I would expect to see the
following rows: Product Name Total Orders % of Category Red
Soap 40 80%
Blue Soap 10 20%
*
So far I have managed to get the following but I'm struggling to progress past this...
;WITH [Products] (CategoryId, TotalNumberOfOrders)
AS (
SELECT p.CategoryId,
COUNT (OD.OrderID) as TotalNumberOfOrders
FROM [dbo].[Products] p
INNER JOIN [dbo].[Order Details] OD
ON p.ProductID=OD.ProductID
GROUP BY p.CategoryId )
SELECT * From Products
Any help would be fantastic! ( I'm using NORTHWND database btw)
I think what you need is
;WITH temp AS
(
SELECT p.CategoryId,
COUNT (DISTINCT OD.OrderID) as TotalNumberOfOrders
FROM [dbo].[Products] p
INNER JOIN [dbo].[Order Details] OD ON p.ProductID=OD.ProductID
GROUP BY p.CategoryId
)
SELECT p.ProductName,
Count(DISTINCT Od.OrderId) AS Total,
Count(DISTINCT Od.OrderId)*100/temp.TotalNumberOfOrders AS Percentage
FROM Products p
INNER JOIN [dbo].[Order Details] OD ON p.ProductId = OD.ProductId
INNER JOIN temp ON p.CategoryId = temp.CategoryId
GROUP BY p.ProductId, p.ProductName, temp.TotalNumberOfOrders
Remember Count(Distinct..) to count Order.
Using a sub-Query:
SELECT P.ProductName,
COUNT(OrderID) AS NumberOfOrders,
CAST((CAST(COUNT(OrderID) AS DECIMAL(9,2)) / CAST(Derived_Table.CNT AS
DECIMAL(9,2))) * 100 AS DECIMAL(9,2)) AS Percentage
FROM [Northwind].[dbo].[Order Details]
INNER JOIN dbo.Products AS P
ON [Order Details].ProductID = P.ProductID
INNER JOIN (SELECT COUNT(F.OrderID) AS CNT, P.CategoryID
FROM [Northwind].[dbo].[Order Details] F
INNER JOIN dbo.Products P
ON F.ProductID = P.ProductID
GROUP BY P.CategoryID) AS Derived_Table
ON P.CategoryID = Derived_Table.CategoryID
GROUP BY [Order Details].ProductID, P.ProductName, Derived_Table.CNT
ORDER BY [Order Details].ProductID

Query returns cartesian product when not expected

Task: Select all orders having products belonging to ‘Sea Food’ category.
Result: OrderNo, OrderDate, Product Name
I write this query but it returns Cartesian products.
select o.orderid, o.orderdate as "Order Date", p.productname , ct.categoryname from orders o,
order_details od , products p , customers c ,categories ct
where
od.orderid = o.orderid and p.productid = od.productid and ct.categoryid = p.categoryid
and ct.categoryname = 'Seafood';
Question: What is wrong with my query ?
You're doing a CROSS JOIN on customers table since you forgot to specify the connection. This is why you should use explicit JOIN syntax rather than old syntax using commas in WHERE clause.
After translating your query into explicit syntax, you will see that there is no WHERE condition involving customers table:
select
o.orderid,
o.orderdate as "Order Date",
p.productname,
ct.categoryname
from
orders o,
inner join order_details od on od.orderid = o.orderid
inner join products p on p.productid = od.productid
inner join categories ct on ct.categoryid = p.categoryid
cross join customers c -- either you don't need this table, or you need to specify conditions
where
ct.categoryname = 'Seafood'
Basically the reason you got it was that your where clause omitted join condition involving customers table, so you were left with:
from (...), customers -- cross join when joining condition not applied in where clause

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

SQL selection criteria on grouped aggregate

I'm trying to find a simple MySQL statement for the following two problems:
I have 4 tables: Employees, Customers, Orders, Products (Each entry in Orders contains a date, a reference one product, a quantity and a reference to a customer, and a reference to an Employee).
Now I'm trying to get all customers where the volume of sale (quantity * product.price) is bigger in 1996 than in 1995.
And: I want to list all Employees whose volume of sale is below the average volume of sale.
Any help would really be appreciated. I've managed to get the information using a php script but I think this can be done with some clever SQL Statements.
Can anybody help me?
Employee Table: ID# Name
Products Table: ID# NAME# PRICE
Orders Table: ODERID# CUSTOMERID # DATE # EMPLOYEE# PRODUCTID# QUANTITY
For the first part (assuming quite a bit about the schema):
SELECT Customers.ID
FROM Customers
LEFT JOIN orders AS o1 ON o1.CustomerID=Customers.ID AND YEAR(o1.DATE) = 1995
LEFT JOIN products AS p1 ON p1.id = o1.productID
LEFT JOIN orders AS o2 ON o2.CustomerID=Customers.ID AND YEAR(o2.DATE) = 1996
LEFT JOIN products AS p2 ON p2.id = o2.productID
HAVING SUM(o1.quantity* p1.price) < SUM(o2.quantity*p2.price)
I don't know the database type you're using, so I'll use sqlserver. The 'Year' function is available on most databases, so you should be able to rewrite the query for your db in question.
I think this is the query which returns all customerid's + ordertotal for the customers which have a higher total in 1996 than in 1995, but I haven't tested it. Crucial is the HAVING clause, where you can specify a WHERE kind of clause based on the grouped result.
SELECT o.CustomerId, SUM(o.Quantity * p.Price) AS Total
FROM Orders o INNER JOIN Products p
ON o.ProductId = p.ProductId
WHERE YEAR(o.Date) == 1996
GROUP BY o.CustomerId
HAVING SUM(o.Quantity * p.Price) >
(
SELECT SUM(o.Quantity * p2.Price) AS Total
FROM Orders o2 INNER JOIN Products p2
ON o2.ProductId = p.ProductId
WHERE YEAR(o.Date) == 1995
AND o2.CustomerId = o.CustomerId
GROUP BY o.CustomerId
)
something like that:
select * from customers c
where (select sum (o.quantity * p.price) from orders o, product p where o.productID = p.productID and o.dateyear = 1996 and o.customerID = c.customerID) < (select sum (o.quantity * p.price) from orders o, product p where o.productID = p.productID and o.dateyear = 1995 and o.customerID = c.customerID)
select * from employees e where (select avg (o.quantity * p.price) from orders o, product p where o.productID = p.productID and o.empID = e.EmpID) < (select avg (o.quantity * p.price) from orders o, product p where o.productID = p.productID)