Conditional time to status calculation - sql

I am trying to calculate how long it takes a rep to have x amount of clients apply for service: meaning I need the time between date_created - ie. date the rep was onboarded, and when rep reaches a certain "status". Status is reached when x of the rep's clients (= users) have a non-null date_applied- ie. date user signed up.
x is minimum criteria to reach each "status", and ties back to a previous question: Aggregate case when inside non aggregate query where I am currently calculating "status" like so:
case when count(date_applied) over (partition by rep_id) >=10 then 'status1'
when count(date_applied) over (partition by rep_id) >=5 then 'status2'
when count(date_applied) over (partition by rep_id) >=1 then 'status3'
else 'no_status' end status
So it takes 10 clients to reach status1, 5 to reach status2 and 1 to reach status3. These are the criteria for each "status", so if you have 7 users for example, you still calculate status2 based on the date the 5th user applied.
I think calculating time_to_status1/2/3 (what i am trying to get at) should look something like this:
case when count(date_applied) over (partition by rep_id) >=10 then
datediff(day, date_created, date_applied for the 10th user that applied with that rep) end as time_to_status1,
case when count(date_applied) over (partition by rep_id) >=5 then
datediff(day, date_created, date_applied for the 5th user that applied with that rep) end as time_to_status2,
case when count(date_applied) over (partition by rep_id) >=1 then
datediff(day, date_created, date_applied for the 1st user that applied with that rep) end as time_to_status3
Any help is greatly appreciated!
--Edit--
Sample current data:
rep_id user_id date_created date_applied status
1 1 1/1/2018 6:43:22 AM 1/5/2018 2:45:15 PM status2
1 2 1/1/2018 6:43:22 AM 1/5/2018 3:35:15 PM status2
1 3 1/1/2018 6:43:22 AM 1/6/2018 4:25:15 PM status2
1 4 1/1/2018 6:43:22 AM 1/7/2018 5:05:15 PM status2
1 5 1/1/2018 6:43:22 AM 1/10/2018 3:35:15 PM status2
1 6 1/1/2018 6:43:22 AM 1/15/2018 12:55:23 PM status2
2 7 1/12/2018 1:13:42 PM 1/15/2018 4:25:15 PM status3
2 8 1/12/2018 1:13:42 PM 1/16/2018 1:05:15 PM status3
2 9 1/12/2018 1:13:42 PM 1/16/2018 3:35:15 PM status3
3 10 1/20/2018 10:13:15 AM 1/26/2018 7:25:15 PM status3
4 11 1/21/2018 3:33:23 PM (null) no_status
Desired output:
rep_id user_id date_created date_applied status time_to_status1 time_to_status2 time_to_status3
1 1 1/1/2018 6:43:22 AM 1/5/2018 2:45:15 PM status2 (null) 9 (null)
1 2 1/1/2018 6:43:22 AM 1/5/2018 3:35:15 PM status2 (null) 9 (null)
1 3 1/1/2018 6:43:22 AM 1/6/2018 4:25:15 PM status2 (null) 9 (null)
1 4 1/1/2018 6:43:22 AM 1/7/2018 5:05:15 PM status2 (null) 9 (null)
1 5 1/1/2018 6:43:22 AM 1/10/2018 3:35:15 PM status2 (null) 9 (null)
1 6 1/1/2018 6:43:22 AM 1/15/2018 12:55:23 PM status2 (null) 9 (null)
2 7 1/12/2018 1:13:42 PM 1/15/2018 4:25:15 PM status3 (null) (null) 3
2 8 1/12/2018 1:13:42 PM 1/16/2018 1:05:15 PM status3 (null) (null) 3
2 9 1/12/2018 1:13:42 PM 1/16/2018 3:35:15 PM status3 (null) (null) 3
3 10 1/20/2018 10:13:15 AM 1/26/2018 7:25:15 PM status3 (null) (null) 6
4 11 1/21/2018 3:33:23 PM (null) no_status (null) (null) (null)
rep_id=1 has status2 because he has 6 users with with a non null date_applied, so time_to_status2 in his case is based on date_applied of 5th client rep signed up: datediff(day, '1/1/2018 6:43:22 AM', '1/10/2018 3:35:15 PM') = 9 days
rep_id=2 has status3 because he has 3 users with a non null date_applied, so time_to_status3 in his case is based on date_applied of 1st client rep signed up: datediff(day, '1/12/2018 1:13:42 PM', '1/15/2018 4:25:15 PM') = 3 days
rep_id=3 has status3 because he has 1 (>=1) user with a non null date_applied, so time_to_status3 in his case is datediff(day, '1/20/2018 10:13:15 AM', '1/26/2018 7:25:15 PM') = 6 days

