Get top one record of difference date - sql

I try to get just one record of last Order of everysingle customer. ATM I have this idea but it giving me all of them Date of orders.
I work on dataBase AdventurerWorks2012
SELECT
H.CustomerID AS Customer,
H.SalesOrderID AS OrderNumber,
MAX(CAST(H.OrderDate AS DATE)) AS DateOrder
FROM
Sales.SalesOrderHeader H
JOIN
Sales.SalesOrderDetail D
ON H.SalesOrderID = D.SalesOrderID
GROUP BY
H.CustomerID,
H.SalesOrderID
ORDER BY
CustomerID;

You can use TOP 1 WITH TIES
select top 1 with ties
*
From
Sales.SalesOrderHeader H
INNER JOIN Sales.SalesOrderDetail D
ON H.SalesOrderID = D.SalesOrderID
ORDER BY row_number() over (partition by H.CustomerID order by H.OrderDate desc)
This applies and ordering with row_number() giving each customer's order an ID starting at 1 and going to N based off the OrderDate. The WITH TIES allows us to return the TOP 1 for each customer.
Another way, is using a CTE
;with cte as(
select top 1 with ties
*, RN = row_number() over (partition by H.CustomerID order by H.OrderDate desc)
From
Sales.SalesOrderHeader H
INNER JOIN Sales.SalesOrderDetail D
ON H.SalesOrderID = D.SalesOrderID)
select *
from cte
where RN = 1

I think you don't need to do JOIN:
SELECT h.*
FROM Sales.SalesOrderHeader h
WHERE OrderDate = (SELECT MAX(h1.OrderDate)
FROM Sales.SalesOrderHeader h1
WHERE h.CustomerID = h1.CustomerID
);
By this you will get customers with most recent order information.

select top 1 with ties
H.CustomerID AS Customer,
H.SalesOrderID as OrderNumber,
H.OrderDate As DataLast
From
Sales.SalesOrderHeader H
INNER JOIN Sales.SalesOrderDetail D
ON H.SalesOrderID = D.SalesOrderID
ORDER BY row_number() over (partition by H.CustomerID order by H.OrderDate desc)
This work like i need. Thanks!

Related

Not getting the result that I need by using ROW_NUMBER()

I'm using advantureworks2017 and what I'm trying to get is the top 2 selling products by year,, what I have so far is this but it's showing me only the top 2 rows which is not what I need, I need the top 2 products in each year.
SELECT TOP (2) ROW_NUMBER() OVER (ORDER BY sum(linetotal) DESC) ,
ProductID,
year(h.OrderDate) 'OrderYear'
from Sales.SalesOrderDetail so
left outer join Sales.SalesOrderHeader h
on so.SalesOrderID = h.SalesOrderID
group by year(h.OrderDate), ProductID
Try to add row_number in the subquery and then use that rank <= 2 in the outer query to select top 2
select
ProductID,
OrderYear
from
(
SELECT
ProductID,
year(h.OrderDate) 'OrderYear',
ROW_NUMBER() OVER (ORDER BY sum(linetotal) DESC) as rnk
from Sales.SalesOrderDetail so
left outer join Sales.SalesOrderHeader h
on so.SalesOrderID = h.SalesOrderID
group by year(h.OrderDate), ProductID
) val
where rnk <= 2
When you ORDER your ROW_NUMBER by sum(linetotal) it's goning to fail if you have multiple sum(linetotal) which are equal.
I prefer to do it that way:
Declare table(number of columns = number of your query results columns + 1)
fill first column in declared table with identity(1,1) and next insert query results into the rest columns.

Get the second last record in a date column within a inner join

