You get SQL Server to do the required accumulation, once you have some way of ordering the rows (here, I've added ord
):
declare @t table (speaker int not null,duration int not null,ord int not null)
insert into @t (speaker,duration,ord) values
(1, 480,1),
(2, 100,2),
(2, 260,3),
(2, 200,4),
(1, 2640,5),
(2, 280,6)
;with Merged as (
select speaker,duration,ord,ord as last
from @t t1
where not exists(
select * from @t t2
where t1.speaker = t2.speaker and t1.ord = t2.ord + 1)
union all
select m.speaker,m.duration+t.duration,m.ord,t.ord
from Merged m
inner join @t t on m.speaker = t.speaker and m.last = t.ord - 1
), Final as (
select speaker,duration,ord,last,
ROW_NUMBER() OVER (PARTITION BY ord ORDER by last desc) as rn
from Merged
)
select * from Final where rn = 1 order by duration desc
Result:
speaker duration ord last rn
----------- ----------- ----------- ----------- --------------------
1 2640 5 5 1
2 560 2 4 1
1 480 1 1 1
2 280 6 6 1
So speaker 1 had the longest single duration at 2640, speaker 2 came second with 560, etc.
The above query uses two Common Table Expressions (CTE). In the first (Merged
), we define one recursively. The first part of the query finds rows for which there isn't an immediately preceding row with the same speaker (so, logically, these are the first rows of each unbroken section of speech for the speaker).
In the recursive portion, we add the next row if it belongs to the same speaker, and we keep track (in last
) of which row we added last. This recursive part runs as many times as needed to accumulate the unbroken sections.
Unfortunately, the set that Merged
produces also includes all of the intermediate steps we took in building up the uninterrupted speech. So in Final
, assign a ROW_NUMBER()
which allows us to easily find the last output for each row that was part of the initial set produced by Merged
. And so the final query just selects those rows.
If you don't have a convenient column like ord
, as I do above (monotonically increasing), you can simply generate such a column using another CTE, and whatever columns you do have that uniquely order the rows(*). So if you can uniquely identify rows by a column called time
, you could place this CTE as the first one:
;WITH StrictOrdered as (
SELECT speaker,duration,
ROW_NUMBER() OVER (ORDER BY time) as ord
FROM YourTable
)
And then replace all uses of @t
in the remainder of my query with StrictOrdered
.
(* Your updated example's time
doesn't fit this requirement)
To get just the highest value for each speaker, replace:
select * from Final where rn = 1 order by duration desc
with:
, Highest as (
select *,ROW_NUMBER() OVER (PARTITION BY Speaker ORDER BY duration desc) as rnDuration
from Final where rn = 1
)
select * from Highest where rnDuration = 1