Based on #Parfait's deleted hint, and #Gordon's answer on a different question, I was able to come up with an answer:
with cte as
(
initial query with:
case when count(client_signup_date) over (partition by rep_id) >=10 then 'status1'
when count(client_signup_date) over (partition by rep_id) >=5 then 'status2'
when count(client_signup_date) over (partition by rep_id) >=1 then 'status3'
else 'none' end status,
row_number() over(partition by rep_id order by client_signup_date) as rank
)
select *,
max(case when status = 'status1' and rank = 10
then datediff(day, advisor_onboard_date, client_signup_date)
end) over (partition by rep_id) as time_to_status1,
max(case when status = 'status2' and rank = 5
then datediff(day, advisor_onboard_date, client_signup_date)
end) over (partition by rep_id) as time_to_status2,
max(case when status = 'status3' and rank = 1
then datediff(day, advisor_onboard_date, client_signup_date)
end) over (partition by rep_id) as time_to_status3
into #t
from cte

Related

Add a counting condition into dense_rank window Function SQL

I have a function that counts how many times you've visited and if you have converted or not.
What I'd like is for the dense_rank to re-start the count, if there has been a conversion:
SELECT
uid,
channel,
time,
conversion,
dense_rank() OVER (PARTITION BY uid ORDER BY time asc) as visit_order
FROM table
current table output:
this customer (uid) had a conversion at visit 18 and now I would want the visit_order count from dense_rank to restart at 0 for the same customer until it hits the next conversion that is non-null.
See this (I do not like "try this" 😉):
SELECT
id,
ts,
conversion,
-- SC,
ROW_NUMBER() OVER (PARTITION BY id,SC) R
FROM (
SELECT
id,
ts,
conversion,
-- COUNT(conversion) OVER (PARTITION BY id, conversion=0 ORDER BY ts ) CC,
SUM(CASE WHEN conversion=1 THEN 1000 ELSE 1 END) OVER (PARTITION BY id ORDER BY ts ) - SUM(CASE WHEN conversion=1 THEN 1000 ELSE 1 END) OVER (PARTITION BY id ORDER BY ts )%1000 SC
FROM sample
ORDER BY ts
) x
ORDER BY ts;
DBFIDDLE
output:
id
ts
conversion
R
1
2022-01-15 10:00:00
0
1
1
2022-01-16 10:00:00
0
2
1
2022-01-17 10:00:00
0
3
1
2022-01-18 10:00:00
1
1
1
2022-01-19 10:00:00
0
2
1
2022-01-20 10:00:00
0
3
1
2022-01-21 10:00:00
0
4
1
2022-01-22 10:00:00
0
5
1
2022-01-23 10:00:00
0
6
1
2022-01-24 10:00:00
0
7
1
2022-01-25 10:00:00
1
1
1
2022-01-26 10:00:00
0
2
1
2022-01-27 10:00:00
0
3

If Column from Group of Rows Contains 'x' Return 'x' for All Related Rows in New column

