计算最长的不间断语音持续时间

时间:2013-01-10 11:50:10

标签: sql-server-2008

我想计算两个发言者不间断语音的最长持续时间。数据作为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的新手。 我不知道该怎么做.. :(

2 个答案:

答案 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