Creating Start/Stop date ranges dataset with multiple starts for one stop










1















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.










share|improve this question


























    1















    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.










    share|improve this question
























      1












      1








      1








      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.










      share|improve this question














      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






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked Nov 14 '18 at 21:12









      ssokol91ssokol91

      30211023




      30211023






















          4 Answers
          4






          active

          oldest

          votes


















          1














          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.






          share|improve this answer

























          • 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


















          2














          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





          share|improve this answer






























            1














            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





            share|improve this answer






























              1














              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





              share|improve this answer

























              • Great suggestion! Thanks!

                – ssokol91
                Nov 16 '18 at 18:28










              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
              );



              );













              draft saved

              draft discarded


















              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









              1














              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.






              share|improve this answer

























              • 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















              1














              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.






              share|improve this answer

























              • 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













              1












              1








              1







              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.






              share|improve this answer















              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.







              share|improve this answer














              share|improve this answer



              share|improve this answer








              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

















              • 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













              2














              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





              share|improve this answer



























                2














                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





                share|improve this answer

























                  2












                  2








                  2







                  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





                  share|improve this answer













                  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






                  share|improve this answer












                  share|improve this answer



                  share|improve this answer










                  answered Nov 14 '18 at 21:39









                  dnoethdnoeth

                  45.9k31839




                  45.9k31839





















                      1














                      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





                      share|improve this answer



























                        1














                        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





                        share|improve this answer

























                          1












                          1








                          1







                          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





                          share|improve this answer













                          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






                          share|improve this answer












                          share|improve this answer



                          share|improve this answer










                          answered Nov 14 '18 at 21:27









                          linjoehanlinjoehan

                          111




                          111





















                              1














                              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





                              share|improve this answer

























                              • Great suggestion! Thanks!

                                – ssokol91
                                Nov 16 '18 at 18:28















                              1














                              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





                              share|improve this answer

























                              • Great suggestion! Thanks!

                                – ssokol91
                                Nov 16 '18 at 18:28













                              1












                              1








                              1







                              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





                              share|improve this answer















                              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






                              share|improve this answer














                              share|improve this answer



                              share|improve this answer








                              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

















                              • 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

















                              draft saved

                              draft discarded
















































                              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.




                              draft saved


                              draft discarded














                              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





















































                              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







                              Popular posts from this blog

                              How to how show current date and time by default on contact form 7 in WordPress without taking input from user in datetimepicker

                              Syphilis

                              Darth Vader #20