Simon P Stevens
Simon P Stevens

Reputation: 27499

Is it possible to group rows by difference to each other in T-SQL

Is it possible to write a SQL query that groups rows based on a datetime column value's difference from an adjacent row's value?

Let me give an example...I have a SQL query something like this:

SELECT
    Id,
    StartTime,
    EndTime,
    datediff(second, max(StartTime), EndTime)) as Duration
FROM Timings
ORDER BY StartTime

Which returns results something like this:

| ID | StartTime           | EndTime             | Duration
| 1  | 2017-10-06 10:59:48 | 2017-10-06 10:59:58 | 10
| 2  | 2017-10-06 11:00:02 | 2017-10-06 11:00:06 | 4
| 3  | 2017-10-06 11:00:15 | 2017-10-06 11:00:22 | 7
| 4  | 2017-10-06 11:00:30 | 2017-10-06 11:00:39 | 9
| 5  | 2017-10-06 15:34:31 | 2017-10-06 15:34:45 | 14
| 6  | 2017-10-06 15:34:48 | 2017-10-06 15:34:56 | 8
| 7  | 2017-10-06 15:34:52 | 2017-10-06 15:34:59 | 7

The important thing here is that the timings are in two batches, the first four were all done around 11am, and the second two around half past 3pm.

I want to get details about the start/end time of each batches of timings, the average duration, and the number of timings in a group. To do that what I need is a way to group the timings by batch, where a batch is defined as the group of timings with less than 30 seconds between the end of one and the start of the next, when ordered by start time. Is it possible?

Some related notes on the real situation...

Upvotes: 3

Views: 184

Answers (2)

Monofuse
Monofuse

Reputation: 827

You could group by the datetime parts

group by DATEPART(StartTime, yyyy) + DATEPART(StartTime, MM) + DATEPART(StartTime, DD)

You'd also have to change you select to match

Upvotes: 0

SqlZim
SqlZim

Reputation: 38023

In SQL Server 2012+:

Using the window function lag() in a common table expression to get the datediff() of the current row starttime compared to the previous row value for endtime, and then sum() over() with conditional aggregation (comparision to the hardcoded value) to generate the batch:

;with cte as (
select *
  , datediff(second,lag(endtime) over (order by starttime),starttime) as prev_dat
from timings
)
select id, starttime, endtime, duration
  , sum(case when coalesce(prev_dat,31)>30 then 1 else 0 end) over (
    order by starttime
    ) as batch
from cte

rextester demo: http://rextester.com/OVNF90739

returns:

+----+---------------------+---------------------+----------+-------+
| id |      starttime      |       endtime       | duration | batch |
+----+---------------------+---------------------+----------+-------+
|  1 | 2017-10-06 10:59:48 | 2017-10-06 10:59:58 |       10 |     1 |
|  2 | 2017-10-06 11:00:02 | 2017-10-06 11:00:06 |        4 |     1 |
|  3 | 2017-10-06 11:00:15 | 2017-10-06 11:00:22 |        7 |     1 |
|  4 | 2017-10-06 11:00:30 | 2017-10-06 11:00:39 |        9 |     1 |
|  5 | 2017-10-06 15:34:31 | 2017-10-06 15:34:45 |       14 |     2 |
|  6 | 2017-10-06 15:34:48 | 2017-10-06 15:34:56 |        8 |     2 |
|  7 | 2017-10-06 15:34:52 | 2017-10-06 15:34:59 |        7 |     2 |
+----+---------------------+---------------------+----------+-------+

Upvotes: 3

Related Questions