SQL Server : SELECT Highest Price and add qty's from table - sql

I have the following table TableAllProds:
ProdName ManuPartNo Price Qty Supplier
--------------------------------------------------
Part1 R10001 100.00 2 Supp1
Part2 R10002 500.00 2 Supp2
Part3 R30023 50.00 1 Supp3
Part2again R10002 100.00 5 Supp4
Part2Again R10002 300.00 10 Supp5
Part1again R10001 200.00 5 Supp3
I have a select statement to bring me back the highest price which works fine if there are duplicate products from different suppliers.
SELECT
ProdName, ManuPartNo, Price, Qty, Supplier
FROM
(SELECT
dbo.TableAllProds.*,
ROW_NUMBER() OVER (PARTITION BY ManuPartNo ORDER BY Price ASC) AS RN
FROM
dbo.TableAllProds) AS t
WHERE
RN = 1
ORDER BY
ManuPartNo
However I would also like to total all of the qty's for all suppliers Example for ManuPartNo - R10001 I would Like to return R10001 - 200.00 - 7(qty) and the supplier of the highest Price if possible.
Not sure how to google this, I can either return the highest/Lowest price easily and also return a sum of the qty for each part but am not sure about how to perform both queries at once.
Thanks for any help.

You can use SUM as a windowed function:
SELECT ProdName, ManuPartNo, Price, Qty, TotalQty, Supplier
FROM ( SELECT *,
ROW_NUMBER() OVER(PARTITION BY ManuPartNo ORDER BY Price ASC) AS RN,
SUM(Qty) OVER(PARTITION BY ManuPartNo) AS TotalQty,
FROM dbo.TableAllProds) AS t
WHERE RN = 1
ORDER BY ManuPartNo;

