我想计算两个发言者不间断语音的最长持续时间。数据作为XML存储在MS SQL数据库中。
到目前为止,我converted the xml into table (in MSSQL) 输出了这样的结果..生成的表按时间顺序排列(以ms为单位的时间 - 存储为int)。
speaker duration time
1 480 0
2 100 0
2 260 100
2 200 360
1 2640 480
2 280 560
.. .. ..
接下来,我想用以下逻辑迭代结果:
如果发言人ID相同,请继续添加持续时间
如果说话者改变,将当前发言者的总时间与某个全局变量进行比较(如果新发言时间更长,则更新变量)
我需要在SQL中执行此操作 - 我是编写条件SQL的新手。 我不知道该怎么做.. :(
答案 0 :(得分:4)
一旦你有一些排序行的方法(这里,我添加了ord
),你就让SQL Server做了必要的积累:
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
结果:
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
因此,扬声器1的单个持续时间最长为2640,扬声器2为560,等等。
以上查询使用两个Common Table Expressions(CTE)。在第一个(Merged
)中,我们递归地定义一个。查询的第一部分查找没有前一行的行具有相同的扬声器的行(因此,从逻辑上讲,这些是扬声器的每个不间断语音段的第一行)。
在递归部分,我们添加下一行(如果它属于同一个发言者),并且我们跟踪(在last
)我们最后添加的行。这个递归部分可以根据需要运行多次,以累积不间断的部分。
不幸的是,Merged
生成的集合还包括我们在构建不间断语音时所采取的所有中间步骤。因此,在Final
中,指定ROW_NUMBER()
,这样我们就可以轻松找到Merged
生成的初始集合中每一行的最后一个输出。因此最终查询只选择那些行。
如果你没有像ord
那样方便的列,就像我上面那样(单调增加),你可以使用另一个CTE生成这样一个列,以及你做的任何列对行(*)进行唯一排序。因此,如果您可以通过名为time
的列唯一标识行,则可以将此CTE作为第一个:
;WITH StrictOrdered as (
SELECT speaker,duration,
ROW_NUMBER() OVER (ORDER BY time) as ord
FROM YourTable
)
然后使用@t
替换查询剩余部分中StrictOrdered
的所有用途。
(*您更新的示例time
不符合此要求)
要获得每个发言者的最高价值,请替换:
select * from Final where rn = 1 order by duration desc
使用:
, 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
答案 1 :(得分:1)
这是解决此问题的另一种方法。
与Damien的解决方案一样,您需要为数据添加序列号,因为SQL表本身就是无序集,因此您需要一个列来定义任何排序。我会称之为ord
(虽然我通常只使用id
)。生成此方法的典型方法是使用以下语句:
create table as (. . .
ord int identity(1,1),
. . .
)
create view v_table as
select <everything but ord>
from table
然后您可以插入或批量插入视图,并自动创建ord列。
对于每个发言者,我想通过为它们分配值来将连续行组合在一起。我选择的值是它们后面一行的“ord”值:
1 480 1 2
2 100 2 5
2 260 3 5
2 200 4 5
1 2640 5 . . .
在此结果上,我按最后一列分组,取持续时间的总和并选择最长持续时间。
挑战在于获得第四列。为此,我将使用相关子查询。以下是所有这些:
select top 1 speaker, sum(duration)
from (select t.*,
(select min(ord) from t t2 where t2.speaker <> t.speaker and t2.ord > t.ord
) as GroupingValue
from t
) t
group by GroupingValue, speaker
order by sum(duration) desc
要获得每个发言者的最高持续时间,您只需使用另一个窗口函数row_number()
即可。这只需要一个级别的子查询,我正在使用CTE:
with SpeakerDur as (
select speaker, sum(duration) as GroupedDuration
from (select t.*,
(select min(ord) from t t2 where t2.speaker <> t.speaker and t2.ord > t.ord
) as GroupingValue
from t
) t
group by GroupingValue, speaker
)
select *
from (select sd.*,
row_number() over (partition by speaker order by GroupedDuration desc) as seqnum
from SpeakerDur sd
) sd
where seqnum = 1
row_number()
将序号1,2,3等分配给每个发言者(partition by speaker
)的持续时间,从最长持续时间(order by GroupedDuration desc
)开始。然后它选择最高值。如果您想要前五名,可以将where
子句更改为seqnum <= 5
。