cecilj
cecilj

Reputation: 152

How to Select MIN and MAX datetimes within a 24 hour period in T-SQL

The record exists in this format:

+-----------------------------+-----------------------------+----------+------------+----------+-----------------+
| StartDTM                    | EndDTM                      | PersonID | PersonName | Duration | TimeSheetItemID |
+-----------------------------+-----------------------------+----------+------------+----------+-----------------+
| 2019-08-17 17:48:00.0000000 | 2019-08-17 18:00:00.0000000 | 111111   | Smith, Bob | 0.200000 | 154446149       |
+-----------------------------+-----------------------------+----------+------------+----------+-----------------+
| 2019-08-17 18:00:00.0000000 | 2019-08-17 23:00:00.0000000 | 111111   | Smith, Bob | 5.000000 | 154446149       |
+-----------------------------+-----------------------------+----------+------------+----------+-----------------+
| 2019-08-17 23:00:00.0000000 | 2019-08-17 23:30:00.0000000 | 111111   | Smith, Bob | 0.500000 | 154446149       |
+-----------------------------+-----------------------------+----------+------------+----------+-----------------+
| 2019-08-17 23:30:00.0000000 | 2019-08-18 00:00:00.0000000 | 111111   | Smith, Bob | 0.500000 | 154446149       |
+-----------------------------+-----------------------------+----------+------------+----------+-----------------+
| 2019-08-18 00:00:00.0000000 | 2019-08-18 02:14:00.0000000 | 111111   | Smith, Bob | 2.233333 | 154446149       |
+-----------------------------+-----------------------------+----------+------------+----------+-----------------+
| 2019-08-18 02:14:00.0000000 | 2019-08-18 06:18:00.0000000 | 111111   | Smith, Bob | 4.066666 | 154478804       |
+-----------------------------+-----------------------------+----------+------------+----------+-----------------+
| 2019-08-25 17:48:00.0000000 | 2019-08-25 18:00:00.0000000 | 111111   | Smith, Bob | 0.200000 | 154745867       |
+-----------------------------+-----------------------------+----------+------------+----------+-----------------+
| 2019-08-25 18:00:00.0000000 | 2019-08-25 23:00:00.0000000 | 111111   | Smith, Bob | 5.000000 | 154745867       |
+-----------------------------+-----------------------------+----------+------------+----------+-----------------+
| 2019-08-25 23:00:00.0000000 | 2019-08-25 23:30:00.0000000 | 111111   | Smith, Bob | 0.500000 | 154745867       |
+-----------------------------+-----------------------------+----------+------------+----------+-----------------+
| 2019-08-25 23:30:00.0000000 | 2019-08-26 00:00:00.0000000 | 111111   | Smith, Bob | 0.500000 | 154745867       |
+-----------------------------+-----------------------------+----------+------------+----------+-----------------+
| 2019-08-26 00:00:00.0000000 | 2019-08-26 02:00:00.0000000 | 111111   | Smith, Bob | 2.000000 | 154745867       |
+-----------------------------+-----------------------------+----------+------------+----------+-----------------+
| 2019-08-26 02:00:00.0000000 | 2019-08-26 05:54:00.0000000 | 111111   | Smith, Bob | 3.900000 | 154756492       |
+-----------------------------+-----------------------------+----------+------------+----------+-----------------+

I need to select the MIN StartDTM and the MAX EndDTM within a 24 hour period. I have tried selecting MIN(StartDTM) and MAX(EndDTM) in combination with GROUP BY PersonName and TimeSheetID, but this fails as sometimes more than one TimeSheetID exists within a 24 hour period (See row 6 above).

My desired results should look like this:

+-----------------------------+-----------------------------+----------+------------+-----------------+
| StartDTM                    | EndDTM                      | PersonID | PersonName | TimeSheetItemID |
+-----------------------------+-----------------------------+----------+------------+-----------------+
| 2019-08-17 17:48:00.0000000 | 2019-08-18 06:18:00.0000000 | 111111   | Smith, Bob | 154446149       |
+-----------------------------+-----------------------------+----------+------------+-----------------+
| 2019-08-25 17:48:00.0000000 | 2019-08-26 05:54:00.0000000 | 111111   | Smith, Bob | 154745867       |
+-----------------------------+-----------------------------+----------+------------+-----------------+

Is this possible to achieve in T-SQL?

Upvotes: 0

Views: 308

Answers (3)

Kannan Kandasamy
Kannan Kandasamy

Reputation: 13959

You can use lead and lag as below to solve this problem:

;with cte_bucket as (
    select *, sum(difsec) over(partition by personid order by startdtm) bucket from (
        select *, coalesce(ABS(datediff(ss, startdtm, lag(enddtm) over( partition by personid order by startdtm))), 1) difsec
        from #table
    ) a 
)
select min(startdtm), max(enddtm), personid, personname, min(timesheetitemid)  from cte_bucket
group by personid, personname, bucket 

Code for reference:

https://rextester.com/UNF89433

+----+---------------------+---------------------+----------+------------+-----------------+
|    |      startDTM       |       endDTM        | personid | personname | TimesheetItemId |
+----+---------------------+---------------------+----------+------------+-----------------+
|  1 | 17.08.2019 17:48:00 | 18.08.2019 06:18:00 |   111111 | Smith, Bob |       154446149 |
|  2 | 25.08.2019 17:48:00 | 26.08.2019 05:54:00 |   111111 | Smith, Bob |       154745867 |
+----+---------------------+---------------------+----------+------------+-----------------+ 

Upvotes: 0

avery_larry
avery_larry

Reputation: 2135

If I understand that you want min/max per day, then you need to also group by the day. untested

select StartDTM, EndDTM, startTable.PersonID
   ,startTable.PersonName, startTable.TimeSheetItemID
from (
   select min(StartDTM) StartDTM, PersonID, PersonName, TimeSheetItemID
   from YourTable
   group by convert(date,StartDTM), PersonID, TimeSheetItemID, PersonName
) startTable
full outer join (
   select max(EndDTM) EndDTM, PersonID, TimeSheetItemID
   from YourTable
   group by convert(date,endDTM), PersonID, TimeSheetItemID
) EndTable
   on startTable.PersonID = endTable.PersonID
   and startTable.TimeSheetItemID = endTable.TimeSheetItemID
where convert(date,StartDTM) = convert(date,EndDTM)
order by startTable.PersonID,StartDTM

Timesheets that do not have both a startDTM and endDTM in a given day should have null values in this query.

If you are interested in any 24 hour period then that's a whole different thing.

Upvotes: 0

Gordon Linoff
Gordon Linoff

Reputation: 1269493

This is gaps-and-islands problem. You need to find where the islands start. In this case, I recommend a cumulative maximum.

select personId, min(startTM), max(endTM)
from (select t.*,
             sum(case when prev_maxEndTm >= dateadd(day, -1, startTm)
                      then 0  -- maximum is later than this record so no new island
                      else 1  -- maximum is earlier so new island
                  end) over (partition by personId order by startTm) as grp
      from (select t.*,
                   max(EndTm) over (partition by personId
                                    order by startTm
                                    rows between unbounded preceding and 1 preceding
                                   ) as prev_maxEndTm
            from t
           ) t
     ) t
group by personId;

Upvotes: 1

Related Questions