• This can be done more efficiently with a simple ROW_NUMBER implementation.

    😎

    The problem with your solution is that it scans the table 1+(2 x number of rows) times, fear it will be very inefficient on larger sets.

    Single table scan solution

    ;with SAMPLE_DATA as (

    select 'start' [eventtype],cast('2016-05-17 08:00:00' as datetime) [eventdatetime]

    union select 'start',cast('2016-05-17 09:10:00' as datetime)

    union select 'start',cast('2016-05-17 09:40:00' as datetime)

    union select 'stop',cast('2016-05-17 09:50:00' as datetime)

    union select 'stop',cast('2016-05-17 09:12:00' as datetime)

    union select 'stop',cast('2016-05-17 08:10:00' as datetime)

    union select 'start',cast('2016-05-17 09:40:00' as datetime)

    union select 'stop',cast('2016-05-17 09:50:00' as datetime)

    union select 'stop',cast('2016-05-17 09:12:00' as datetime)

    union select 'start',cast('2016-05-17 10:00:00' as datetime)

    union select 'stop',cast('2016-05-17 10:50:00' as datetime)

    union select 'start',cast('2016-05-17 10:40:00' as datetime)

    union select 'stop',cast('2016-05-17 11:50:00' as datetime)

    )

    ,GROUPED_DATA AS

    (

    SELECT

    (ROW_NUMBER() OVER

    (

    ORDER BY SD.eventdatetime

    ) / 2) +

    CASE

    WHEN SD.eventtype = 'start' THEN 1

    ELSE 0

    END AS GRP

    ,SD.eventtype

    ,SD.eventdatetime

    FROM SAMPLE_DATA SD

    )

    SELECT

    MAX(CASE WHEN GB.eventtype = 'start' THEN GB.eventdatetime END) AS starttime

    ,MAX(CASE WHEN GB.eventtype = 'stop' THEN GB.eventdatetime END) AS endtime

    FROM GROUPED_DATA GB

    GROUP BY GB.GRP;

    Output

    starttime endtime

    ----------------------- -----------------------

    2016-05-17 08:00:00.000 2016-05-17 08:10:00.000

    2016-05-17 09:10:00.000 2016-05-17 09:12:00.000

    2016-05-17 09:40:00.000 2016-05-17 09:50:00.000

    2016-05-17 10:00:00.000 2016-05-17 10:50:00.000

    2016-05-17 10:40:00.000 2016-05-17 11:50:00.000