This seems to be what you want... uncomment the where clause if you only want that supplier.
declare #TableAllProds table (ProdName varchar(16), ManuPartNo varchar(16), Price decimal (5,2), Qty int, Supplier varchar(16))
insert into #TableAllProds
values
('Part1','R10001',100.00,2,'Supp1'),
('Part2','R10002',500.00,2,'Supp2'),
('Part3','R30023',50.00,1,'Supp3'),
('Part2again','R10002',100.00,5,'Supp4'),
('Part2Again','R10002',300.00,10,'Supp5'),
('Part1again','R10001',200.00,5,'Supp3')
;WITH CTE AS(
SELECT
ProdName,
ManuPartNo,
Price,
Supplier,
sum(Qty) over (partition by ManuPartNo) TotalOverAllSuppliers,
case when Price = max(price) over (partition by ManuPartNo) then Supplier end HighestPricedSupplier
FROM
#TableAllProds)
select
*
from cte
--where HighestPricedSupplier is not null

SELECT a.ManuPartNo, a.Price, a.QTY, b.Supplier
FROM (SELECT t1.ManuPartNo, MAX(t1.Price) AS Price, SUM(t1.Qty) AS QTY
FROM dbo.alltableprods t1
GROUP BY t1.ManuPartNo) a
JOIN (SELECT t2.ManuPartNo, t2.price, T2.Supplier,
ROW_NUMBER() OVER (PARTITION BY t2.ManuPartNo ORDER BY t2.price desc)
AS RN
FROM dbo.alltableprods t2
GROUP BY t2.ManuPartNo, t2.Price, t2.Supplier) b ON a.ManuPartNo =
b.ManuPartNo
WHERE b.RN = 1
Using this will return
R10001 200.00 7 Supp3
R10002 500.00 17 Supp2
R30023 50.00 1 Supp3
I have a question though. Is it possible for there to be more than one supplier that has the same part at the same price? If so then this will still work however it will just grab whatever applicable supplier it finds first.

You can query using row_number as below:
Select * from (
Select *, RowN = Row_Number() over(Partition by ManuPartNo order by Price desc), SmQty = Sum(Qty) over(Partition by ManuPartNo) from dbo.TableAllProds ) a
where a.RowN = 1

Related

Getting the lastest entry grouped by ID

I have a table with stock for products. The problem is that every time there is a stock change, the new value is stored, together with the new Quantity. Example:
ProductID | Quantity | LastUpdate
1 123 2019.01.01
2 234 2019.01.01
1 444 2019.01.02
2 222 2019.01.02
I therefore need to get the latest stock update for every Product and return this:
ProductID | Quantity
1 444
2 222
The following SQL works, but is slow.
SELECT ProductID, Quantity
FROM (
SELECT ProductID, Quantity
FROM Stock
WHERE LastUpdate
IN (SELECT MAX(LastUpdate) FROM Stock GROUP BY ProductID)
)
Since the query is slow and supposed to be left joined into another query, I really would like some input on how to do this better.
Is there another way?
Use analytic functions. row_number can be used in this case.
SELECT ProductID, Quantity
FROM (SELECT ProductID, Quantity, row_number() over(partition by ProductID order by LstUpdte desc) as rnum
FROM Stock
) s
WHERE RNUM = 1
Or with first_value.
SELECT DISTINCT ProductID, FIRST_VALUE(Quantity) OVER(partition by ProductID order by LstUpdte desc) as quantuity
FROM Stock
Just another option is using WITH TIES in concert with Row_Number()
Full Disclosure: Vamsi's answer will be a nudge more performant.
Example
Select Top 1 with ties *
From YourTable
Order by Row_Number() over (Partition By ProductID Order by LastUpdate Desc)
Returns
ProductID Quantity LastUpdate
1 444 2019-01-02
2 222 2019-01-02
So you Could use a CTE(Common Table Expression)
Base Data:
SELECT 1 AS ProductID
,123 AS Quantity
,'2019-01-01' as LastUpdate
INTO #table
UNION
SELECT 2 AS ProductID
,234 AS Quantity
,'2019-01-01' as LastUpdate
UNION
SELECT 1 AS ProductID
,444 AS Quantity
,'2019-01-02' as LastUpdate
UNION
SELECT 2 AS ProductID
,222 AS Quantity
,'2019-01-02' as LastUpdate
Here is the code using a Common Table Expression.
WITH CTE (ProductID, Quantity, LastUpdate, Rnk)
AS
(
SELECT ProductID
,Quantity
,LastUpdate
,ROW_NUMBER() OVER(PARTITION BY ProductID ORDER BY LastUpdate DESC) AS Rnk
FROM #table
)
SELECT ProductID, Quantity, LastUpdate
FROM CTE
WHERE rnk = 1
Returns
You could then Join the CTE to whatever table you need.
row_number() function might be the most efficient, but the big slow down in your query is the use of the IN statement when used on a subquery, it's a little bit of a tricky one but a join is faster. This query should get what you want and be much faster.
SELECT
a.ProductID
,a.Quantity
FROM stock as a
INNER JOIN (
SELECT
ProductID
,MAX(LastUpdate) as LastUpdate
FROM stock
GROUP BY ProductID
) b
ON a.ProductID = b.ProductId AND
a.LastUpdate = b.LastUpdate

Use CTE and UNION ALL with SQL Server 2014

My problem is that:
Create a view that shows the top 5 selling products as well as an aggregated row that shows the total sales for all other products and a Grand total row that sums all of the above.
WITH ProductTop5 AS
(
SELECT [dbo].[Product].[ProductName] AS ProductName, SUM([dbo].[SalesOrderDetail].[LineTotal]) AS TotalAmount
FROM [dbo].[Product]
JOIN [dbo].[SalesOrderDetail] ON [dbo].[Product].[ProductID] = [dbo].[SalesOrderDetail].[ProductID]
GROUP BY [dbo].[Product].[ProductName]
)
You could use ROW_NUMBER/RANK to calculate ranking of product:
WITH Product AS
(
SELECT p.[ProductName] AS ProductName,
SUM(sod.[LineTotal]) AS TotalAmount
FROM [dbo].[Product] p
JOIN [dbo].[SalesOrderDetail] sod
ON p.[ProductID] = sod.[ProductID]
GROUP BY p.[ProductName]
), ProductWithRank AS (
SELECT ProductName, Total_Amount,
ROW_NUMBER() OVER(ORDER BY Total_Amount DESC) AS rn
FROM Product
)
SELECT ProductName, TotalAmount
FROM ProductWithRank
WHERE rn <= 5
UNION ALL
SELECT 'All Others', SUM(Total_Amount)
FROM ProductWithRank
WHERE rn > 5
UNION ALL
SELECT 'Grand Total', SUM(TotalAmount)
FROM ProductWithRank;

Query for the top 20 item wise record of every customer

I have a table sales invoice, there are itemno, custno, and quantity, date. i want top 3 record of every customer with item details in Descending Order (top sales sum(quantity)).
means let say
c1 i1 sum(quantity)
c1 i2 sum(quantity)
c1 i3 sum(quantity)
c2 i2 sum(quantity)
c2 i5 sum(quantity)
c2 i3 sum(quantity)
and so on.let me know if anymore clarification required
For SQL Server, using common table expressions CTE
;WITH SumQty AS -- The data format we wish to display
(
SELECT CustNo, ItemNo, SUM(Quantity) AS SumQty
FROM SalesInvoice
GROUP BY CustNo, ItemNo
),
Seq AS -- The grouping of customer order by qty of items in descending order
(
SELECT
ROW_NUMBER() OVER (PARTITION BY CustNo ORDER BY SumQty DESC) AS Seq,
*
FROM SumQty
)
SELECT
CustNo,
ItemNo,
SumQty
FROM Seq
WHERE Seq <= 3 -- Interested on top 3 for each customer only
You may try something like this
SELECT *
FROM
(SELECT s.custid, s.itemno, sum(s.quantity) as quantity_new
FROM Salesinvoice AS s
GROUP BY s.custid, s.itemno)
GROUP BY custid, itemno
HAVING COUNT(custid) <= 3
ORDER BY quantity_new DESC;

Get most expensive and cheapest items from two tables

I'm trying to get the most expensive and cheapest items from two different tables.
The output should be one row with the values for MostExpensiveItem, MostExpensivePrice, CheapestItem, CheapestPrice
I was able to get the price of the most expensive and cheapest items in the two tables with following query:
SELECT
MAX(ExtrasPrice) as MostExpensivePrice, MIN(ExtrasPrice) as CheapestPrice
FROM
(
SELECT ExtrasPrice FROM Extras
UNION ALL
SELECT ItemPrice FROM Items
) foo
How can I add the names of the items (ItemName, ExtrasName) to my output? Again, there should only be one row as the output.
Try this:
SELECT TOP 1 FIRST_VALUE(Price) OVER (ORDER BY Price) AS MinPrice,
FIRST_VALUE(Name) OVER (ORDER BY Price) AS MinName,
LAST_VALUE(Price) OVER (ORDER BY Price DESC) AS MaxPrice,
LAST_VALUE(Name) OVER (ORDER BY Price DESC) AS MaxName
FROM (
SELECT ExtrasName AS Name, ExtrasPrice AS Price FROM Extras
UNION ALL
SELECT ItemName As Name, ItemPrice AS Price FROM Items) u
SQL Fiddle Demo
TOP 1 with order by clause should work for you. Try this
SELECT *
FROM (SELECT TOP 1 ExtrasPrice,ExtrasName
FROM Extras ORDER BY ExtrasPrice Asc),
(SELECT TOP 1 ItemPrice,ItemName
FROM Items ORDER BY ItemPrice Desc)
Note: Comma can be replaced with CROSS JOIN
You can use row_number() for this. If you are satisfied with two rows:
SELECT item, price
FROM (SELECT foo.*, row_number() over (order by price) as seqnum_asc,
row_number() over (order by price) as seqnum_desc
FROM (SELECT item, ExtrasPrice as price FROM Extras
UNION ALL
SELECT item, ItemPrice FROM Items
) foo
) t
WHERE seqnum_asc = 1 or seqnum_desc = 1;
EDIT:
If you have an index on "price" in both tables, then the cheapest method is probably:
with exp as (
(select top 1 item, ExtrasPrice as price
from Extras e
order by price desc
) union all
(select top 1 i.item, ItemPrice
from Items i
order by price desc
)
),
cheap as (
(select top 1 item, ExtrasPrice as price
from Extras e
order by price asc
) union all
(select top 1 i.item, ItemPrice
from Items i
order by price asc
)
)
select top 1 *
from exp
order by price desc
union all
select top 1 *
from cheap
order by price asc;
If you want this in one row, you can replace the final query with:
select e.*, c.*
from (select top 1 *
from exp
order by price desc
) e cross join
(select top 1 *
from cheap
order by price asc
) c

SQL latest record per group with an aggregated column

I have a table similar to this:
STOCK_ID TRADE_TIME PRICE VOLUME
123 1 5 100
123 2 6 150
456 1 7 200
456 2 8 250
For each stock I want to get latest price (where latest is just the max trade_time) and aggregated volume, so for the above table I want to see:
123 6 250
456 8 450
I've just discovered that the current query doesn't (always) work, ie there's no guarantee that the price selected is always the latest:
select stock_id, price, sum(volume) group by stock_id
Is this possible to do without subqueries? Thanks!
As you didn't specify the database you are using Here is some generic SQL that will do what you want.
SELECT
b.stock_id,
b.trade_time,
b.price,
a.sumVolume
FROM (SELECT
stock_id,
max(trade_time) AS maxtime,
sum(volume) as sumVolume
FROM stocktable
GROUP BY stock_id) a
INNER JOIN stocktable b
ON b.stock_id = a.stock_id and b.trade_time = a.maxtime
In SQL Server 2005 and up, you could use a CTE (Common Table Expression) to get what you're looking for:
;WITH MaxStocks AS
(
SELECT
stock_id, price, tradetime, volume,
ROW_NUMBER() OVER(PARTITION BY stock_ID ORDER BY TradeTime DESC) 'RowNo'
FROM
#stocks
)
SELECT
m.StockID, m.Price,
(SELECT SUM(VOLUME)
FROM maxStocks m2
WHERE m2.STock_ID = m.Stock_ID) AS 'TotalVolume'
FROM maxStocks m
WHERE rowno = 1
Since you want both the last trade as well as the volume of all trades for each stock, I don't see how you could do this totally without subqueries, however....
declare #Stock table(STOCK_ID int,TRADE_TIME int,PRICE int,VOLUME int)
insert into #Stock values(123,1,5,100),(123,2,6,150),(456,1,7,200),(456,2,8,250)
Select Stock_ID,Price,(Select sum(Volume) from #Stock B where B.Stock_ID=A.Stock_ID)Volume from #Stock A
where A.Trade_Time=(Select max(Trade_Time) from #Stock)
select a.stock_id, b.price , sum(a.volume) from tablename a
join (select stock_id, max(trade_time), price from tablename
group by stock_id) b
on a.stock_id = b.stock_id
group by stock_id