I have the table below and I want to create a new column which looks at the [Registration Date] and the [Person ID] and checks all the values in [Registration Status], if the [Person ID] has 1 or more ‘Present’ or 1 or more ‘Not Registered’ within a day then they are present for the whole day, otherwise they are absent.
As an example (Highlighted red in image below), look at the 7th Feb 2022 where Person ID = 103. He has 4 rows of ‘Not Registered’ resulting in 'Present' for the day:
The granularity of the rows needs to stay the same so Group By wouldn’t work in this instance.
Thank you in advance.
Register ID Person ID Registration Date Registration Time Registration Status Desired Output
1 100 07-Feb-22 07:30:00 Present Present
2 100 07-Feb-22 08:00:00 Not Registered Present
2 100 07-Feb-22 08:55:00 Absent Present
2 100 07-Feb-22 10:10:00 Present Present
3 101 07-Feb-22 07:30:00 Not Registered Present
3 101 07-Feb-22 07:40:00 Absent Present
3 101 07-Feb-22 08:25:00 Absent Present
3 101 07-Feb-22 09:10:00 Present Present
5 102 07-Feb-22 07:30:00 Absent Absent
5 102 07-Feb-22 12:00:00 Absent Absent
2 103 07-Feb-22 07:30:00 Not Registered Present
2 103 07-Feb-22 07:40:00 Not Registered Present
2 103 07-Feb-22 08:25:00 Not Registered Present
2 103 07-Feb-22 09:10:00 Not Registered Present
6 100 08-Feb-22 07:30:00 Absent Absent
7 100 08-Feb-22 08:00:00 Absent Absent
8 101 09-Feb-22 07:30:00 Absent Absent
9 101 09-Feb-22 07:40:00 Absent Absent
10 101 09-Feb-22 08:25:00 Absent Absent
11 101 09-Feb-22 09:10:00 Absent Absent
select *, case when count(case when Registration_Status= 'Present' then 1 end) OVER (PARTITION BY person_id, cast(Registration_Date as date)) >= 1
or count(case when Registration_Status= 'Not Registered' then 1 end) OVER (PARTITION BY person_id, cast(Registration_Date as date)) >= 1
then 'Present'
else 'Absent'
end
from table
You can use CASE expression for that.
For your example:
SELECT [Register ID],[Person ID],[Registration Date],[Registration Time],[Registration Status]
CASE
WHEN COUNT(CASE WHEN [Registration Status]= 'Present' THEN 1 END) OVER (PARTITION BY [Person ID], [Registration Date]) > 0 THEN 'Present'
WHEN COUNT(CASE WHEN [Registration Status]= 'Not Registered' THEN 1 END) OVER (PARTITION BY [Person ID], [Registration Date]) > 0 THEN 'Present'
ELSE 'Absent'
END AS [Desired Output]
FROM YourTable;
should work.
The final answer was a combination of Erin and Domni0's solutions.
SELECT [registration date],
[person id],
CASE
WHEN Count(CASE
WHEN [registrationstatus] = 'Present'
OR [registrationstatus] = 'Not Registered' THEN 1
END)
OVER (
partition BY [person id], [registration date]) > 0 THEN
'Present'
ELSE 'Absent'
END AS [Desired Outcome] FROM Table
Thank you both for your help.

case when.. then.. where

