Get max value from another query - sql

I have problems with some query. I need to get max value and product_name from that query:
select
products.product_name,
sum(product_invoice.product_amount) as total_amount
from
product_invoice
inner join
products on product_invoice.product_id = products.product_id
inner join
invoices on product_invoice.invoice_id = invoices.invoice_id
where
month(invoices.invoice_date) = 2
group by
products.product_name
This query returns a result like this:
product_name | total_amount
--------------+--------------
chairs | 70
ladders | 500
tables | 150
How to get from this: ladders 500?

Select product_name,max(total_amount) from(
select
products.product_name,
sum(product_invoice.product_amount) as total_amount
from product_invoice
inner join products
on product_invoice.product_id = products.product_id
inner join invoices
on product_invoice.invoice_id = invoices.invoice_id
where month(invoices.invoice_date) = 2
group by products.product_name
) outputTable

You can use order by and fetch first 1 row only:
select p.product_name,
sum(pi.product_amount) as total_amount
from product_invoice pi inner join
products p
on pi.product_id = p.product_id inner join
invoices i
on pi.invoice_id = i.invoice_id
where month(i.invoice_date) = 2 -- shouldn't you have the year here too?
group by p.product_name
order by total_amount
fetch first 1 row only;
Not all databases support the ANSI-standard fetch first clause. You may need to use limit, select top, or some other construct.
Note that I have also introduced table aliases -- they make the query easier to write and to read. Also, if you are selecting the month, shouldn't you also be selecting the year?
In older versions of SQL Server, you would use select top 1:
select top (1) p.product_name,
sum(pi.product_amount) as total_amount
from product_invoice pi inner join
products p
on pi.product_id = p.product_id inner join
invoices i
on pi.invoice_id = i.invoice_id
where month(i.invoice_date) = 2 -- shouldn't you have the year here too?
group by p.product_name
order by total_amount;
To get all rows with the top amount, use SELECT TOP (1) WITH TIES . . ..

If you are using SQL Server, then TOP can offer a solution:
SELECT TOP 1
p.product_name,
SUM(pi.product_amount) AS total_amount
FROM product_invoice pi
INNER JOIN products p
ON pi.product_id = p.product_id
INNER JOIN invoices i
ON pi.invoice_id = i.invoice_id
WHERE
MONTH(i.invoice_date) = 2
GROUP BY
p.product_name
ORDER BY
SUM(pi.product_amount) DESC;
Note: If there could be more than one product tied for the top amount, and you want all ties, then use TOP 1 WITH TIES, e.g.
SELECT TOP 1 WITH TIES
... (the same query I have above)

Related

Using MS Access SQL - how would I select the top 5 food items by group (restaurant) based on the number of orders?

