SQL nested aggregate grouping error - sql

I'm trying to find name of product which has sold maximum units, I've two tables, purchases and products, products has pname and pid, purchases has pid, qty(units sold).
I've managed this
select p.pname, sum(q.qty) from purchases q
inner join products p on p.pid=q.pid
where p.pid=q.pid
group by p.pname
order by sum(q.qty) desc
I'm getting the result in descending order but I need only the top most selling units, multiple products can have top most selling units. When I use
max(sum(q.qty))
I get grouping error.

One approach is to derive the values first using a common table expression.
Simply put you can't wrap aggregates in other aggregates. You may be able to wrap an aggregate around an analytic however.
with cte as (select p.pname, sum(q.qty) from purchases q
inner join products p on p.pid=q.pid
where p.pid=q.pid
group by p.pname
order by sum(q.qty) desc)
Select pname, max(purchases)
from cte
group by pname

You can use ctes to do this.
1)First get the total quantity of each product
2)Then get the maximum of all those totals
3)Join it with your original query
with totals as (select pid, sum(qty) totalqty from purchases group by pid)
, t1 as (select p.pid, p.pname, sum(q.qty) totqty
from purchases q
inner join products p on p.pid=q.pid
group by p.pname)
, t2 as (select max(totalqty) maxtotal from totals)
select pname, totqty
from t1
join t2 on t1.totqty = t2.maxtotal

