Sort result when using ROW_NUMBER() - sql

This is a follow-up to this question I asked yesterday.
I did not know if I should expand my original question, I decided to start a new one (bear with me...)
My SELECT is like this at the moment:
SELECT *
FROM
(
SELECT
a.guid_column1, b.guidID_column1, c.date_column1, d.guid_column1
ROW_NUMBER() OVER (PARTITION BY a.guid_column1, b.guid_column1 ORDER BY c.date_column1 DESC) as rn
...
-- JOINS AND WHERE STUFF HERE
....
) t
WHERE t.rn = 1
I get the (expected) result like this:
a.guid_column1 b.guid_column1 c.date_column1 d.guid_column1
-------------------------------------------------------------------
a1 b1 07/08/2013 someUniqueID
a2 b2 05/06/2012 someUniqueID
The tricky part is that I would like to sort that result by a dateadd, something like this ORDER BY dateadd(month, a.float_column, c.date_column1) asca.float_column is of course not always the same (and is in fact entered by the user later on).
Is there a way to accomplish this in SQL (I'm using SQL Server 2005)

Below query will calculate the new date in the SELECT-CLAUSE.
If a.float_column is NULL nothing will be added to the original date.
SELECT dateadd(month, ISNULL(t.float_column,0), t.date_column1) as newDate
, *
FROM
(
SELECT
a.guid_column1, b.guidID_column1, c.date_column1, d.guid_column1, a.float_column
ROW_NUMBER() OVER (PARTITION BY a.guid_column1, b.guid_column1 ORDER BY c.date_column1 DESC) as rn
...
-- JOINS AND WHERE STUFF HERE
....
) t
WHERE t.rn = 1
ORDER BY newDate ASC

why don't you just add column into your subquery?
select *
from
(
select
a.guid_column1, b.guidID_column1, c.date_column1, d.guid_column1,
dateadd(month, a.float_column, c.date_column1) as sort_order,
row_number() over (partition by a.guid_column1, b.guid_column1 order by c.date_column1 desc) as rn
--
-- JOINS AND WHERE STUFF HERE
--
) t
where t.rn = 1
order by t.sort_order asc

Related

Get Earliest Date corresponding to the latest occurrence of a recurring name

I have a table with Name and Date columns. I want to get the earliest date when the current name appeared. For example:
Name
Date
X
30-Jan-2021
X
29-Jan-2021
X
28-Jan-2021
Y
27-Jan-2021
Y
26-Jan-2021
Y
25-Jan-2021
Y
24-Jan-2021
X
23-Jan-2021
X
22-Jan-2021
Now when I try to get the earliest date when current name (X) started to appear, I want 28-Jan, but the sql query would give 22-Jan-2021 because that's when X appeared originally for the first time.
Update: This was the query I was using:
Select min(Date) from myTable where Name='X'
I am using older SQL Server 2008 (in the process of upgrading), so do not have access to LEAD/LAG functions.
The solutions suggested below do work as intended. Thanks.
This is a type of gaps-and-islands problem.
There are many solutions. Here is one that is optimized for your case
Use LEAD/LAG to identify the first row in each grouping
Filter to only those rows
Number them rows and take the first one
WITH StartPoints AS (
SELECT *,
IsStart = CASE WHEN Name <> LEAD(Name, 1, '') OVER (ORDER BY Date DESC) THEN 1 END
FROM YourTable
),
Numbered AS (
SELECT *,
rn = ROW_NUMBER() OVER (PARTITION BY Name ORDER BY Date DESC)
FROM StartPoints
WHERE IsStart = 1 AND Name = 'X'
)
SELECT
Name, Date
FROM Numbered
WHERE rn = 1;
db<>fiddle
For SQL Server 2008 or earlier (which I strongly suggest you upgrade from), you can use a self-join with row-numbering to simulate LEAD/LAG
WITH RowNumbered AS (
SELECT *,
AllRn = ROW_NUMBER() OVER (ORDER BY Date ASC)
FROM YourTable
),
StartPoints AS (
SELECT r1.*,
IsStart = CASE WHEN r1.Name <> ISNULL(r2.Name, '') THEN 1 END
FROM RowNumbered r1
LEFT JOIN RowNumbered r2 ON r2.AllRn = r1.AllRn - 1
),
Numbered AS (
SELECT *,
rn = ROW_NUMBER() OVER (PARTITION BY Name ORDER BY Date DESC)
FROM StartPoints
WHERE IsStart = 1
)
SELECT
Name, Date
FROM Numbered
WHERE rn = 1;
This is a gaps and island problem. Based on the sample data, this will work:
WITH Groups AS(
SELECT YT.[Name],
YT.[Date],
ROW_NUMBER() OVER (ORDER BY YT.Date DESC) -
ROW_NUMBER() OVER (PARTITION BY YT.[Name] ORDER BY Date DESC) AS Grp
FROM dbo.YourTable YT),
FirstGroup AS(
SELECT TOP (1) WITH TIES
G.[Name],
G.[Date]
FROM Groups G
WHERE [Name] = 'X'
ORDER BY Grp ASC)
SELECT MIN(FG.[Date]) AS Mi
db<>fiddle
If i did understand, you want to know when the X disappeared and reappeared again. in that case you can search for gaps in dates by group.
this and example how to detect that
SELECT name
,DATE
FROM (
SELECT *
,DATEDIFF(day, lead(DATE) OVER (
PARTITION BY name ORDER BY DATE DESC
), DATE) DIF
FROM YourTable
) a
WHERE DIF > 1

SQL Earliest hour for every day

I have table like this
And I want to have only earliest time from column time for each day from column date. Rest of table has to be unaffected.
So result would be that for example that I have only time 9:25 for 2018-07-13 and rest of rows with later times for 2018-07-13 are deleted
To delete you can use a CTE with ROW_NUMBER window function
;WITH cteDups
AS(
SELECT *, RN=ROW_NUMBER()OVER (PARTITION BY M.Date ORDER BY M.Time ASC)
FROM dbo.yourtable M
)
--SELECT *
DELETE
FROM cteDups D WHERE D.RN > 1
You can use a window function to return all rows
select
*,
min([time]) over (partition by [date] order by [time])
from YourTable
Or just the aggregate to remove them
select *
from YourTable
inner join
(select whatever, min(FullDate) dt
from yourtable
group by whatever) x on x.whatever = YourTable.whatever and x.dt = YourTable.FullDate
If the whatever column doesn't matter, and you only want the date and time:
Select
[date],
min([time])
from YourTable
group by [Date]
The simplest way to keep certain records and remove the rest would be by using a CTE with a windowing function to rank (or add rownumbers). Check this out:
;WITH EarliestHourEveryDay AS (
SELECT
whatever
,FullDate
,[date]
,[time]
,rn = ROW_NUMBER() OVER (PARTITION BY [date] ORDER BY [time])
FROM TableName
)
SELECT *
FROM EarliestHourEveryDay
WHERE rn = 1
/*
DELETE FROM EarliestHourEveryDay
WHERE rn > 1
*/
I have commented out the delete statement so that you can test this first. Run the CTE as-is, and if the result set contains the exact rows which you want, remove the SELECT statement from the CTE and uncomment the DELETE statement and you'll be good to go.
Group by day and select the MIN time.
Use the MIN function and a GROUP BY clause.
Something like:
SELECT date, MIN(time) AS EarliestTime
FROM MyTable
GROUP BY date
ORDER BY date ASC
Here is an example of this working: SQL Fiddle

SQL query to get maximum value for each day

So I have a table that looks something like this:
Now, I want the max totalcst for both days, something like this:
I tried using different variations of max and the Row_number funtion but still can't seem to get the result I want. My query:
select date,pid,max(quan*cst), totalcst
from dbo.test1
group by date, pid
But this returns all the records. So if somebody can point me towards the right direction, that would be very helpful.
Thanks in advance!
ROW_NUMBER should work just fine:
WITH CTE AS
(
SELECT *,
RN = ROW_NUMBER() OVER(PARTITION BY [date] ORDER BY totalcst)
FROM dbo.YourTable
)
SELECT [date],
pid,
totalcst
FROM CTE
WHERE RN = 1
;
Here is one simple way:
select t.*
from test1 t
where t.totalcst = (select max(t2.totalcst) from test1 t2 where t2.date = t.date);
This often has the best performance if you have an index on (date, totalcst). Of course, row_number()/rank() is also a very acceptable solution:
select t.*
from (select t.*, row_number() over (partition by date order by totalcst desc) as seqnum
from test1
) t
where seqnum = 1;

Filter the table with latest date having duplicate OrderId

I have following table:
I need to filter out the rows for which start date is latest corresponding to its order id .With reference to given table row no 2 and 3 should be the output.
As row 1 and row 2 has same order id and order date but start date is later than first row. And same goes with row number 3 and 4 hence I need to take out row no 3 . I am trying to write the query in SQL server. Any help is appreciated.Please let me know if you need more details.Apologies for poor English
You can do this easily with a ROW_NUMBER() windowed function:
;With Cte As
(
Select *,
Row_Number() Over (Partition By OrderId Order By StartDate Desc) RN
From YourTable
)
Select *
From Cte
Where RN = 1
But I question the StartDate datatype. It looks like these are being stored as VARCHAR. If that is the case, you need to CONVERT the value to a DATETIME:
;With Cte As
(
Select *,
Row_Number() Over (Partition By OrderId
Order By Convert(DateTime, StartDate) Desc) RN
From YourTable
)
Select *
From Cte
Where RN = 1
Another way using a derived table.
select
t.*
from
YourTable t
inner join
(select OrderId, max(StartDate) dt
from YourTable
group by OrderId) t2 on t2.dt = t.StartDate and t2.OrderId = t.OrderId

How to get the row that holds the last value in a queue of identical values? (SQL)

I think it's easier to show you an image:
So, for each fld_call_id, go to the next value, if it's identical. When we get to the last value, I need the value in column fld_menu_id.
Or, to put it in another way, eliminate fld_call_id duplicates and save only the last one.
You can use ROW_NUMBER:
WITH CTE AS(
SELECT RN = ROW_NUMBER() OVER (PARTITION BY fld_call_id ORDER BY fld_id DESC),
fld_menu_id
FROM dbo.TableName
)
SELECT fld_menu_id FROM CTE WHERE RN = 1
You can create a Rank column and only select that row, something along the lines of the following:
;WITH cte AS
(
SELECT
*
,RANK() OVER (PARTITION BY fld_call_id ORDER BY fld_id DESC) Rnk
FROM YourTable
)
SELECT
*
FROM cte
WHERE Rnk=1
So you GROUP BY fld_call_id and ORDER BY fld_id in descending order so that the last value comes first. These are the rows where Rnk=1.
Edit after comments of OP.
SELECT Table.*
FROM Table
INNER JOIN
(
SELECT MAX(fldMenuID) AS fldMenuID,
fldCallID
FROM Table
GROUP BY fldCallID
) maxValues
ON (maxValues.fldMenuID = Table.fldMenuID
AND maxValues.fldCallID= Table.fldCallID)
Hope This works
SELECT A.*
FROM table A
JOIN (SELECT fld_id,
ROW_NUMBER() OVER (PARTITION BY Fld_call_id ORDER BY fld_id DESC) [Row]
FROM table) LU ON A.fld_id = LU.fld_id
WHERE LU.[Row] = 1