Relationship Diagram
What I want to be able to do is return a query that shows the top 5 items/products on the menu for each of the 3 restaurants in the dataset. I've attached an example of the relationship diagram for some more context. The columns I would like to see in the query are:
RestaurantName
ItemName
NumberofOrders (alias column)
This is what I have at the moment but it doesn't work as expected for the top 5.
SELECT RestaurantName, ItemName, COUNT(Orders.OrderNumber) AS NumberofOrders
FROM ((((Restaurants INNER JOIN
Orders ON Restaurants.RestID = Orders.RestID) INNER JOIN
OrderDetails ON Orders.OrderNumber=OrderDetails.OrderNumber) INNER JOIN
Products ON OrderDetails.ItemID = Products.ItemID) INNER JOIN
FoodType ON Products.ProdTypeID = FoodType.ProdType)
WHERE ItemName IN
(SELECT TOP 5 ItemName
FROM Products
WHERE ItemName IS NOT NULL)
GROUP BY RestaurantName, ItemName
ORDER BY COUNT(Orders.OrderNumber) DESC;
This just repeats the same 5 items across all the restaurants. Any point in the right direction would be awesome.
EDIT 1:
Based on a response I got yesterday, I have made some amendments to the code. This the query is returning the full list, as though ignoring the top 5 in the subquery. I can see all the items are sorted by Total Orders (I have also changed the formula for this) Any ideas what I am doing wrong here?
SELECT RestaurantName, ItemName, SUM(Quantity)*COUNT(Orders.OrderNumber) AS TotalOrders
FROM ((((Restaurants INNER JOIN
Orders ON Restaurants.RestID = Orders.RestID) INNER JOIN
OrderDetails ON Orders.OrderNumber=OrderDetails.OrderNumber) INNER JOIN
Products ON OrderDetails.ItemID = Products.ItemID) INNER JOIN
FoodType ON Products.ProdTypeID = FoodType.ProdType)
WHERE ItemName IN
(SELECT TOP 5 p2.ItemName
FROM Products AS p2
WHERE p2.ItemName = Products.ItemName
GROUP BY p2.ItemName
ORDER BY COUNT(*) DESC)
GROUP BY RestaurantName, ItemName
ORDER BY RestaurantName, SUM(Quantity) DESC;
Thanks
You want a correlated subquery:
WHERE ItemName IN (SELECT TOP 5 p2.ItemName
FROM Products as p2
WHERE p2.RestaurantName = products.RestaurantName
GROUP BY p2.ItemName
ORDER BY COUNT(*) DESC
)
It seems really odd to me that a table called products would have a column called RestaurantName. But you claim that your query works and it has the same reference.
Your filter in the outer WHERE is only using the ItemName field. For your purpose it should contain both fields.
Like so:
SELECT
RestaurantName,
ItemName,
COUNT(Orders.OrderNumber) AS NumberofOrders
FROM Restaurants
INNER JOIN Orders ON Restaurants.RestID = Orders.RestID
INNER JOIN OrderDetails ON Orders.OrderNumber=OrderDetails.OrderNumber
INNER JOIN Products ON OrderDetails.ItemID = Products.ItemID
INNER JOIN FoodType ON Products.ProdTypeID = FoodType.ProdType
WHERE (RestaurantName, ItemName) IN
(SELECT TOP 5 RestaurantName, ItemName
FROM Products
WHERE RestaurantName IS NOT NULL)
GROUP BY RestaurantName, ItemName
ORDER BY COUNT(Orders.OrderNumber) DESC;

Find average revenue by each subcategory of top 5 categories in terms of quantity sold in vba sql

I have two data set prod_cat_info and Transactions with two common fields for product category and sub category. I am trying to get average revenue by each subcategory of top 5 category in terms of quantity sold. I tried it from below query in which where clause is not working.
SELECT prod_cat_info.prod_subcat, AVG(Transactions.total_amt)
FROM Transactions INNER JOIN
prod_cat_info
ON Transactions.prod_cat_code = prod_cat_info.prod_cat_code AND
Transactions.prod_subcat_code = prod_cat_info.prod_sub_cat_code
GROUP BY prod_cat_info.prod_subcat
WHERE Transactions.prod_cat_code IN (
SELECT TOP 5 Transactions.prod_cat_code
FROM Transactions
GROUP BY Transactions.prod_cat_code
ORDER BY SUM(Transactions.Qty) DESC
)
GROUP BY prod_cat_info.prod_subcat
You don't need a subquery at all. Just use TOP and ORDER BY:
SELECT TOP (5) pci.prod_subcat, AVG(t.total_amt)
FROM Transactions t INNER JOIN
prod_cat_info pci
ON t.prod_cat_code = pci.prod_cat_code AND
t.prod_subcat_code = pci.prod_sub_cat_code
GROUP BY pci.prod_subcat
ORDER BY SUM(t.qty DESC);
EDIT:
In MS Access and for the clarified question:
SELECT pci.prod_cat_code, pci.prod_subcat, AVG(t.total_amt)
FROM Transactions as t INNER JOIN
prod_cat_info as pci
ON t.prod_cat_code = pci.prod_cat_code AND
t.prod_subcat_code = pci.prod_sub_cat_code
WHERE pci.prod_cat_code IN (
SELECT TOP 5 t.prod_cat_code
FROM Transactions as t
GROUP BY t.prod_cat_code
ORDER BY t.qty DESC
)
GROUP BY pci.prod_cat_code, pci.prod_subcat;

SQL strategy to fetch maximum