I have the following piece of code:
case when status=status2 and rank=5 then datediff(day, rep_onboard_date, client_signup_date) end as time_to_status2
where:
rank= row_number() over(partition by rep_id order by client_signup_date)
and:
status2= case when count(client_signup_date) over (partition by rep_id) >=5.
This takes the time difference between rep_onboard_date and client_signup_date of his 5th client.
This works fine however only the 5th row per rep is populated, while the rest are (null).
What I would like is that if any row is populated for the rep with time_to_status then all rows should carry the same value.
Simplified query:
with cte as (
select rep_id, rep_onboard_date, user_id, client_signup_date, /* a bunch of other fields,*/
count(client_signup_date) over (partition by rep_id) as total_applicants,
case when count(client_signup_date) over (partition by rep_id) >=10 then 'status1'
when count(client_signup_date) over (partition by rep_id) >=5 then 'status2'
when count(client_signup_date) over (partition by rep_id) >=1 then 'status3'
else 'none' end status,
row_number() over(partition by rep_id order by client_signup_date) as rank,
from table1 r
left join table2 u on r.user_id=u.user_id
left join table3 pi on u.user_id=pi.user_id
)
select *,
case when status='status1' and rank=10 then datediff(day, advisor_onboard_date, client_signup_date) end as time_to_status1,
case when status='status2' and rank=5 then datediff(day, advisor_onboard_date, client_signup_date) end as time_to_status2,
case when status='status3' and rank=1 then datediff(day, advisor_onboard_date, client_signup_date) end as time_to_status3
from cte
Current output:
rep_id user_id rep_onboard_date client_signup_date status rank time_to_status
1 1 1/1/2018 1/5/2018 status2 1 (null)
1 2 1/1/2018 1/5/2018 status2 2 (null)
1 3 1/1/2018 1/6/2018 status2 3 (null)
1 4 1/1/2018 1/7/2018 status2 4 (null)
1 5 1/1/2018 1/10/2018 status2 5 9
1 6 1/1/2018 1/15/2018 status2 6 (null)
Expected output:
rep_id user_id rep_onboard_date client_signup_date status rank time_to_status
1 1 1/1/2018 1/5/2018 status2 1 9
1 2 1/1/2018 1/5/2018 status2 2 9
1 3 1/1/2018 1/6/2018 status2 3 9
1 4 1/1/2018 1/7/2018 status2 4 9
1 5 1/1/2018 1/10/2018 status2 5 9
1 6 1/1/2018 1/15/2018 status2 6 9
I believe what you want are window functions:
select cte.*,
max(case when status = 'status1' and rank = 10
then datediff(day, advisor_onboard_date, client_signup_date)
end) over (partition by rep_id) as time_to_status1
from cte;
It seems you don't want a CASE, you want a subquery. Something like this:
SELECT col1,
col2,
(SELECT datediff(day, rep_onboard_date, client_signup_date)
FROM yourTable t2
WHERE t2.rep_id = t.rep_id
AND ((t.rank = 10 AND t.status = 'status')
OR (t.rank = 5 AND t.status = 'status2')
OR (t.rank = 1 AND t.status = 'status3'))) as time_to_status
FROM (yourTable or derivedTable with rank function) t

Sql group by latest repeated field