I need to pull the second last record in a date column called OrderDate. However, I need to bring only one date (I am making the search into a table with all the purchases orders, dates and costs, in which a have to bring only the second last and its cost). The way its query is written today (and working) is pulling me the the newest date.
select distinct
a.PurchaseNum, a.ItemID, a.SupplierNum, a.Location, a.OrderDate, a.Cost
from
PurchaseOrder a
inner join
(select
l.SupplierNum, l.ItemID, l.Location, maxdate = max(l.OrderDate)
from
PurchaseOrder l
where
l.Cost <> 0
group by
l.SupplierNum, l.itemid, l.Location) l on a.SupplierNum = l.SupplierNumand a.itemid = l.itemid
and l.Location = a.Location
and a.OrderDate = l.maxdate
I have tried to use lag(), offset (but with limitations once is within a join, forcing me to use the order by and include the dateOrder column which is not what I want because we need only one date)
A bit of context: I have a report in which I need to show the last and second last cost of a purchase order for each supplier. Bring the last cost of an order is easy, the problem is go back to the second last... and it is where I am stuck right now.
Any thought?
If I'm understanding you correctly, here's one option using row_number to return the 2 highest orderdate records:
select *
from (
select *,
row_number() over (partition by SupplierNum, ItemID, Location
order by OrderDate desc) rn
from PurchaseOrder
where cost <> 0
) t
where rn <= 2
Inner query does order by desc and outside query does order by asc.
select distinct top 1 a.*
from PurchaseOrder a
inner join
(
select Top 2 l.*
from PurchaseOrder l
where
l.Cost <> 0
group by l.SupplierNum, l.itemid, l.Location order by orderdate desc) l
on a.SupplierNum= l.SupplierNumand a.itemid = l.itemid and l.Location=a.Location and a.OrderDate = l.Orderdate
order by a.orderdate
or
SELECT TOP 1 * FROM (SELECT * FROM PurchaseOrder a
EXCEPT SELECT TOP (SELECT (COUNT(*)-2) FROM PurchaseOrder a where
l.Cost <> 0
group by l.SupplierNum, l.itemid, l.Location) * FROM PurchaseOrder) A
or
SELECT *
FROM PurchaseOrder a
WHERE OrderDate = ( SELECT MAX(OrderDate)
FROM PurchaseOrder
WHERE Orderdate < ( SELECT MAX(OrderDate)
FROM PurchaseOrder l where
l.Cost <> 0
group by l.SupplierNum, l.itemid, l.Location
)
) ;
or
SELECT TOP (1) *
FROM PurchaseOrder
WHERE OrderDate < ( SELECT MAX(OrderDate)
FROM PurchaseOrder where ....
)
ORDER BY OrderDate DESC ;

Find product id of the top selling product of each day, using total sold quantity to determine the top selling product

Adventureworks2008R2 database.
The result I want is each day the MaxQantity sold, for example, OrderDate 2007-09-01 shall have the max quantity of 96 only, but my query gives me 3 different results from the same day, maybe because it is considering the timestamp as well
SELECT DISTINCT CAST(oh.OrderDate AS DATE) OrderDate, (od.ProductID),SUM(od.OrderQty) MAXOrderQty
FROM Sales.SalesOrderDetail od
Inner Join Sales.SalesOrderHeader oh
ON od.SalesOrderID = oh.SalesOrderID
GROUP BY od.ProductID, CAST(oh.OrderDate AS DATE), od.OrderQty
ORDER BY SUM(od.OrderQty) DESC
You can write CTE and self JOIN on MAX Qty by date
;WITH CTE(OrderDate,ProductID,MAXOrderQty) AS(
SELECT CAST(oh.OrderDate AS DATE) OrderDate,od.ProductID,SUM(od.OrderQty) MAXOrderQty
FROM Sales.SalesOrderDetail od
Inner Join Sales.SalesOrderHeader oh
ON od.SalesOrderID = oh.SalesOrderID
GROUP BY od.ProductID, CAST(oh.OrderDate AS DATE)
)
SELECT t1.*
FROM CTE t1 INNER JOIN (
select OrderDate,MAX(MAXOrderQty) 'MAXOrderQty'
from CTE
GROUP BY OrderDate
)t2 on t1.OrderDate = t2.OrderDate and t1.MAXOrderQty = t2.MAXOrderQty
It's much easier addressing problems like this with window functions. In this case, rank should do the trick:
SELECT OrderDate, ProductID, MaxOrderQty
FROM (SELECT OrderDate, ProductID, MaxOrderQty,
RANK() OVER (PARTITION BY OrderDate ORDER BY MaxOrderQty DESC) AS rk
FROM Sales.SalesOrderDetail) s
WHERE rk = 1

