Creating Start/Stop date ranges dataset with multiple starts for one stop
I am trying to create a dataset with all Starts to the nearest Stop.
The problem is that number of starts between the 1st Start and the next Stop varies.
The source dataset looks like this:
RowNum Timestamp Action
==============================
1 01/01/18 12:00 Start
2 01/01/18 01:00 Start
3 01/01/18 02:00 Stop
4 01/01/18 03:00 Start
5 01/01/18 05:00 Stop
6 01/01/18 13:00 Start
7 01/01/18 15:00 Start
8 01/01/18 17:00 Start
9 01/01/18 21:00 Stop
I want my final outcome to be something like this:
Start Stop
================================
01/01/18 12:00 01/01/18 02:00
01/01/18 03:00 01/01/18 05:00
01/01/18 13:00 01/01/18 21:00
Or even if it's there is a record for every Start to the nearest Stop that would be great as well.
Thank you so much for any guidance.
sql tsql
add a comment |
I am trying to create a dataset with all Starts to the nearest Stop.
The problem is that number of starts between the 1st Start and the next Stop varies.
The source dataset looks like this:
RowNum Timestamp Action
==============================
1 01/01/18 12:00 Start
2 01/01/18 01:00 Start
3 01/01/18 02:00 Stop
4 01/01/18 03:00 Start
5 01/01/18 05:00 Stop
6 01/01/18 13:00 Start
7 01/01/18 15:00 Start
8 01/01/18 17:00 Start
9 01/01/18 21:00 Stop
I want my final outcome to be something like this:
Start Stop
================================
01/01/18 12:00 01/01/18 02:00
01/01/18 03:00 01/01/18 05:00
01/01/18 13:00 01/01/18 21:00
Or even if it's there is a record for every Start to the nearest Stop that would be great as well.
Thank you so much for any guidance.
sql tsql
add a comment |
I am trying to create a dataset with all Starts to the nearest Stop.
The problem is that number of starts between the 1st Start and the next Stop varies.
The source dataset looks like this:
RowNum Timestamp Action
==============================
1 01/01/18 12:00 Start
2 01/01/18 01:00 Start
3 01/01/18 02:00 Stop
4 01/01/18 03:00 Start
5 01/01/18 05:00 Stop
6 01/01/18 13:00 Start
7 01/01/18 15:00 Start
8 01/01/18 17:00 Start
9 01/01/18 21:00 Stop
I want my final outcome to be something like this:
Start Stop
================================
01/01/18 12:00 01/01/18 02:00
01/01/18 03:00 01/01/18 05:00
01/01/18 13:00 01/01/18 21:00
Or even if it's there is a record for every Start to the nearest Stop that would be great as well.
Thank you so much for any guidance.
sql tsql
I am trying to create a dataset with all Starts to the nearest Stop.
The problem is that number of starts between the 1st Start and the next Stop varies.
The source dataset looks like this:
RowNum Timestamp Action
==============================
1 01/01/18 12:00 Start
2 01/01/18 01:00 Start
3 01/01/18 02:00 Stop
4 01/01/18 03:00 Start
5 01/01/18 05:00 Stop
6 01/01/18 13:00 Start
7 01/01/18 15:00 Start
8 01/01/18 17:00 Start
9 01/01/18 21:00 Stop
I want my final outcome to be something like this:
Start Stop
================================
01/01/18 12:00 01/01/18 02:00
01/01/18 03:00 01/01/18 05:00
01/01/18 13:00 01/01/18 21:00
Or even if it's there is a record for every Start to the nearest Stop that would be great as well.
Thank you so much for any guidance.
sql tsql
sql tsql
asked Nov 14 '18 at 21:12
ssokol91ssokol91
30211023
30211023
add a comment |
add a comment |
4 Answers
4
active
oldest
votes
You can use lag
and ceiling
functions as :
select max(Start) as Start, max(Stop) as Stop
from
(
select row_number() over ( order by rownum ) as rn,
( case when action = 'Start' then q.timestamp end ) as Start,
( case when action = 'Stop' then q.timestamp end ) as Stop
from
(
select t.*,
lag(action) over (order by rownum) as lg
from tab t
) q
where q.action != coalesce(lg,'Stop')
) r
group by ceiling(rn*.5)
order by ceiling(rn*.5);
Start Stop
01.01.2018 12:00:00 01.01.2018 02:00:00
01.01.2018 03:00:00 01.01.2018 05:00:00
01.01.2018 13:00:00 01.01.2018 21:00:00
Rextester Demo
P.S. For every binary pair of steps we determine START
and STOP
rows where one of them non-null
, while the other member of pair is null
. Because of this logic I needed mod(...,2)
for row numbers and satisfied that by ceil(rn*.5)
which yields 1
for both 1
or 2
, 2
for both 3
and 4
, and 3
for 5
and 6
, respectively, i.e. nearest consecutive upper integer.
Great solution! For my education, could you explain what does Ceiling (rn*.5) does in this situation? Thanks!
– ssokol91
Nov 15 '18 at 15:45
1
@ssokol91 you're welcome. I edited.
– Barbaros Özhan
Nov 15 '18 at 16:03
1
Thanks! I figured it out. Great thinking!
– ssokol91
Nov 15 '18 at 18:46
add a comment |
You can utilize a Cumulative Min to find the next "Stop" event:
select *,
min(case when Action = 'Stop' then Timestamp end) -- next Stop
over (--partition by ???
order by Timestamp
rows between current row and unbounded following) as Stop
from tab
Based on that it's a simple aggregation:
with cte as
( select *,
min(case when Action = 'Stop' then Timestamp end) -- next Stop
over (--partition by ???
order by Timestamp
rows between current row and unbounded following) as Stop
from tab
)
select
min(Timestamp) as start,
Stop
from cte
group by Stop
order by 1
add a comment |
Firstly Get a stop for every start in the table
select
A.Timestamp as Start
min(B.Timestamp) as Stop
from
mytable A
left join
mytable B
on A.Action = 'Start'
and B.Action = 'Stop'
and A.Timestamp < B.Timestamp
group by A.Timestamp
You can then use that result (Alias table R1 int the below query) to get your final table
select
min(Start),
Stop
from
(
select
A.Timestamp as Start
min(B.Timestamp) as Stop
from
mytable A
left join
mytable B
on A.Action = 'Start'
and B.Action = 'Stop'
and A.Timestamp < B.Timestamp
group by A.Timestamp
) as R1
group by Stop
add a comment |
Solution plan:
Scenario A is when you want to see a Stop for all Starts: find the earliest Stop that is after a Start.
Scenario B is when you want to see a Stop for the earliest/latest Start only: first get a set from the result of Senario A, then find the earliest/latest Start that is before a Stop.
This solution does not consider when there are duplicates and you want to keep them in the result - that would involve a third field like RowNum.
One possible implementation:
DECLARE @Table TABLE (
Timestamp DATETIME,
Action VARCHAR(5)
)
INSERT @Table
VALUES
('01/01/18 12:00', 'Start'),
('01/01/18 01:00', 'Start'),
('01/01/18 02:00', 'Stop'),
('01/01/18 03:00', 'Start'),
('01/01/18 05:00', 'Stop'),
('01/01/18 13:00', 'Start'),
('01/01/18 15:00', 'Start'),
('01/01/18 17:00', 'Start'),
('01/01/18 21:00', 'Stop'),
('01/01/18 22:00', 'Start')
SELECT * FROM @Table WHERE Action = 'Start' ORDER BY Timestamp
SELECT * FROM @Table WHERE Action = 'Stop' ORDER BY Timestamp
-- Scenario A:
SELECT Starts.Timestamp as Start, MIN (Stops.Timestamp) as Stop
FROM
(SELECT * FROM @Table WHERE Action = 'Start') as Starts
LEFT OUTER JOIN
(SELECT * FROM @Table WHERE Action = 'Stop') as Stops
on Stops.Timestamp >= Starts.Timestamp
GROUP BY Starts.Timestamp
ORDER BY Starts.Timestamp
-- Scenario B:
-- same block as above with a temp table to hold the results
SELECT Starts.Timestamp as Start, MIN (Stops.Timestamp) as Stop
INTO #allstops
FROM
(SELECT * FROM @Table WHERE Action = 'Start') as Starts
LEFT OUTER JOIN
(SELECT * FROM @Table WHERE Action = 'Stop') as Stops
on Stops.Timestamp >= Starts.Timestamp
GROUP BY Starts.Timestamp
SELECT allstops.Start, LatestStart.Stop
FROM #allstops as allstops
LEFT OUTER JOIN (
SELECT MIN (Start) as Start, Stop -- this returns the earliest Start, switch to MAX to get the latest
FROM #allstops
GROUP BY Stop
) as LatestStart
on allstops.Start = LatestStart.Start
Great suggestion! Thanks!
– ssokol91
Nov 16 '18 at 18:28
add a comment |
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53308789%2fcreating-start-stop-date-ranges-dataset-with-multiple-starts-for-one-stop%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
4 Answers
4
active
oldest
votes
4 Answers
4
active
oldest
votes
active
oldest
votes
active
oldest
votes
You can use lag
and ceiling
functions as :
select max(Start) as Start, max(Stop) as Stop
from
(
select row_number() over ( order by rownum ) as rn,
( case when action = 'Start' then q.timestamp end ) as Start,
( case when action = 'Stop' then q.timestamp end ) as Stop
from
(
select t.*,
lag(action) over (order by rownum) as lg
from tab t
) q
where q.action != coalesce(lg,'Stop')
) r
group by ceiling(rn*.5)
order by ceiling(rn*.5);
Start Stop
01.01.2018 12:00:00 01.01.2018 02:00:00
01.01.2018 03:00:00 01.01.2018 05:00:00
01.01.2018 13:00:00 01.01.2018 21:00:00
Rextester Demo
P.S. For every binary pair of steps we determine START
and STOP
rows where one of them non-null
, while the other member of pair is null
. Because of this logic I needed mod(...,2)
for row numbers and satisfied that by ceil(rn*.5)
which yields 1
for both 1
or 2
, 2
for both 3
and 4
, and 3
for 5
and 6
, respectively, i.e. nearest consecutive upper integer.
Great solution! For my education, could you explain what does Ceiling (rn*.5) does in this situation? Thanks!
– ssokol91
Nov 15 '18 at 15:45
1
@ssokol91 you're welcome. I edited.
– Barbaros Özhan
Nov 15 '18 at 16:03
1
Thanks! I figured it out. Great thinking!
– ssokol91
Nov 15 '18 at 18:46
add a comment |
You can use lag
and ceiling
functions as :
select max(Start) as Start, max(Stop) as Stop
from
(
select row_number() over ( order by rownum ) as rn,
( case when action = 'Start' then q.timestamp end ) as Start,
( case when action = 'Stop' then q.timestamp end ) as Stop
from
(
select t.*,
lag(action) over (order by rownum) as lg
from tab t
) q
where q.action != coalesce(lg,'Stop')
) r
group by ceiling(rn*.5)
order by ceiling(rn*.5);
Start Stop
01.01.2018 12:00:00 01.01.2018 02:00:00
01.01.2018 03:00:00 01.01.2018 05:00:00
01.01.2018 13:00:00 01.01.2018 21:00:00
Rextester Demo
P.S. For every binary pair of steps we determine START
and STOP
rows where one of them non-null
, while the other member of pair is null
. Because of this logic I needed mod(...,2)
for row numbers and satisfied that by ceil(rn*.5)
which yields 1
for both 1
or 2
, 2
for both 3
and 4
, and 3
for 5
and 6
, respectively, i.e. nearest consecutive upper integer.
Great solution! For my education, could you explain what does Ceiling (rn*.5) does in this situation? Thanks!
– ssokol91
Nov 15 '18 at 15:45
1
@ssokol91 you're welcome. I edited.
– Barbaros Özhan
Nov 15 '18 at 16:03
1
Thanks! I figured it out. Great thinking!
– ssokol91
Nov 15 '18 at 18:46
add a comment |
You can use lag
and ceiling
functions as :
select max(Start) as Start, max(Stop) as Stop
from
(
select row_number() over ( order by rownum ) as rn,
( case when action = 'Start' then q.timestamp end ) as Start,
( case when action = 'Stop' then q.timestamp end ) as Stop
from
(
select t.*,
lag(action) over (order by rownum) as lg
from tab t
) q
where q.action != coalesce(lg,'Stop')
) r
group by ceiling(rn*.5)
order by ceiling(rn*.5);
Start Stop
01.01.2018 12:00:00 01.01.2018 02:00:00
01.01.2018 03:00:00 01.01.2018 05:00:00
01.01.2018 13:00:00 01.01.2018 21:00:00
Rextester Demo
P.S. For every binary pair of steps we determine START
and STOP
rows where one of them non-null
, while the other member of pair is null
. Because of this logic I needed mod(...,2)
for row numbers and satisfied that by ceil(rn*.5)
which yields 1
for both 1
or 2
, 2
for both 3
and 4
, and 3
for 5
and 6
, respectively, i.e. nearest consecutive upper integer.
You can use lag
and ceiling
functions as :
select max(Start) as Start, max(Stop) as Stop
from
(
select row_number() over ( order by rownum ) as rn,
( case when action = 'Start' then q.timestamp end ) as Start,
( case when action = 'Stop' then q.timestamp end ) as Stop
from
(
select t.*,
lag(action) over (order by rownum) as lg
from tab t
) q
where q.action != coalesce(lg,'Stop')
) r
group by ceiling(rn*.5)
order by ceiling(rn*.5);
Start Stop
01.01.2018 12:00:00 01.01.2018 02:00:00
01.01.2018 03:00:00 01.01.2018 05:00:00
01.01.2018 13:00:00 01.01.2018 21:00:00
Rextester Demo
P.S. For every binary pair of steps we determine START
and STOP
rows where one of them non-null
, while the other member of pair is null
. Because of this logic I needed mod(...,2)
for row numbers and satisfied that by ceil(rn*.5)
which yields 1
for both 1
or 2
, 2
for both 3
and 4
, and 3
for 5
and 6
, respectively, i.e. nearest consecutive upper integer.
edited Nov 15 '18 at 16:03
answered Nov 14 '18 at 22:02
Barbaros ÖzhanBarbaros Özhan
14.3k71634
14.3k71634
Great solution! For my education, could you explain what does Ceiling (rn*.5) does in this situation? Thanks!
– ssokol91
Nov 15 '18 at 15:45
1
@ssokol91 you're welcome. I edited.
– Barbaros Özhan
Nov 15 '18 at 16:03
1
Thanks! I figured it out. Great thinking!
– ssokol91
Nov 15 '18 at 18:46
add a comment |
Great solution! For my education, could you explain what does Ceiling (rn*.5) does in this situation? Thanks!
– ssokol91
Nov 15 '18 at 15:45
1
@ssokol91 you're welcome. I edited.
– Barbaros Özhan
Nov 15 '18 at 16:03
1
Thanks! I figured it out. Great thinking!
– ssokol91
Nov 15 '18 at 18:46
Great solution! For my education, could you explain what does Ceiling (rn*.5) does in this situation? Thanks!
– ssokol91
Nov 15 '18 at 15:45
Great solution! For my education, could you explain what does Ceiling (rn*.5) does in this situation? Thanks!
– ssokol91
Nov 15 '18 at 15:45
1
1
@ssokol91 you're welcome. I edited.
– Barbaros Özhan
Nov 15 '18 at 16:03
@ssokol91 you're welcome. I edited.
– Barbaros Özhan
Nov 15 '18 at 16:03
1
1
Thanks! I figured it out. Great thinking!
– ssokol91
Nov 15 '18 at 18:46
Thanks! I figured it out. Great thinking!
– ssokol91
Nov 15 '18 at 18:46
add a comment |
You can utilize a Cumulative Min to find the next "Stop" event:
select *,
min(case when Action = 'Stop' then Timestamp end) -- next Stop
over (--partition by ???
order by Timestamp
rows between current row and unbounded following) as Stop
from tab
Based on that it's a simple aggregation:
with cte as
( select *,
min(case when Action = 'Stop' then Timestamp end) -- next Stop
over (--partition by ???
order by Timestamp
rows between current row and unbounded following) as Stop
from tab
)
select
min(Timestamp) as start,
Stop
from cte
group by Stop
order by 1
add a comment |
You can utilize a Cumulative Min to find the next "Stop" event:
select *,
min(case when Action = 'Stop' then Timestamp end) -- next Stop
over (--partition by ???
order by Timestamp
rows between current row and unbounded following) as Stop
from tab
Based on that it's a simple aggregation:
with cte as
( select *,
min(case when Action = 'Stop' then Timestamp end) -- next Stop
over (--partition by ???
order by Timestamp
rows between current row and unbounded following) as Stop
from tab
)
select
min(Timestamp) as start,
Stop
from cte
group by Stop
order by 1
add a comment |
You can utilize a Cumulative Min to find the next "Stop" event:
select *,
min(case when Action = 'Stop' then Timestamp end) -- next Stop
over (--partition by ???
order by Timestamp
rows between current row and unbounded following) as Stop
from tab
Based on that it's a simple aggregation:
with cte as
( select *,
min(case when Action = 'Stop' then Timestamp end) -- next Stop
over (--partition by ???
order by Timestamp
rows between current row and unbounded following) as Stop
from tab
)
select
min(Timestamp) as start,
Stop
from cte
group by Stop
order by 1
You can utilize a Cumulative Min to find the next "Stop" event:
select *,
min(case when Action = 'Stop' then Timestamp end) -- next Stop
over (--partition by ???
order by Timestamp
rows between current row and unbounded following) as Stop
from tab
Based on that it's a simple aggregation:
with cte as
( select *,
min(case when Action = 'Stop' then Timestamp end) -- next Stop
over (--partition by ???
order by Timestamp
rows between current row and unbounded following) as Stop
from tab
)
select
min(Timestamp) as start,
Stop
from cte
group by Stop
order by 1
answered Nov 14 '18 at 21:39
dnoethdnoeth
45.9k31839
45.9k31839
add a comment |
add a comment |
Firstly Get a stop for every start in the table
select
A.Timestamp as Start
min(B.Timestamp) as Stop
from
mytable A
left join
mytable B
on A.Action = 'Start'
and B.Action = 'Stop'
and A.Timestamp < B.Timestamp
group by A.Timestamp
You can then use that result (Alias table R1 int the below query) to get your final table
select
min(Start),
Stop
from
(
select
A.Timestamp as Start
min(B.Timestamp) as Stop
from
mytable A
left join
mytable B
on A.Action = 'Start'
and B.Action = 'Stop'
and A.Timestamp < B.Timestamp
group by A.Timestamp
) as R1
group by Stop
add a comment |
Firstly Get a stop for every start in the table
select
A.Timestamp as Start
min(B.Timestamp) as Stop
from
mytable A
left join
mytable B
on A.Action = 'Start'
and B.Action = 'Stop'
and A.Timestamp < B.Timestamp
group by A.Timestamp
You can then use that result (Alias table R1 int the below query) to get your final table
select
min(Start),
Stop
from
(
select
A.Timestamp as Start
min(B.Timestamp) as Stop
from
mytable A
left join
mytable B
on A.Action = 'Start'
and B.Action = 'Stop'
and A.Timestamp < B.Timestamp
group by A.Timestamp
) as R1
group by Stop
add a comment |
Firstly Get a stop for every start in the table
select
A.Timestamp as Start
min(B.Timestamp) as Stop
from
mytable A
left join
mytable B
on A.Action = 'Start'
and B.Action = 'Stop'
and A.Timestamp < B.Timestamp
group by A.Timestamp
You can then use that result (Alias table R1 int the below query) to get your final table
select
min(Start),
Stop
from
(
select
A.Timestamp as Start
min(B.Timestamp) as Stop
from
mytable A
left join
mytable B
on A.Action = 'Start'
and B.Action = 'Stop'
and A.Timestamp < B.Timestamp
group by A.Timestamp
) as R1
group by Stop
Firstly Get a stop for every start in the table
select
A.Timestamp as Start
min(B.Timestamp) as Stop
from
mytable A
left join
mytable B
on A.Action = 'Start'
and B.Action = 'Stop'
and A.Timestamp < B.Timestamp
group by A.Timestamp
You can then use that result (Alias table R1 int the below query) to get your final table
select
min(Start),
Stop
from
(
select
A.Timestamp as Start
min(B.Timestamp) as Stop
from
mytable A
left join
mytable B
on A.Action = 'Start'
and B.Action = 'Stop'
and A.Timestamp < B.Timestamp
group by A.Timestamp
) as R1
group by Stop
answered Nov 14 '18 at 21:27
linjoehanlinjoehan
111
111
add a comment |
add a comment |
Solution plan:
Scenario A is when you want to see a Stop for all Starts: find the earliest Stop that is after a Start.
Scenario B is when you want to see a Stop for the earliest/latest Start only: first get a set from the result of Senario A, then find the earliest/latest Start that is before a Stop.
This solution does not consider when there are duplicates and you want to keep them in the result - that would involve a third field like RowNum.
One possible implementation:
DECLARE @Table TABLE (
Timestamp DATETIME,
Action VARCHAR(5)
)
INSERT @Table
VALUES
('01/01/18 12:00', 'Start'),
('01/01/18 01:00', 'Start'),
('01/01/18 02:00', 'Stop'),
('01/01/18 03:00', 'Start'),
('01/01/18 05:00', 'Stop'),
('01/01/18 13:00', 'Start'),
('01/01/18 15:00', 'Start'),
('01/01/18 17:00', 'Start'),
('01/01/18 21:00', 'Stop'),
('01/01/18 22:00', 'Start')
SELECT * FROM @Table WHERE Action = 'Start' ORDER BY Timestamp
SELECT * FROM @Table WHERE Action = 'Stop' ORDER BY Timestamp
-- Scenario A:
SELECT Starts.Timestamp as Start, MIN (Stops.Timestamp) as Stop
FROM
(SELECT * FROM @Table WHERE Action = 'Start') as Starts
LEFT OUTER JOIN
(SELECT * FROM @Table WHERE Action = 'Stop') as Stops
on Stops.Timestamp >= Starts.Timestamp
GROUP BY Starts.Timestamp
ORDER BY Starts.Timestamp
-- Scenario B:
-- same block as above with a temp table to hold the results
SELECT Starts.Timestamp as Start, MIN (Stops.Timestamp) as Stop
INTO #allstops
FROM
(SELECT * FROM @Table WHERE Action = 'Start') as Starts
LEFT OUTER JOIN
(SELECT * FROM @Table WHERE Action = 'Stop') as Stops
on Stops.Timestamp >= Starts.Timestamp
GROUP BY Starts.Timestamp
SELECT allstops.Start, LatestStart.Stop
FROM #allstops as allstops
LEFT OUTER JOIN (
SELECT MIN (Start) as Start, Stop -- this returns the earliest Start, switch to MAX to get the latest
FROM #allstops
GROUP BY Stop
) as LatestStart
on allstops.Start = LatestStart.Start
Great suggestion! Thanks!
– ssokol91
Nov 16 '18 at 18:28
add a comment |
Solution plan:
Scenario A is when you want to see a Stop for all Starts: find the earliest Stop that is after a Start.
Scenario B is when you want to see a Stop for the earliest/latest Start only: first get a set from the result of Senario A, then find the earliest/latest Start that is before a Stop.
This solution does not consider when there are duplicates and you want to keep them in the result - that would involve a third field like RowNum.
One possible implementation:
DECLARE @Table TABLE (
Timestamp DATETIME,
Action VARCHAR(5)
)
INSERT @Table
VALUES
('01/01/18 12:00', 'Start'),
('01/01/18 01:00', 'Start'),
('01/01/18 02:00', 'Stop'),
('01/01/18 03:00', 'Start'),
('01/01/18 05:00', 'Stop'),
('01/01/18 13:00', 'Start'),
('01/01/18 15:00', 'Start'),
('01/01/18 17:00', 'Start'),
('01/01/18 21:00', 'Stop'),
('01/01/18 22:00', 'Start')
SELECT * FROM @Table WHERE Action = 'Start' ORDER BY Timestamp
SELECT * FROM @Table WHERE Action = 'Stop' ORDER BY Timestamp
-- Scenario A:
SELECT Starts.Timestamp as Start, MIN (Stops.Timestamp) as Stop
FROM
(SELECT * FROM @Table WHERE Action = 'Start') as Starts
LEFT OUTER JOIN
(SELECT * FROM @Table WHERE Action = 'Stop') as Stops
on Stops.Timestamp >= Starts.Timestamp
GROUP BY Starts.Timestamp
ORDER BY Starts.Timestamp
-- Scenario B:
-- same block as above with a temp table to hold the results
SELECT Starts.Timestamp as Start, MIN (Stops.Timestamp) as Stop
INTO #allstops
FROM
(SELECT * FROM @Table WHERE Action = 'Start') as Starts
LEFT OUTER JOIN
(SELECT * FROM @Table WHERE Action = 'Stop') as Stops
on Stops.Timestamp >= Starts.Timestamp
GROUP BY Starts.Timestamp
SELECT allstops.Start, LatestStart.Stop
FROM #allstops as allstops
LEFT OUTER JOIN (
SELECT MIN (Start) as Start, Stop -- this returns the earliest Start, switch to MAX to get the latest
FROM #allstops
GROUP BY Stop
) as LatestStart
on allstops.Start = LatestStart.Start
Great suggestion! Thanks!
– ssokol91
Nov 16 '18 at 18:28
add a comment |
Solution plan:
Scenario A is when you want to see a Stop for all Starts: find the earliest Stop that is after a Start.
Scenario B is when you want to see a Stop for the earliest/latest Start only: first get a set from the result of Senario A, then find the earliest/latest Start that is before a Stop.
This solution does not consider when there are duplicates and you want to keep them in the result - that would involve a third field like RowNum.
One possible implementation:
DECLARE @Table TABLE (
Timestamp DATETIME,
Action VARCHAR(5)
)
INSERT @Table
VALUES
('01/01/18 12:00', 'Start'),
('01/01/18 01:00', 'Start'),
('01/01/18 02:00', 'Stop'),
('01/01/18 03:00', 'Start'),
('01/01/18 05:00', 'Stop'),
('01/01/18 13:00', 'Start'),
('01/01/18 15:00', 'Start'),
('01/01/18 17:00', 'Start'),
('01/01/18 21:00', 'Stop'),
('01/01/18 22:00', 'Start')
SELECT * FROM @Table WHERE Action = 'Start' ORDER BY Timestamp
SELECT * FROM @Table WHERE Action = 'Stop' ORDER BY Timestamp
-- Scenario A:
SELECT Starts.Timestamp as Start, MIN (Stops.Timestamp) as Stop
FROM
(SELECT * FROM @Table WHERE Action = 'Start') as Starts
LEFT OUTER JOIN
(SELECT * FROM @Table WHERE Action = 'Stop') as Stops
on Stops.Timestamp >= Starts.Timestamp
GROUP BY Starts.Timestamp
ORDER BY Starts.Timestamp
-- Scenario B:
-- same block as above with a temp table to hold the results
SELECT Starts.Timestamp as Start, MIN (Stops.Timestamp) as Stop
INTO #allstops
FROM
(SELECT * FROM @Table WHERE Action = 'Start') as Starts
LEFT OUTER JOIN
(SELECT * FROM @Table WHERE Action = 'Stop') as Stops
on Stops.Timestamp >= Starts.Timestamp
GROUP BY Starts.Timestamp
SELECT allstops.Start, LatestStart.Stop
FROM #allstops as allstops
LEFT OUTER JOIN (
SELECT MIN (Start) as Start, Stop -- this returns the earliest Start, switch to MAX to get the latest
FROM #allstops
GROUP BY Stop
) as LatestStart
on allstops.Start = LatestStart.Start
Solution plan:
Scenario A is when you want to see a Stop for all Starts: find the earliest Stop that is after a Start.
Scenario B is when you want to see a Stop for the earliest/latest Start only: first get a set from the result of Senario A, then find the earliest/latest Start that is before a Stop.
This solution does not consider when there are duplicates and you want to keep them in the result - that would involve a third field like RowNum.
One possible implementation:
DECLARE @Table TABLE (
Timestamp DATETIME,
Action VARCHAR(5)
)
INSERT @Table
VALUES
('01/01/18 12:00', 'Start'),
('01/01/18 01:00', 'Start'),
('01/01/18 02:00', 'Stop'),
('01/01/18 03:00', 'Start'),
('01/01/18 05:00', 'Stop'),
('01/01/18 13:00', 'Start'),
('01/01/18 15:00', 'Start'),
('01/01/18 17:00', 'Start'),
('01/01/18 21:00', 'Stop'),
('01/01/18 22:00', 'Start')
SELECT * FROM @Table WHERE Action = 'Start' ORDER BY Timestamp
SELECT * FROM @Table WHERE Action = 'Stop' ORDER BY Timestamp
-- Scenario A:
SELECT Starts.Timestamp as Start, MIN (Stops.Timestamp) as Stop
FROM
(SELECT * FROM @Table WHERE Action = 'Start') as Starts
LEFT OUTER JOIN
(SELECT * FROM @Table WHERE Action = 'Stop') as Stops
on Stops.Timestamp >= Starts.Timestamp
GROUP BY Starts.Timestamp
ORDER BY Starts.Timestamp
-- Scenario B:
-- same block as above with a temp table to hold the results
SELECT Starts.Timestamp as Start, MIN (Stops.Timestamp) as Stop
INTO #allstops
FROM
(SELECT * FROM @Table WHERE Action = 'Start') as Starts
LEFT OUTER JOIN
(SELECT * FROM @Table WHERE Action = 'Stop') as Stops
on Stops.Timestamp >= Starts.Timestamp
GROUP BY Starts.Timestamp
SELECT allstops.Start, LatestStart.Stop
FROM #allstops as allstops
LEFT OUTER JOIN (
SELECT MIN (Start) as Start, Stop -- this returns the earliest Start, switch to MAX to get the latest
FROM #allstops
GROUP BY Stop
) as LatestStart
on allstops.Start = LatestStart.Start
edited Nov 15 '18 at 17:57
answered Nov 14 '18 at 21:57
Dávid LaczkóDávid Laczkó
429129
429129
Great suggestion! Thanks!
– ssokol91
Nov 16 '18 at 18:28
add a comment |
Great suggestion! Thanks!
– ssokol91
Nov 16 '18 at 18:28
Great suggestion! Thanks!
– ssokol91
Nov 16 '18 at 18:28
Great suggestion! Thanks!
– ssokol91
Nov 16 '18 at 18:28
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53308789%2fcreating-start-stop-date-ranges-dataset-with-multiple-starts-for-one-stop%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown