SQL-Server query to select last and previous information for multiple columns - sql

After looking in Stackoverflow I cant find a solution to this problem.
I'm using this query:
SELECT *
FROM(
SELECT DISTINCT *
FROM Table_01
ORDER BY ID, StartDate
UNION ALL(
SELECT DISTINCT * FROM Table_02
ORDER BY ID, StartDate
)
UNION ALL (...
) a ORDER BY a.ID, a.StartDate
I got something like this, for each ID i would like to keep the last and previous date and other columns, to record a history
+------+------------+-----------+-------+-------+
| ID | StartDate | EndDate | Value | rate |
+------+------------+-----------+-------+-------+
| 1 | 2018-06-29 |2018-10-22 | 15 | 77.2 |
| 1 | 2018-04-28 |2018-06-21 | 23 | 55.3 |
| 1 | 2018-02-24 |2018-04-15 | 41 | 44.3 |
| 1 | 2017-06-29 |2017-11-29 | 55 | 44.1 |
| 2 | 2018-07-29 |2018-11-22 | 15 | 106.1 |
| 2 | 2018-03-28 |2018-07-21 | 23 | 10.8 |
| 2 | 2017-12-28 |2018-03-28 | 22 | 11.0 |
| 3 | 2017-09-28 |2018-01-28 | 11 | 87.09 |
| 3 | 2017-06-27 |2018-09-28 | 58 | 100 |
| ... | ... | ... | ... | ... |
+------+------------+-----------+-------+--------+
And I would like to have the next table, to keep the previous information
+------+------------+-----------+------------+-----------+-------+--------+-------+--------+
| ID | StartDate | EndDate | StartDateP | EndDateP | Value | rate | ValueP| rateP |
+------+------------+------------+-----------+-----------+-------+--------+-------+--------+
| 1 | 2018-06-29 |2018-10-22 | 2018-04-28 |2018-06-21 | 15 | 77.2 | 23 | 55.3 |
| 2 | 2018-07-29 |2018-11-22 | 2018-03-28 |2018-07-21 | 15 | 106.1 | 23 | 10.8 |
| 3 | 2017-09-28 |2018-01-28 | 2017-06-27 |2018-09-28 | 11 | 87.09 | 58 | 100 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... |
+------+------------+-----------+------------+-----------+-------+--------+-------+--------+

If I understand you correctly you want the row with the latest start date combined with the row with the startdate just before that? This might do the trick
WITH results AS
(
SELECT *, ROW_NUMBER() OVER(PARTITION BY ID ORDER BY StartDate DESC) r
FROM (
-- start of your original query
SELECT DISTINCT *
FROM Table_01
ORDER BY ID, StartDate
UNION ALL
(
SELECT DISTINCT *
FROM Table_02
ORDER BY ID, StartDate
)
UNION ALL
(...) a
ORDER BY a.ID, a.StartDate
-- end of your original query
)
)
SELECT
r1.id, r1.startDate, r2.enddate,
r2.startDate startDateP, r2.enddate enddateP,
r1.value, r1.rate,
r2.value valueP, r2.rate rateP
FROM results r1
LEFT JOIN results r2 ON r2.id = r1.id AND r2.r = 2
WHERE r1.r = 1

Another option is using Row_Number() in concert with a conditional aggregation
Example
Select ID
,StartDate = max(case when RN=1 then StartDate end)
,EndDate = max(case when RN=1 then EndDate end)
,StartDateP = max(case when RN=2 then StartDate end)
,EndDateP = max(case when RN=2 then EndDate end)
,Value = max(case when RN=1 then Value end)
,Rate = max(case when RN=1 then Rate end)
,ValueP = max(case when RN=2 then Value end)
,RateP = max(case when RN=2 then Rate end)
From (
Select *
,RN = Row_Number() over (Partition By ID Order by EndDate Desc)
From YourTable
) A
Group By ID
Returns

Related

Difference in dates when actions are taken multiple times

I have the following table:
Table (History h)
| Source ID | Action | Created Date |
| 1 | Filing Rejected | 1/3/2023 |
| 2 | Filing Rejected | 1/4/2023 |
| 1 | Filing Resubmitted | 1/5/2023 |
| 3 | Filing Rejected | 1/5/2023 |
| 2 | Filing Resubmitted | 1/6/2023 |
| 1 | Filing Rejected | 1/7/2023 |
| 3 | Filing Resubmitted | 1/8/2023 |
| 1 | Filing Resubmitted | 1/9/2023 |
The results that I want are:
|Source ID | Rejected Date | Resubmitted Date | Difference |
| 1 | 1/3/2023 | 1/5/2023 | 2 |
| 1 | 1/7/2023 | 1/9/2023 | 2 |
| 2 | 1/4/2023 | 1/6/2023 | 2 |
| 3 | 1/5/2023 | 1/8/2023 | 3 |
My current query language is:
SELECT h1.Source_ID, min(CONVERT(varchar,h1.CREATED_DATE,101)) AS 'Rejected Date',
min(CONVERT(varchar,h2.Created_Date,101)) AS 'Resubmitted Date',
DATEDIFF(HOUR, h1.Created_Date, min(h2.Created_Date)) / 24 Difference
FROM History h1 INNER JOIN History h2
ON h2.Source_ID = h1.Source_ID AND h2.Created_Date > h1.Created_Date
WHERE (h1.Created_Date >= '2023-01-01 00:00:00.000' AND h1.Created_Date <= '2023-01-31 23:59:59.000')
AND ((h1.CHANGE_VALUE_TO = 'Filing Rejected' AND h2.CHANGE_VALUE_TO = 'Filing Resubmitted'))
GROUP BY h1.Source_ID, h1.Created_Date,h2.Created_Date
ORDER BY 'Rejected Date' ASC;
The results I get are:
|Source ID | Rejected Date | Resubmitted Date | Difference |
| 1 | 1/3/2023 | 1/5/2023 | 2 |
| 1 * | 1/3/2023 | 1/9/2023 | 6 |
| 1 | 1/7/2023 | 1/9/2023 | 2 |
| 2 | 1/4/2023 | 1/6/2023 | 2 |
| 3 | 1/5/2023 | 1/8/2023 | 3 |
So there is one row that is showing up that should not be. I have marked it with an asterisk.
I just want the difference from the first rejection to the first resubmission, the second rejection to the second rejection.
Any help, another idea on how to do it, anything really, is greatly appreciated.
If events always properly interleave, one approach uses row_number() and conditional aggregation:
select source_id,
max(case when action = 'Filing Rejected' then created_date end) rejected_date,
max(case when action = 'Filing Resubmitted' then created_date end) resubmitted_date
from (
select h.*,
row_number() over(partition by source_id, action order by created_date) rn
from history h
where created_date >= '2023-01-01' and created_date < '2023-02-01'
) h
group by source_id, rn
order by source_id, rn
This will not work if your data has consecutive rejections or resubmissions.
As for the date difference, we can add another layer so we don’t need to type the conditional expressions twice:
select h.*, datediff(day, rejected_date, resubmitted_date) diff_in_days
from (
select source_id,
max(case when action = 'Filing Rejected' then created_date end) rejected_date,
max(case when action = 'Filing Resubmitted' then created_date end) resubmitted_date
from (
select h.*,
row_number() over(partition by source_id, action order by created_date) rn
from history h
where created_date >= '2023-01-01' and created_date < '2023-02-01'
) h
group by source_id, rn
) h
order by source_id, rejected_date

SQL Show the previous value until the given value changes

I have a table ordered by ID with column Value.
| ID | Value |
| -------- | -------------- |
| 1 | 50 |
| 2 | 50 |
| 3 | 62 |
| 4 | 62 |
| 5 | 62 |
| 6 | 79 |
| 7 | 90 |
| 8 | 90 |
I would like to create another column Prev_Value that for each row of column Value takes the previous/preceding number that differs from the current row value, as in the table below.
Output table:
| ID | Value |Prev_Value |
| -------- | -------------- |---------------|
| 1 | 50 |NULL |
| 2 | 50 |NULL |
| 3 | 62 |50 |
| 4 | 62 |50 |
| 5 | 62 |50 |
| 6 | 79 |62 |
| 7 | 90 |79 |
| 8 | 90 |79 |
Should I use modified LAG() function, the CROSS APPLY or nested CASE and what approach would be the most time-efficient? Any help would be appreciated.
Here are some references that unfortunately does not solve my problem:
LAG(offset) until value is reached in BigQuery and
SQL Server : select distinct until the value is changed
One method uses apply:
select t.*, tprev.value
from t outer apply
(select top (1) tprev.*
from t tprev
where tprev.value <> t.value and
tprev.id < t.id
order by tprev.id desc
) tprev;
The above is not the most efficient method on a large dataset. For that, I would suggest getting the first time that a value changes and marking that.
select t.*,
max(case when prev_value <> value then prev_value end) over (partition by grp) as prev_value
from (select t.*,
sum(case when prev_value = value then 0 else 1 end) over (order by id) as grp
from (select t.*,
lag(value) over (order by id) as prev_value
from t
) t
) t;
Here is a db<>fiddle.
Here is one way to do it. (Check it live on this fiddle)
select
t1.*,
t2.value prev_value
from
t t1
left join t t2 on t2.id = (select max(id) from t where value<t1.value)

Get users who took ride for 3 or more consecutive dates

I have below table, it shows user_id and ride_date.
+---------+------------+
| user_id | ride_date |
+---------+------------+
| 1 | 2019-11-01 |
| 1 | 2019-11-03 |
| 1 | 2019-11-05 |
| 2 | 2019-11-03 |
| 2 | 2019-11-04 |
| 2 | 2019-11-05 |
| 2 | 2019-11-06 |
| 3 | 2019-11-03 |
| 3 | 2019-11-04 |
| 3 | 2019-11-05 |
| 3 | 2019-11-06 |
| 4 | 2019-11-05 |
| 4 | 2019-11-07 |
| 4 | 2019-11-08 |
| 4 | 2019-11-09 |
| 5 | 2019-11-11 |
| 5 | 2019-11-13 |
+---------+------------+
I want user_id who took rides for 3 or more consecutive days along with days on which they took consecutive rides
The desired result is as below
+---------+-----------------------+
| user_id | consecutive_ride_date |
+---------+-----------------------+
| 2 | 2019-11-03 |
| 2 | 2019-11-04 |
| 2 | 2019-11-05 |
| 2 | 2019-11-06 |
| 3 | 2019-11-03 |
| 3 | 2019-11-04 |
| 3 | 2019-11-05 |
| 3 | 2019-11-06 |
| 4 | 2019-11-08 |
| 4 | 2019-11-09 |
| 4 | 2019-11-10 |
+---------+-----------------------+
SQL Fiddle
With LAG() and LEAD() window functions:
with cte as (
select *,
datediff(
day,
lag([ride_date]) over (partition by [user_id] order by [ride_date]),
[ride_date]
) prev1,
datediff(
day,
lag([ride_date], 2) over (partition by [user_id] order by [ride_date]),
[ride_date]
) prev2,
datediff(
day,
[ride_date],
lead([ride_date]) over (partition by [user_id] order by [ride_date])
) next1,
datediff(
day,
[ride_date],
lead([ride_date], 2) over (partition by [user_id] order by [ride_date])
) next2
from Table1
)
select [user_id], [ride_date]
from cte
where
(prev1 = 1 and prev2 = 2) or
(prev1 = 1 and next1 = 1) or
(next1 = 1 and next2 = 2)
See the demo.
Results:
> user_id | ride_date
> ------: | :---------
> 2 | 03/11/2019
> 2 | 04/11/2019
> 2 | 05/11/2019
> 2 | 06/11/2019
> 3 | 03/11/2019
> 3 | 04/11/2019
> 3 | 05/11/2019
> 3 | 06/11/2019
> 4 | 07/11/2019
> 4 | 08/11/2019
> 4 | 09/11/2019
Here is one way to adress this gaps-and-island problem:
first, assign a rank to each user ride with row_number(), and recover the previous ride_date (aliased lag_ride_date)
then, compare the date of the previous ride to the current one in a conditional sum, that increases when the dates are successive ; by comparing this with the rank of the user ride, you get groups (aliased grp) that represent consecutive rides with a 1 day spacing
do a window count how many records belong to each group (aliased cnt)
filter on records whose window count is greater than 3
Query:
select user_id, ride_date
from (
select
t.*,
count(*) over(partition by user_id, grp) cnt
from (
select
t.*,
rn1
- sum(case when ride_date = dateadd(day, 1, lag_ride_date) then 1 else 0 end)
over(partition by user_id order by ride_date) grp
from (
select
t.*,
row_number() over(partition by user_id order by ride_date) rn1,
lag(ride_date) over(partition by user_id order by ride_date) lag_ride_date
from Table1 t
) t
) t
) t
where cnt >= 3
Demo on DB Fiddle
This is a typical gaps and island problems.
We can solve it as follows
with data
as (
select user_id
,ride_date
,dateadd(day
,-row_number() over(partition by user_id order by ride_date asc)
,ride_date) as grp_field
from Table1
)
,consecutive_days
as(
select user_id
,ride_date
,count(*) over(partition by user_id,grp_field) as cnt
from data
)
select *
from consecutive_days
where cnt>=3
order by user_id,ride_date
https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=7bb851d9a12966b54afb4d8b144f3d46
There is no need to apply gaps-and-islands methodologies to this problem. The problem is much simpler to solve.
You can return the users and first date just by using LEAD():
SELECT t1.*
FROM (SELECT t1.*,
LEAD(ride_date, 2) OVER (PARTITION BY user_id ORDER BY ride_date) as ride_date_2
FROM table1 t1
) t1
WHERE ride_date_2 = DATEADD(day, 2, ride_date);
If you want the actual dates, you can unpivot the results:
SELECT DISTINCT t1.user_id, v.ride_date
FROM (SELECT t1.*,
LEAD(ride_date, 2) OVER (PARTITION BY user_id ORDER BY ride_date) as ride_date_2
FROM table1 t1
) t1 CROSS APPLY
(VALUES (t1.ride_date),
(DATEADD(day, 1, t1.ride_date)),
(DATEADD(day, 2, t1.ride_date))
) v(ride_date)
WHERE t1.ride_date_2 = DATEADD(day, 2, t1.ride_date)
ORDER BY t1.user_id, v.ride_date;

Different aggregation fields in same query

I am trying to aggregate some fields in different way - using partition - in the same query but found a problem with AVG():
Take this definition:
CREATE TABLE Result
(CheckListId int, CheckId int, AuditId int, CheckListResult FLOAT, CheckResult FLOAT)
INSERT INTO Result VALUES (1,1,1,1,1)
INSERT INTO Result VALUES (1,2,1,1,3)
INSERT INTO Result VALUES (1,2,2,3,1)
INSERT INTO Result VALUES (2,1,1,3,1)
+-------------+---------+---------+-----------------+-------------+
| CheckListId | CheckId | AuditId | CheckListResult | CheckResult |
+-------------+---------+---------+-----------------+-------------+
| 1 | 1 | 1 | 1 | 1 |
| 1 | 2 | 1 | 1 | 3 |
| 1 | 2 | 2 | 3 | 1 |
| 2 | 1 | 1 | 3 | 1 |
+-------------+---------+---------+-----------------+-------------+
thanks to Format Text as Table for formatting
this is my select
SELECT
CheckListId
, CheckId
, (dense_rank() over (PARTITION BY CheckListId order by [AuditId])
+ dense_rank() over (PARTITION BY CheckListId order by [AuditId] desc)
- 1) AS N_AuditForCheckList
, AVG(CheckListResult) OVER(PARTITION BY CheckListId) AS AvgCheckListResult
, COUNT(AuditId) OVER (PARTITION BY CheckListId, CheckId) AS N_AuditForCheck
, AVG(CheckResult) OVER(PARTITION BY CheckListId, CheckId) AS AvgCheckResult
FROM Result
i get this result
+-------------+---------+---------------------+--------------------+-----------------+----------------+
| CheckListId | CheckId | N_AuditForCheckList | AvgCheckListResult | N_AuditForCheck | AvgCheckResult |
+-------------+---------+---------------------+--------------------+-----------------+----------------+
| 1 | 1 | 2 | 1,67 | 1 | 1 |
| 1 | 2 | 2 | 1,67 | 2 | 2 |
| 1 | 2 | 2 | 1,67 | 2 | 2 |
| 2 | 1 | 1 | 3 | 1 | 1 |
+-------------+---------+---------------------+--------------------+-----------------+----------------+
while in AvgCheckListResult i want 2 because on this checklist i have two results: 1 on first audit and 3 on second audit, while sql calculate avg of the 3 rows
Is there a way to do it without sub-query or joining many query?
p.s.
link to test it:
http://rextester.com/ZFEXOD67600
I didn't find (at moment) how to do without a sub query (but I think you already explored my following solution):
P.S. A +1 for the way you wrote your question (scripts, sample data, etc.)
SELECT *
, SUM(CheckListResult / C3 ) OVER (PARTITION BY CheckListId) / N_AuditForCheckList AS AvgChk3
FROM (
SELECT
CheckListId
, CheckId
, AuditId
, CheckListResult
, (dense_rank() over (PARTITION BY CheckListId order by [AuditId])
+ dense_rank() over (PARTITION BY CheckListId order by [AuditId] desc)
- 1) AS N_AuditForCheckList
, AVG(CheckListResult) OVER(PARTITION BY CheckListId) AS AvgCheckListResult
, AVG(CheckListResult) OVER(PARTITION BY CheckListId,AuditId) AS AvgCheckListResult2
, COUNT(*) OVER (PARTITION BY CheckListId, AuditId) AS C3
, COUNT(AuditId) OVER (PARTITION BY CheckListId, CheckId) AS N_AuditForCheck
, AVG(CheckResult) OVER(PARTITION BY CheckListId, CheckId) AS AvgCheckResult
FROM Result) A
Output:
+-------------+---------+---------+-----------------+---------------------+--------------------+---------------------+----+-----------------+----------------+---------+
| CheckListId | CheckId | AuditId | CheckListResult | N_AuditForCheckList | AvgCheckListResult | AvgCheckListResult2 | C3 | N_AuditForCheck | AvgCheckResult | AvgChk3 |
+-------------+---------+---------+-----------------+---------------------+--------------------+---------------------+----+-----------------+----------------+---------+
| 1 | 1 | 1 | 1 | 2 | 1,66666666666667 | 1 | 2 | 1 | 1 | 2 |
| 1 | 2 | 1 | 1 | 2 | 1,66666666666667 | 1 | 2 | 2 | 2 | 2 |
| 1 | 2 | 2 | 3 | 2 | 1,66666666666667 | 3 | 1 | 2 | 2 | 2 |
| 2 | 1 | 1 | 3 | 1 | 3 | 3 | 1 | 1 | 1 | 3 |
+-------------+---------+---------+-----------------+---------------------+--------------------+---------------------+----+-----------------+----------------+---------+
maybe i found a solution with nested query:
SELECT *
,(SELECT AVG(T.CheckListResult)
FROM
(SELECT DISTINCT AuditId, CheckListId, CheckListResult FROM Result) as T
WHERE T.CheckListId = Base.CheckListId
) AS AvgCheckListResult
,(SELECT COUNT(T.CheckListResult)
FROM
(SELECT DISTINCT AuditId, CheckListId, CheckListResult FROM Result) as T
WHERE T.CheckListId = Base.CheckListId
) AS N_AuditForCheckList
,(SELECT AVG(T.CheckResult)
FROM
(SELECT DISTINCT AuditId, CheckId, CheckResult FROM Result) as T
WHERE T.CheckId = Base.CheckId
) AS AvgCheckResult
FROM Result AS Base
result is correct but not sure about performance

Finding next row in SQL query and deleting it only if previous row matches

I have a table like this.
|-DT--------- |-ID------|
|5/30 12:00pm |10 |
|5/30 01:00pm |30 |
|5/30 02:30pm |30 |
|5/30 03:00pm |50 |
|5/30 04:30pm |10 |
|5/30 05:00pm |10 |
|5/30 06:30pm |10 |
|5/30 07:30pm |10 |
|5/30 08:00pm |50 |
|5/30 09:30pm |10 |
I want to remove any duplicate rows only if the previous row has the same ID as the following row. I want to keep the duplicate row with the datetime furthest in the future. For example the above table would look like this.
|-DT--------- |-ID------|
|5/30 12:00pm |10 |
|5/30 02:30pm |30 |
|5/30 03:00pm |50 |
|5/30 07:30pm |10 |
|5/30 08:00pm |50 |
|5/30 09:30pm |10 |
Can I get any tips on how this can be done?
with C as
(
select ID,
row_number() over(order by DT) as rn
from YourTable
)
delete C1
from C as C1
inner join C as C2
on C1.rn = C2.rn-1 and
C1.ID = C2.ID
SE-Data
Do these 3 steps: http://www.sqlfiddle.com/#!3/b58b9/19
First make the rows sequential:
with a as
(
select dt, id, row_number() over(order by dt) as rn
from tbl
)
select * from a;
Output:
| DT | ID | RN |
----------------------------------------
| May, 30 2012 12:00:00-0700 | 10 | 1 |
| May, 30 2012 13:00:00-0700 | 30 | 2 |
| May, 30 2012 14:30:00-0700 | 30 | 3 |
| May, 30 2012 15:00:00-0700 | 50 | 4 |
| May, 30 2012 16:30:00-0700 | 10 | 5 |
| May, 30 2012 17:00:00-0700 | 10 | 6 |
| May, 30 2012 18:30:00-0700 | 10 | 7 |
| May, 30 2012 19:30:00-0700 | 10 | 8 |
| May, 30 2012 20:00:00-0700 | 50 | 9 |
| May, 30 2012 21:30:00-0700 | 10 | 10 |
Second, using the sequential numbers, we can find which rows are at the bottom (and also those not at the bottom for that matter):
with a as
(
select dt, id, row_number() over(order by dt) as rn
from tbl
)
select below.*,
case when above.id <> below.id or above.id is null then
1
else
0
end as is_at_bottom
from a below
left join a above on above.rn + 1 = below.rn;
Output:
| DT | ID | RN | IS_AT_BOTTOM |
-------------------------------------------------------
| May, 30 2012 12:00:00-0700 | 10 | 1 | 1 |
| May, 30 2012 13:00:00-0700 | 30 | 2 | 1 |
| May, 30 2012 14:30:00-0700 | 30 | 3 | 0 |
| May, 30 2012 15:00:00-0700 | 50 | 4 | 1 |
| May, 30 2012 16:30:00-0700 | 10 | 5 | 1 |
| May, 30 2012 17:00:00-0700 | 10 | 6 | 0 |
| May, 30 2012 18:30:00-0700 | 10 | 7 | 0 |
| May, 30 2012 19:30:00-0700 | 10 | 8 | 0 |
| May, 30 2012 20:00:00-0700 | 50 | 9 | 1 |
| May, 30 2012 21:30:00-0700 | 10 | 10 | 1 |
Third, delete all rows not at the bottom:
with a as
(
select dt, id, row_number() over(order by dt) as rn
from tbl
)
,b as
(
select below.*,
case when above.id <> below.id or above.id is null then
1
else
0
end as is_at_bottom
from a below
left join a above on above.rn + 1 = below.rn
)
delete a
from a
inner join b on b.rn = a.rn
where b.is_at_bottom = 0;
To verify:
select * from tbl order by dt;
Output:
| DT | ID |
-----------------------------------
| May, 30 2012 12:00:00-0700 | 10 |
| May, 30 2012 13:00:00-0700 | 30 |
| May, 30 2012 15:00:00-0700 | 50 |
| May, 30 2012 16:30:00-0700 | 10 |
| May, 30 2012 20:00:00-0700 | 50 |
| May, 30 2012 21:30:00-0700 | 10 |
You can also simplify the deletion to this: http://www.sqlfiddle.com/#!3/b58b9/20
with a as
(
select dt, id, row_number() over(order by dt, id) as rn
from tbl
)
delete above
from a below
left join a above on above.rn + 1 = below.rn
where case when above.id <> below.id or above.id is null then 1 else 0 end = 0;
Mikael Eriksson's answer is the best though, if I simplify again my simplified query, it will look like his answer ツ For that, I +1'd his answer. I will just make his query a bit more readable though; by swapping the joining order and giving good aliases.
with a as
(
select *, row_number() over(order by dt, id) as rn
from tbl
)
delete above
from a below
join a above on above.rn + 1 = below.rn and above.id = below.id;
Live test: http://www.sqlfiddle.com/#!3/b58b9/24
Here you go, simply replace [Table] with the name of your table.
SELECT *
FROM [dbo].[Table]
WHERE [Ident] NOT IN
(
SELECT Extent.[Ident]
FROM
(
SELECT TOP 100 PERCENT T1.[DT],
T1.[ID],
T1.[Ident],
(
SELECT TOP 1 Previous.ID
FROM [dbo].[Table] AS Previous
WHERE Previous.[Ident] > T1.Ident -- this is where the identity seed is important
ORDER BY [Ident] ASC
) AS 'PreviousId'
FROM [dbo].[Table] AS T1
ORDER BY T1.[Ident] DESC
) AS Extent
WHERE [Id] = [PreviousId]
)
Note: You will need an indentity column on the table - use a CTE if you can't change the structure of the table.
You can try following Query ...
select * from
(
select *,RANK() OVER (ORDER BY dt,id) AS Rank from test
) as a
where 0 = (
select count(id) from (
select id, RANK() OVER (ORDER BY dt,id) AS Rank from test
)as b where b.id = a.id and b.Rank = a.Rank + 1
) order by dt
Thanks,
Mahesh