SQL values for min and max rows in one row

I have a table with price changes and I need to get initial price and latest price.
In other words I want to display price values for min(StartDate) and max(StartDate) in one row for each product.
Table structure is simple:
ProductID, StartDate, Price
Desired result is
ProductId, StartDate, InitialPrice, LatestDate, LatestPrice
WITH latestPrice AS
(
SELECT ProductID, StartDate, Price,
ROW_NUMBER() OVER (PArtition BY ProductID ORDER BY StartDate DESC) rn
FROM TableName
)
, initalPrice AS
(
SELECT ProductID, StartDate, Price,
ROW_NUMBER() OVER (PArtition BY ProductID ORDER BY StartDate ASC) rn
FROM TableName
)
SELECT a.ProductID,
b.StartDate,
b.Price InitalPrice,
c.StartDate LatestDate,
c.Price LatestPrice
FROM (SELECT DISTINCT ProductID FROM tableName) a
INNER JOIN initalPrice b
ON a.ProductID = b.ProductID AND b.rn = 1
INNER JOIN latestprice c
ON a.ProductID = c.ProductID AND c.rn = 1
SQLFiddle Demo
(SELECT x.ProductId, x.MinStartDate, x.MinPrice, y.MaxStartDate, y.MaxPrice FROM (SELECT a.ProductId, a.MinStartDate, b.Price AS MinPrice FROM (SELECT ProductId, MIN(StartDate) AS MinStartDate FROM Products GROUP BY ProductId) a INNER JOIN Products b ON a.ProductId = b.ProductId AND a.MinStartDate = b.StartDate) x INNER JOIN (SELECT a.ProductId, a.MaxStartDate, b.Price AS MaxPrice FROM (SELECT ProductId, MAX(StartDate) AS MaxStartDate FROM Products GROUP BY ProductId) a INNER JOIN Products b ON a.ProductId = b.ProductId AND a.MaxStartDate = b.StartDate) y ON x.ProductId = y.ProductId
DISCLAIMER: This is from memory so I apologize if this is not entirely accurate. And it assumes that the StartDate is a datetime or is not repeated for each productId.
Hope this helps.

SQL: improving join efficiency

If I turn this sub-query which selects sales persons and their highest price paid for any item they sell:
select *,
(select top 1 highestProductPrice
from orders o
where o.salespersonid = s.id
order by highestProductPrice desc ) as highestProductPrice
from salespersons s
in to this join in order to improve efficiency:
select *, highestProductPrice
from salespersons s join (
select salespersonid, highestProductPrice, row_number(
partition by salespersonid
order by salespersonid, highestProductPrice) as rank
from orders ) o on s.id = o.salespersonid
It still touches every order record (it enumerates the entire table before filtering by salespersonid it seems.) However you cannot do this:
select *, highestProductPrice
from salespersons s join (
select salespersonid, highestProductPrice, row_number(
partition by salespersonid
order by salespersonid, highestProductPrice) as rank
from orders
where orders.salepersonid = s.id) o on s.id = o.salespersonid
The where clause in the join causes a `multi-part identifier "s.id" could not be bound.
Is there any way to join the top 1 out of each order group with a join but without touching each record in orders?
Try
SELECT
S.*,
T.HighestProductPrice
FROM
SalesPersons S
CROSS APPLY
(
SELECT TOP 1 O.HighestProductPrice
FROM Orders O
WHERE O.SalesPersonid = S.Id
ORDER BY O.SalesPersonid, O.HighestProductPrice DESC
) T
would
select s.*, max(highestProductPrice)
from salespersons s
join orders o on o.salespersonid = s.id
group by s.*
or
select s.*, highestProductPrice
from salespersons s join (select salepersonid,
max(highestProductPrice) as highestProductPrice
from orders o) as o on o.salespersonid = s.id
work?