I don't even know what's a good title for this question.
But I'm having a table:
create table trans
(
[transid] INT IDENTITY (1, 1) NOT NULL,
[customerid] int not null,
[points] decimal(10,2) not null,
[date] datetime not null
)
and records:
--cus1
INSERT INTO trans ( customerid , points , date )
VALUES ( 1, 10, '2016-01-01' ) , ( 1, 20, '2017-02-01' ) , ( 1, 22, '2017-03-01' ) ,
( 1, 24, '2018-02-01' ) , ( 1, 50, '2018-02-25' ) , ( 2, 44, '2016-02-01' ) ,
( 2, 20, '2017-02-01' ) , ( 2, 32, '2017-03-01' ) , ( 2, 15, '2018-02-01' ) ,
( 2, 10, '2018-02-25' ) , ( 3, 10, '2018-02-25' ) , ( 4, 44, '2015-02-01' ) ,
( 4, 20, '2015-03-01' ) , ( 4, 32, '2016-04-01' ) , ( 4, 15, '2016-05-01' ) ,
( 4, 10, '2017-02-25' ) , ( 4, 10, '2018-02-27' ) ,( 4, 20, '2018-02-28' ) ,
( 5, 44, '2015-02-01' ) , ( 5, 20, '2015-03-01' ) , ( 5, 32, '2016-04-01' ) ,
( 5, 15, '2016-05-01' ) ,( 5, 10, '2017-02-25' );
-- selecting the data
select * from trans
Produces:
transid customerid points date
----------- ----------- --------------------------------------- -----------------------
1 1 10.00 2016-01-01 00:00:00.000
2 1 20.00 2017-02-01 00:00:00.000
3 1 22.00 2017-03-01 00:00:00.000
4 1 24.00 2018-02-01 00:00:00.000
5 1 50.00 2018-02-25 00:00:00.000
6 2 44.00 2016-02-01 00:00:00.000
7 2 20.00 2017-02-01 00:00:00.000
8 2 32.00 2017-03-01 00:00:00.000
9 2 15.00 2018-02-01 00:00:00.000
10 2 10.00 2018-02-25 00:00:00.000
11 3 10.00 2018-02-25 00:00:00.000
12 4 44.00 2015-02-01 00:00:00.000
13 4 20.00 2015-03-01 00:00:00.000
14 4 32.00 2016-04-01 00:00:00.000
15 4 15.00 2016-05-01 00:00:00.000
16 4 10.00 2017-02-25 00:00:00.000
17 4 10.00 2018-02-27 00:00:00.000
18 4 20.00 2018-02-28 00:00:00.000
19 5 44.00 2015-02-01 00:00:00.000
20 5 20.00 2015-03-01 00:00:00.000
21 5 32.00 2016-04-01 00:00:00.000
22 5 15.00 2016-05-01 00:00:00.000
23 5 10.00 2017-02-25 00:00:00.000
I'm trying to group all the customerid and sum their points. But here's the catch, If the trans is not active for 1 year(the next tran is 1 year and above), the points will be expired.
For this case:
Points for each customers should be:
Customer1 20+22+24+50
Customer2 20+32+15+10
Customer3 10
Customer4 10+20
Customer5 0
Here's what I have so far:
select
t1.transid as transid1,
t1.customerid as customerid1,
t1.date as date1,
t1.points as points1,
t1.rank1 as rank1,
t2.transid as transid2,
t2.customerid as customerid2,
t2.points as points2,
isnull(t2.date,getUTCDate()) as date2,
isnull(t2.rank2,t1.rank1+1) as rank2,
cast(case when(t1.date > dateadd(year,-1,isnull(t2.date,getUTCDate()))) Then 0 ELSE 1 END as bit) as ShouldExpire
from
(
select transid,CustomerID,Date,points,
RANK() OVER(PARTITION BY CustomerID ORDER BY date ASC) AS RANK1
from trans
)t1
left join
(
select transid,CustomerID,Date,points,
RANK() OVER(PARTITION BY CustomerID ORDER BY date ASC) AS RANK2
from trans
)t2 on t1.RANK1=t2.RANK2-1
and t1.customerid=t2.customerid
which gives
from the above table,how do I check for ShouldExpire field having max(rank1) for customer, if it's 1, then totalpoints will be 0, otherwise,sum all the consecutive 0's until there are no more records or a 1 is met?
Or is there a better approach to this problem?
The following query uses LEAD to get the date of the next record withing the same CustomerID slice:
;WITH CTE AS (
SELECT transid, CustomerID, [Date], points,
LEAD([Date]) OVER (PARTITION BY CustomerID
ORDER BY date ASC) AS nextDate,
CASE
WHEN [date] > DATEADD(YEAR,
-1,
-- same LEAD() here as above
ISNULL(LEAD([Date]) OVER (PARTITION BY CustomerID
ORDER BY date ASC),
getUTCDate()))
THEN 0
ELSE 1
END AS ShouldExpire
FROM trans
)
SELECT transid, CustomerID, [Date], points, nextDate, ShouldExpire
FROM CTE
ORDER BY CustomerID, [Date]
Output:
transid CustomerID Date points nextDate ShouldExpire
-------------------------------------------------------------
1 1 2016-01-01 10.00 2017-02-01 1 <-- last exp. for 1
2 1 2017-02-01 20.00 2017-03-01 0
3 1 2017-03-01 22.00 2018-02-01 0
4 1 2018-02-01 24.00 2018-02-25 0
5 1 2018-02-25 50.00 NULL 0
6 2 2016-02-01 44.00 2017-02-01 1 <-- last exp. for 2
7 2 2017-02-01 20.00 2017-03-01 0
8 2 2017-03-01 32.00 2018-02-01 0
9 2 2018-02-01 15.00 2018-02-25 0
10 2 2018-02-25 10.00 NULL 0
11 3 2018-02-25 10.00 NULL 0 <-- no exp. for 3
12 4 2015-02-01 44.00 2015-03-01 0
13 4 2015-03-01 20.00 2016-04-01 1
14 4 2016-04-01 32.00 2016-05-01 0
15 4 2016-05-01 15.00 2017-02-25 0
16 4 2017-02-25 10.00 2018-02-27 1 <-- last exp. for 4
17 4 2018-02-27 10.00 2018-02-28 0
18 4 2018-02-28 20.00 NULL 0
19 5 2015-02-01 44.00 2015-03-01 0
20 5 2015-03-01 20.00 2016-04-01 1
21 5 2016-04-01 32.00 2016-05-01 0
22 5 2016-05-01 15.00 2017-02-25 0
23 5 2017-02-25 10.00 NULL 1 <-- last exp. for 5
Now, you seem to want to calculate the sum of points after the last expiration.
Using the above CTE as a basis you can achieve the required result with:
;WITH CTE AS (
... above query here ...
)
SELECT CustomerID,
SUM(CASE WHEN rnk = 0 THEN points ELSE 0 END) AS sumOfPoints
FROM (
SELECT transid, CustomerID, [Date], points, nextDate, ShouldExpire,
SUM(ShouldExpire) OVER (PARTITION BY CustomerID ORDER BY [Date] DESC) AS rnk
FROM CTE
) AS t
GROUP BY CustomerID
Output:
CustomerID sumOfPoints
-----------------------
1 116.00
2 77.00
3 10.00
4 30.00
5 0.00
Demo here
The tricky part here is to dump all points when they expire, and start accumulating them again. I assumed that if there was only one transaction that we don't expire the points until there's a new transaction, even if that first transaction was over a year ago now?
I also get a different answer for customer #5, as they do appear to have a "transaction chain" that hasn't expired?
Here's my query:
WITH ordered AS (
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY customerid ORDER BY [date]) AS order_id
FROM
trans),
max_transid AS (
SELECT
customerid,
MAX(transid) AS max_transid
FROM
trans
GROUP BY
customerid),
not_expired AS (
SELECT
t1.customerid,
t1.points,
t1.[date] AS t1_date,
CASE
WHEN m.customerid IS NOT NULL THEN GETDATE()
ELSE t2.[date]
END AS t2_date
FROM
ordered t1
LEFT JOIN ordered t2 ON t2.customerid = t1.customerid AND t1.transid != t2.transid AND t2.order_id = t1.order_id + 1 AND t1.[date] > DATEADD(YEAR, -1, t2.[date])
LEFT JOIN max_transid m ON m.customerid = t1.customerid AND m.max_transid = t1.transid
),
max_not_expired AS (
SELECT
customerid,
MAX(t1_date) AS max_expired
FROM
not_expired
WHERE
t2_date IS NULL
GROUP BY
customerid)
SELECT
n.customerid,
SUM(n.points) AS points
FROM
not_expired n
LEFT JOIN max_not_expired m ON m.customerid = n.customerid
WHERE
ISNULL(m.max_expired, '19000101') < n.t1_date
GROUP BY
n.customerid;
It could be refactored to be simpler, but I wanted to show the steps to get to the final answer:
customerid points
1 116.00
2 77.00
3 10.00
4 30.00
5 57.00
can you try this:
SELECT customerid,
Sum(t1.points)
FROM trans t1
WHERE NOT EXISTS (SELECT 1
FROM trans t2
WHERE Datediff(year, t1.date, t2.date) >= 1)
GROUP BY t1.customerid
Hope it helps!
try this:
select customerid,Sum(points)
from trans where Datediff(year, date, GETDATE()) < 1
group by customerid
output:
customerid Points
1 - 74.00
2 - 25.00
3 - 10.00
4 - 30.00