Suppose I have these three tables:
I want to get, for all products, it's product_id and the client that bougth it most times (the biggest client of the product).
I solved it like this:
SELECT
product_id AS product,
(SELECT TOP 1 client_id FROM Bill_Item, Bill
WHERE Bill_Item.product_id = p.product_id
and Bill_Item.bill_id = Bill.bill_id
GROUP BY
client_id
ORDER BY
COUNT(*) DESC
) AS client
FROM Product p
Do you know a better way?
the inner query will give you the ranking. The outer query will give you the client that puchase the most for a product
SELECT *
(
SELECT i.product_id, b.client_id,
r = row_number() over (partition by i.product_id
order by count(*) desc)
FROM Bill b
INNER JOIN Bill_Item i ON b.bill_id = i.bill_id
GROUP BY i.product_id, b.client_id
) d
WHERE r = 1
I was going to submit pretty much the same thing as #Squirrell only with a Common Table Expression [CTE] rather than a derived table. So I wont duplicate that but there are some learning points concerning your query. First is IMPLICIT JOINS such as FROM Bill_Item, Bill are really easy to have uintended consequences (one of many questions: Queries that implicit SQL joins can't do?) Next for the Calculated column you can actually do this in a OUTER APPLY or CROSS APPLY which is a very useful technique.
So you could re-write your method as follows:
SELECT *
FROM
Product p
OUTER APPLY (SELECT TOP 1 b.client_id
FROM
Bill_Item bi
INNER JOIN Bill b
ON bi.bill_id = b.bill_id
WHERE
bi.product_id = p.product_id
GROUP BY
b.client_id
ORDER BY
COUNT(*) DESC) c
And to show you how squirell's answer can still include products that have never been sold all you need to do is join Products and LEFT JOIN to other tables:
;WITH cte AS (
SELECT
p.product_id
,b.client_id
,ROW_NUMBER() OVER (PARTITION BY p.product_id ORDER BY COUNT(*) DESC) as RowNumber
FROM
Product p
LEFT JOIN Bill_Item bi
ON p.product_id = bi.product_id
LEFT JOIN Bill b
ON bi.bill_id = b.bill_id
GROUP BY
p.product_id
,b.client_id
)
SELECT *
FROM
cte
WHERE
RowNumber = 1
Techniques used in some of these that are useful.
CTE
APPLY (Outer & Cross)
Window Functions
Squirrel's answer doesn't return products that have never been sold. If you want to include those, then your approach is ok, although I would write the query as:
SELECT product_id as product,
(SELECT TOP 1 b.client_id
FROM Bill_Item bi JOIN
Bill b
ON bi.bill_id = b.bill_id
WHERE Bill_Item.product_id = p.product_id
GROUP BY client_id
ORDER BY COUNT(*) DESC
) as client
FROM Product p;
You can also express this using APPLY, but a correlated subquery is also fine.
Note the correct use of the explicit JOIN syntax.

SQL Join with SUM()

I have two Tables
Product Details(About Product)
Sale Order Details(What Price is sold, quantity of products sold per order).
I am trying to do Join on Table 1 and Table 2 which should give the all the product details and sum(Quantity), Sum(Price)
Problem Facing: There are some products in Table 1 which are never sold, and those rows are missing in the result set, but I want details for all the rows in Table 1 with rows of Products never purchased should be NULL or 'o'
Query I am Using:
select
P.*,
ISNULL((sum([Q.Quantity])),0),
ISNULL((sum([Q.Price])),0)
From Table1 P
Left Outer Join Table2 Q on P.Product_ID = Q.Product_ID
Please help me with any suggestions that would work for me
How about this one:
select
P.Product_ID,
ISNULL(sum([Q.Quantity]),0),
ISNULL(sum([Q.Price]),0)
From Table1 P
Left Outer Join Table2 Q
on P.Product_ID = Q.Product_ID
group by
P.Product_Id

SQL Grouping using left join

I have 2 tables with related data. One table is for products and the other price. In the price table one product may appear several times. How can I return the result by grouping.
Below is my Query but the output is not with Group
SELECT distinct
p.Product,
p.Qty,
MAX(pr.netprice)
FROM Products p
LEFT OUTER JOIN Price pr ON p.Product=pr.Product
WHERE p.brand=''
GROUP BY p.Product, p.Qty
You should probably leave the Qty out of the group by, like this:
SELECT p.Product,
MAX(pr.netprice)
FROM Products p
LEFT OUTER JOIN Price pr ON p.Product=pr.Product
WHERE p.brand=''
GROUP BY p.Product