Analytics can simplify this for you. If you have more than one product with the same sum(qty) and that happens to be the max(sum(qty)), then this should get you them:
select pname, quantity
FROM (
select p.pname
, sum(q.qty) quantity
,rank() over (order by sum(q.qty desc) ranking
from purchases q
inner join products p on p.pid=q.pid
group by p.pname
)
where ranking = 1

Related

How to find the earnings from the most sold product with sql?

I have two tables:
I need to find the product name that was sold the most and the earnings from that.
The code I wrote:
SELECT *
FROM Products
WHERE ProductId = (SELECT ProductId
FROM
(SELECT
ProductId,
SUM(Quantity) AS total_order,
MAX(SUM(Quantity)) OVER () AS maxSm
FROM
Orders
GROUP BY
ProductId)
WHERE
total_order = maxSm)
But with this I just find the product name that was sold the most. Can you tell me please how can I find the earnings only from this product?
select top 1
a.name,
(b.total * a.price) as revenue
from
products a
left join (select productid, sum(quantity) as total group by productid) b
on a.productid = b.productid
order by
b.total desc
You need to join the result of your derived table to your Products table.
Without actual sample data I am unable to test, however the following should be what you need, or at least very close:
select p.Name, o.total_order, o.total_order * p.Price as TotalValue
from (
select * from (
select ProductId,
Sum(Quantity) as total_order,
Max(Sum(Quantity)) over () as maxSm
from Orders
group byProductId
)t
where total_order = maxSm
)o join Products p on p.ProductId=o.ProductId

Display Product Name and City where that product sold in largest quantity

I'm trying to get a query to display the product name and city where the product had the highest quantity sold. Here is the code I'm working with:
SELECT DISTINCT
(s.city),
MAX(t.quantity),
p.Name
FROM [DS715-Cameron-Erwin].dbo.Tb_Transactions AS t,
[DS715-Cameron-Erwin].dbo.Tb_Product AS p,
[DS715-Cameron-Erwin].dbo.Tb_Supplier AS s
WHERE p.prod_id = t.prod_id
AND s.Supp_ID = t.Supp_ID
GROUP BY t.Prod_ID,
p.name,
s.city
ORDER BY p.name, s.city
This is giving me the highest quantity sold for each product in each city.
Sample Data
From the screenshot there are multiple records for each product (Airplane, Auto, Boat...). I'm trying to get a single record for each product where ever the highest quantity was purchased.
So, the top record would only show for Airplane because the most orders were from there.
You want to use the ROW_NUMBER() OVER functionality to order by the quantity and then select the one with the biggest quantity over each product.
SELECT
city,
quantity,
name
FROM
(
SELECT S.city,
T.quantity,
P.name,
ROW_NUMBER() OVER
( PARTITION BY
P.name
ORDER BY t.Quantity DESC
) as RowNum
FROM
Tb_Transactions T
INNER JOIN
Tb_Product P
ON
P.prod_id = T.prod_id
INNER JOIN
Tb_Supplier S
ON
S.supp_id = T.supp_id
) a
WHERE
RowNum = 1
http://sqlfiddle.com/#!6/628458/5
For this, I would use a CTE (also I would use the explicit INNER JOIN syntax):
;With CTE
As
(
Select
s.city
, t.quantity
, p.Name
, Row_Number Over (Partition By P.Name, s.city Order By t.Quantity Desc) as RN
From [DS715-Cameron-Erwin].dbo.Tb_Transactions as t
Inner Join [DS715-Cameron-Erwin].dbo.Tb_Product as p
On p.prod_id = t.prod_id
Inner Join [DS715-Cameron-Erwin].dbo.Tb_Supplier as s
On s.Supp_ID = t.Supp_ID
)
Select
city
, quantity
, Name
From CTE
Where RN = 1

Get the first 10 rows for each group

I have three tables:
Customer(IdCustomer, Name)
Product(IdProduct, Product)
Order(IdProduct, IdCustomer, nbOrders)
So the Order table stores how many times a customer has ordered a product.
I need a view like this:
TopOrder(Name, Product, nbCommands)
But I only want 10 products for each customer, the ones he ordered the most and I can't figure it out.
The dense_rank window function should be exactly what the doctor prescribed:
CREATE View TopOrder AS
SELECT Name, Product, nbOrders
FROM (SELECT Name, Product, nbOrders,
DENSE_RANK() OVER (PARTITION BY o.idCustomer
ORDER BY nbOrders DESC) AS rk
FROM Customer c
JOIN Orders o ON c.idCustomer = o.idCustomer
JOIN Product p ON p.idProduct = o.idProduct
) t
WHERE rk <= 10

Could not get the expected query with SQL Server

I´m trying to do a SQL query but I couldn´t get the expected result. I really don´t know what is going wrong.
I have a table Product which contains (product_id, title) and other table Product_Variation which contains (product_variation_id, product_id, description, gender, price)
Basically, I have a product. For each product, have N variations.
e.g
Product: title "I have no idea"
Product_Variation: description "T-Shirt", gender "Male", price "59.90"
What I need is select Product and Product_Variation showing only the product with the lowest price.
I don´t care if a product has t-shirt, jacket or anything else as variation. I just need to get the variation which has the lowest price.
My query is:
SELECT b.product_id, b.title, MIN(b.price) as price, b.gender
FROM (
SELECT p.product_id, p.title, MIN(pv.price) AS price, pv.gender
FROM products p
join product_variation pv ON pv.product_id = p.product_id
GROUP BY p.product_id, p.title, pv.price, pv.gender
) b
GROUP BY b.product_id, b.title, b.price, b.gender
Pls, see my example in SQL Fiddle
Thanks!
Since you're using SQL 2008 you can use ROW_NUMBER to find the row with the lowest price:
SELECT *
FROM products p
INNER JOIN
(SELECT
product_id,
Description,
Gender,
Price,
ROW_NUMBER() OVER(PARTITION BY product_id ORDER BY price) Row
FROM product_variation )
pv ON pv.product_id = p.product_id
AND Row = 1
If you have two variations with the same price you'll get one random row.
-- Solution #1
SELECT *
FROM
(
SELECT p.product_id, p.title,
pv.price, pv.gender,
ROW_NUMBER() OVER(PARTITION BY p.product_id ORDER BY pv.price) AS row_num
FROM products p
join product_variation pv ON pv.product_id = p.product_id
) src
WHERE src.row_num = 1
-- Solution #2
SELECT p.product_id, p.title,
x.price, x.gender
FROM products p
cross apply
(
SELECT TOP(1) pv.price, pv.gender
FROM product_variation pv
WHERE pv.product_id = p.product_id
ORDER BY pv.price
) x
You can use the rank() window function to order stuff within a partition (group), but giving equal items the same rank:
;With b as (
Select
p.product_id,
p.title,
pv.price,
pv.gender,
rank() over(partition by p.product_id order by pv.price) rk
From
products p
Inner Join
product_variation pv
On pv.product_id = p.product_id
)
Select
b.product_id,
b.title,
b.price,
b.gender
From
b
Where
rk = 1
If you only want one per product even if there are equal products, use row_number() instead of rank()
Example Fiddle

SQL Query for counting number of orders per customer and Total Dollar amount

I have two tables
Order with columns:
OrderID,OrderDate,CID,EmployeeID
And OrderItem with columns:
OrderID,ItemID,Quantity,SalePrice
I need to return the CustomerID(CID), number of orders per customer, and each customers total amount for all orders.
So far I have two separate queries. One gives me the count of customer orders....
SELECT CID, Count(Order.OrderID) AS TotalOrders
FROM [Order]
Where CID = CID
GROUP BY CID
Order BY Count(Order.OrderID) DESC;
And the other gives me the total sales. I'm having trouble combining them...
SELECT CID, Sum(OrderItem.Quantity*OrderItem.SalePrice) AS TotalDollarAmount
FROM OrderItem, [Order]
WHERE OrderItem.OrderID = [Order].OrderID
GROUP BY CID
I'm doing this in Access 2010.
You would use COUNT(DISTINCT ...) in other SQL engines:
SELECT CID,
Count(DISTINCT O.OrderID) AS TotalOrders,
Sum(OI.Quantity*OI.SalePrice) AS TotalDollarAmount
FROM [Order] O
INNER JOIN [OrderItem] OI
ON O.OrderID = OI.OrderID
GROUP BY CID
Order BY Count(DISTINCT O.OrderID) DESC
Which Access unfortunately does not support. Instead you can first get the Order dollar amounts and then join them before figuring the order counts:
SELECT CID,
COUNT(Orders.OrderID) AS TotalOrders,
SUM(OrderAmounts.DollarAmount) AS TotalDollarAmount
FROM [Orders]
INNER JOIN (SELECT OrderID, Sum(Quantity*SalePrice) AS DollarAmount
FROM OrderItems GROUP BY OrderID) AS OrderAmounts
ON Orders.OrderID = OrderAmounts.OrderID
GROUP BY CID
ORDER BY Count(Orders.OrderID) DESC
If you need to include Customers that have orders with no items (unusual but possible), change INNER JOIN to LEFT OUTER JOIN.
Create a query which uses your 2 existing queries as subqueriers, and join the 2 subqueries on CID. Define your ORDER BY in the parent query instead of in a subquery.
SELECT
sub1.CID,
sub1.TotalOrders,
sub2.TotalDollarAmount
FROM
(
SELECT
CID,
Count(Order.OrderID) AS TotalOrders
FROM [Order]
GROUP BY CID
) AS sub1
INNER JOIN
(
SELECT
CID,
Sum(OrderItem.Quantity*OrderItem.SalePrice)
AS TotalDollarAmount
FROM OrderItem INNER JOIN [Order]
ON OrderItem.OrderID = [Order].OrderID
GROUP BY CID
) AS sub2
ON sub1.CID = sub2.CID
ORDER BY sub1.TotalOrders DESC;