How to calculate time duration using Microsoft SQL?

I want to find the time duration for each person from one start time. I want to calculate the time duration from 1 start time for each day and multiple end times for multiple users. This is my code:
SELECT *,
CAST(DATEDIFF(n, CAST(End_Time AS datetime),
CAST(Start_Time AS datetime)) AS FLOAT) / 60 AS Time_Duration
FROM
( SELECT NAME,
MAX(CASE WHEN DESCRIPTION = 'Green' THEN Final_Value END) AS Start_Time,
MAX(CASE WHEN DESCRIPTION = 'Red' THEN Final_Value END) AS End_Time
FROM mydata
WHERE NAME != ‘NA’
GROUP BY NAME
) C
I am not able to get any results for time duration.
This is what my output looks like:
Name Start_time End_time Time_Duration
1 Day_1 5/6/15 2:30
2 John 5/6/15 3:30
3 Ben 5/6/15 4:30
4 Mike 5/6/15 5:30
5 Day_2 5/7/15 2:30
6 John_2 5/7/15 4:30
7 Ben_2 5/7/15 5:30
8 Mike_2 5/7/15 6:30
I want it to look like this:
Name Start_time End_time Time_Duration
1 Day_1 5/6/15 2:30
2 John 5/6/15 3:30 1.00
3 Ben 5/6/15 4:30 2.00
4 Mike 5/6/15 5:30 3.00
5 Day_2 5/7/15 2:30
6 John_2 5/7/15 4:30 2.00
7 Ben_2 5/7/15 5:30 3.00
8 Mike_2 5/7/15 6:30 4.00
Assuming that the values in name column has suffix of the day number (and none for day 1)
WITH td AS
(
SELECT *,
ROW_NUMBER() OVER (PARTITION BY [day] ORDER BY final_value) rnum
FROM (SELECT *,
CASE WHEN CHARINDEX('_', name) = 0
THEN '1'
ELSE SUBSTRING(name, CHARINDEX('_', name) + 1, LEN(name) - CHARINDEX('_', name))
END [day]
FROM t_dur
) tt
)
SELECT t1.name,
CASE WHEN rnum = 1 THEN t1.final_value END start_time,
CASE WHEN rnum <> 1 THEN t1.final_value END end_time,
CASE CAST(DATEDIFF(hour, (SELECT t2.final_value FROM td t2 WHERE t2.[day] = t1.[day] AND t2.rnum = 1),
t1.final_value) AS DECIMAl(5,2))
WHEN 0 THEN NULL
ELSE CAST(DATEDIFF(hour, (SELECT t2.final_value FROM td t2 WHERE t2.[day] = t1.[day] AND t2.rnum = 1),
t1.final_value) AS DECIMAl(5,2))
END time_duration
FROM td t1
Result
name start_time end_time time_duration
Day_1 2015-05-06 02:30:00.000 NULL NULL
John NULL 2015-05-06 03:30:00.000 1.00
Ben NULL 2015-05-06 04:30:00.000 2.00
Mike NULL 2015-05-06 05:30:00.000 3.00
Day_2 2015-05-07 02:30:00.000 NULL NULL
John_2 NULL 2015-05-07 04:30:00.000 2.00
Ben_2 NULL 2015-05-07 05:30:00.000 3.00
Mike_2 NULL 2015-05-07 06:30:00.000 4.00