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

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

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

Access Query from multiple tables

I currently have an Access Database with a few tables, among them Order, OrderDetails and Client.
Order (OrderID, TimeStamp, FKEmployeeID, FKClientID, OrderStatus, Comments)
OrderDetails (OrderDetailsID, FKOrderID, FKProductID, Quantity, Cost, Total Cost)
Client (ClientID, Name)
I'm trying to build a query where I can get the total orders that a client has made and the total Items.
Example:
Customer, Total Orders, Total Items
John, 5, 15
Alex, 2, 30
Ana, 1, 3
Whenever I try to make a query Total Orders and Total Items give me the same number.
Any help would be greatly appreciated!
This is supported even by ms access:
SELECT c.Name,
(select count(*)
from Orders o
where o. FKClientID = C.ClientID) as [Total Orders],
(select sum(Quantity) as Items
from OrderDetails od
inner join Order o on o.OrderID = od.FKOrderID
where o.FKClientId = C.ClientID) as [Total Items]
from Client c;
Unfortunately, MS Access doesn't support COUNT(DISTINCT). You can do this with two aggregations:
SELECT c.[Name], COUNT(*) As NumOrders, SUM(o.NumItems) As NumItems
FROM Client as c INNER JOIN
(SELECT o.OrderID, o.FKClientID, COUNT(*) As NumItems
FROM [Order] as o INNER JOIN
OrderDetails as od
ON od.FKOrderID = o.OrderID
GROUP BY o.OrderID, o.FKClientID
) as o
ON o.FKClientID = c.ClientId
GROUP BY c.ClientId, c.Name;
What about this solution (clients without orders are left out hereby):
SELECT Client.[Name],
Count(myTotalItems.OrderID) As TotalOrders,
Sum(myTotalItems.TotalItems) As TotalItems
FROM Client,
(SELECT First([Order].OrderID) As OrderID,
First([Order].FKClientID) As
ClientID,
Count(OrderDetails.OrderDetailsID) As TotalItems
FROM [Order], OrderDetails
WHERE OrderDetails.FKOrderID like [Order].OrderID
GROUP BY [Order].OrderID) As myTotalItems
WHERE myTotalItems.ClientID like Client.ClientID
GROUP BY Client.[Name];
It would be easier for you if you divide the task into multiple queries. I am giving the sample using Northwind database, you can test and see on it. Note that the structures and fieldnames are very similar to yours.
First create one that gets ClientId, OrderId, OrderDate and Sum of Quantity.
SELECT c.CustomerId, o.OrderId, o.OrderDate, sum(od.Quantity) AS Qty
FROM (Customers AS c INNER JOIN Orders AS o ON c.CustomerId = o.CustomerId)
INNER JOIN [Order Details] AS od ON o.OrderId = od.OrderID
GROUP BY c.CustomerId, o.OrderId, o.OrderDate;
Save this as "OrderOfClients" (it would be saved in Queries). Next create a query that uses this one and asks for date range:
SELECT c.CustomerId, c.CompanyName,
Count(*) AS [Total Orders],
Sum(Qty) AS [Total Items]
FROM Customers AS c
INNER JOIN OrdersOfClients AS co ON c.CustomerId = co.CustomerId
WHERE co.OrderDate Between [#startDate] And [#endDate]
GROUP BY c.CustomerId, c.CompanyName;
You can save this one as "OrdersOfClientsSummary" and call for your report.
PS: In my personal opinion, if you use a database other than access you would be doing yourself a big favor.

sql use aggregate function that counts a unique value with group by using inner joins

I searched and found similar questions online but not my particular one, they all use where or having clause.If theres one similar to mine please link it. It's a 2 part question and I have the first one done. Thank you in advance.
Okay so heres the question, part 1
"Find by customer, the total cost and the total discounted cost for each product on the order ?".
It also asks to use inner joins to find the customer and order it a specific way. Below is the answer.
SELECT
C.companyname, O.orderid, O.orderdate, P.productname,
OD.orderid, OD.unitprice, OD.qty, OD.discount,
(OD.unitprice * OD.qty - (OD.qty * OD.discount)) AS TotalCost,
(OD.qty * OD.discount) AS TotalDiscountedCost
FROM
Sales.Customers AS C
INNER JOIN
Sales.Orders AS O ON C.custid = O.custid
INNER JOIN
Sales.OrderDetails OD ON O.orderid = OD.orderid
INNER JOIN
Production.Products as P ON OD.productid = P.productid
ORDER BY
C.companyname, O.orderdate;
Now the second question is to
follow up and resume the first one by "customer and the order date year, the total cost and the total discounted cost on the order ?". It also asks for this, "Project following columns in the select clause as.
GroupByColumns.companyname
GroupByColumns.OrderdateYear
AggregationColumns.CountNumberOfIndividualOrders
AggregationColumns.CountNumberOfProductsOrders
AggregationColumns.TotalCost
AggregationColumns.TotalDiscountedCost
Finally to order by company name and orderdateYear( which are groups). Where im stuck is how to count the specific orders of qty that equal 1 as an aggregate function in the SELECT clause. I know it has to use the aggregate function COUNT because of the GROUP BY, just don't know how to. This is what I have.
SELECT
C.companyname, YEAR(O.orderdate) AS orderyear,OD.qty,
-- Where in the count function or if theres another way do I count all the
--single orders
--COUNT(OD.qty) AS indiviualorders,
(OD.unitprice * OD.qty - (OD.qty * OD.discount)) AS TotalCost,
(OD.qty * OD.discount) AS TotalDiscountedCost
FROM
Sales.Customers AS C
INNER JOIN
Sales.Orders AS O ON C.custid = O.custid
INNER JOIN
Sales.OrderDetails OD ON O.orderid = OD.orderid
INNER JOIN
Production.Products as P ON OD.productid = P.productid
GROUP BY
C.companyname, YEAR(O.orderdate)
ORDER BY
C.companyname, O.orderdate;
You case use a case statement inside a sum
SUM(CASE WHEN <xyz> THEN 1 ELSE 0 END)
But for the count of unique orders, use SELECT(DISTINCT ) on a key that is unique in the order table
SELECT COUNT(DISTINCT O.OrderID) As DistinctOrders FROM Table

SQL query with w3schools db

I should have asked multiple questions in my other post. Thanks to all who have helped, I am now stuck on another one..
Using the w3schools db, List SupplierID, SupplierName and ItemSupplied (count of number of items supplied by a supplier), sort the list first by number of items supplied (descending) and then by supplier name (ascending)
SELECT supplierid,
suppliername,
p.productname,
Count(s.supplierid) AS itemssupplied
FROM [Suppliers] AS s
INNER JOIN [Products] AS p
ON p.supplierid = s.supplierid
GROUP BY p.productid,
p.productname
ORDER BY Count (p.productid, p.productname) DESC
order BY s.suppliername
It's giving me an error, then again I am ordering by multiple ones. I think there's something I am not quite understanding here.
My other question is
List customers for each category and the total of order placed by that customer in a given category. In the query show three columnm: CategoryName, CustomerName, and TotalOrders (which is price * quantity for orders for a given customer in a given category). Sort this data in descending order by TotalOrders.
SELECT cg.CategoryName,
c.CustomerName,
Sum(p.Price * od.Quantity) AS TotalOrders
FROM [products] AS p
INNER JOIN [orderdetails] AS od
ON od.ProductID = p.ProductID
INNER JOIN [orders] AS o
ON o.OrderID = od.OrderID
INNER JOIN [customers] AS c
ON c.customerID = o.CustomerID
INNER JOIN [categories] AS cg
ON cg.CategoryID = p.CategoryID
GROUP BY c.CustomerName
ORDER BY TotalOrders DESC
Can someone please check if my query is correct? Thank you once again!
Question 1
You are really close but you only need to state ORDER BY once (also make sure to include all shown fields in your GROUP BY unless you are aggregating them):
SELECT SupplierID, SupplierName, p.ProductName, count(s.SupplierID) AS ItemsSupplied
FROM [Suppliers] AS s
INNER JOIN [Products] AS p ON p.SupplierID = s.SupplierID
GROUP BY p.ProductID, p.ProductName, SupplierID, SupplierName -- Added SupplierID, SupplierName
ORDER BY COUNT (p.productID, p.ProductName) DESC, s.SupplierName
Notice that you just place multiple sorts on the same line with a comma separating them.
Question 2
You're almost there but you need to group by any field that is not being aggregated. So in order not to get a parsing error, I added the cg.CategoryName to the GROUP BY line.
SELECT cg.CategoryName, c.CustomerName, Sum(p.Price*od.Quantity) AS TotalOrders
FROM [Products] AS p
INNER JOIN [OrderDetails] AS od ON od.ProductID = p.ProductID
INNER JOIN [Orders] AS o ON o.OrderID = od.OrderID
INNER JOIN [Customers] AS c ON c.customerID = o.CustomerID
INNER JOIN [Categories] AS cg ON cg.CategoryID = p.CategoryID
GROUP BY c.CustomerName, cg.CategoryName --Added CategoryName
ORDER BY TotalOrders DESC
You have several problems with the first query:
You're grouping by ProductID and ProductName even though you want the number of items supplied by a supplier, which means that you want to group by SupplierID and SupplierName.
You're supplying too many arguments to the COUNT function, which takes a single column name or *.
You've included a ProductName column in your results, which is not called for.
You need to ORDER BY both the number of products supplied and the SupplierName.
With those points in mind:
SELECT
s.SupplierID,
s.SupplierName,
COUNT(p.ProductID) AS ItemsSupplied
FROM
[Suppliers] AS s
INNER JOIN [Products] AS p ON p.SupplierID = s.SupplierID
GROUP BY
s.SupplierID, s.SupplierName
ORDER BY
ItemsSupplied DESC,
s.SupplierName ASC
Your second query is quite close, you're just missing one point, which is that you're looking for total of order placed by that customer in a given category. This means that in addition to grouping by c.CustomerName, you need to group by cg.CategoryID:
SELECT
cg.CategoryName,
c.CustomerName,
SUM(p.Price*od.Quantity) AS TotalOrders
FROM
[Products] AS p
INNER JOIN [OrderDetails] AS od ON od.ProductID = p.ProductID
INNER JOIN [Orders] AS o ON o.OrderID = od.OrderID
INNER JOIN [Customers] AS c ON c.customerID = o.CustomerID
INNER JOIN [Categories] AS cg ON cg.CategoryID = p.CategoryID
GROUP BY
c.CustomerName, cg.CategoryID
ORDER BY
TotalOrders DESC
The first one has two order by clauses
ORDER BY COUNT (p.productID, p.ProductName) DESC
and
ORDER BY s.SupplierName
also some databases will complain when order by columns for queries using group by are not included in the selected columns

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)