SQl:找出表中最后连续数的计数

时间:2016-12-08 14:45:20

标签: sql sql-server tsql

enter image description here

见上述数据。

对于每个学生,我需要得出学生在场的最后连续几天的计数。

有人可以就此提出想法吗?

;with CTE As
(select 1 as stdId, 1 as dt
union
select 1 as stdId, 2 as dt
union
select 1 as stdId, 3 as dt
union
select 1 as stdId, 5 as dt
union
select 1 as stdId, 6 as dt
union
select 1 as stdId, 7 as dt
union
select 1 as stdId, 9 as dt
union
select 2 as stdId, 2 as dt
union
select 2 as stdId, 4 as dt
union
select 2 as stdId, 7 as dt
union
select 2 as stdId, 8 as dt
)
select stdId, dt
FROM 
CTE

3 个答案:

答案 0 :(得分:2)

1)使用dt和rownumber的差异,根据存在的连续天数对组进行分类。

2)然后获得每个学生的最后一组和每组的计数。

3)最后选择每个学生最后一组的计数。

select distinct stdid,cnt 
from (select stdid,grp,count(*) over(partition by stdid,grp) cnt,
      max(grp) over(partition by stdid) lastgrp
      from (select stdId, dt,dt-row_number() over(partition by stdid order by dt) grp
            FROM CTE) x
) y where lastgrp=grp

<强> Sample Demo

答案 1 :(得分:2)

一种方法是使用recursive cte

这项技术从顶部开始向下,一直向下,直到找不到更多匹配。

;with CTE As
(
    select 1 as stdId, 1 as dt
        union
    select 1 as stdId, 2 as dt
        union
    select 1 as stdId, 3 as dt
        union
    select 1 as stdId, 5 as dt
        union
    select 1 as stdId, 6 as dt
        union
    select 1 as stdId, 7 as dt
        union
    select 1 as stdId, 9 as dt
        union
    select 2 as stdId, 2 as dt
        union
    select 2 as stdId, 4 as dt
        union
    select 2 as stdId, 7 as dt
        union
    select 2 as stdId, 8 as dt
),
CTEr AS 
(
    -- Use recursiion, starting at the top and working down by one.

        -- Anchor part.
        SELECT
            stdId,
            MAX(dt) AS dt
        FROM
            CTE
        GROUP BY 
            stdId

    UNION ALL

        -- Recursive part.
        SELECT
            r.stdId,
            r.dt -1 AS dt
        FROM
            CTEr AS r
                INNER JOIN CTE AS c     ON  c.stdId = r.stdId
                                        AND c.dt    = r.dt -1
)
select 
    stdId, 
    COUNT(dt) AS Result
FROM 
    CTEr
GROUP BY
    stdId
;

使用递归时性能可能是一个问题,尤其是对于较大的数据集。如果是这种情况,我建议使用@ vkp的方法。

答案 2 :(得分:0)

如果你想要1为学生1(&#34; 9&#34;)和&#34; 2&#34;对于学生2(&#34; 7&#34;和&#34; 8&#34;)然后:

select top (1) with ties t.*
from (select cte.*,
             row_number() over (partition by stdid order by date) as seqnum
      from cte
      where student = @student
     ) t
order by (dt - seqnum) desc;

这是做什么的?首先,它为每个学生生成一个序列号。这与dt列之间的差异是&#34; dt&#34;的连续值的常量。

然后,它只是按差异排序并获得具有最大差异的所有行。这些是最后的连续行。

这确实有两个假设:

  • 您希望序列基于每个学生的dt的最高值,而不是某个总体最高值。
  • 表中没有重复项。

这些是基于问题和问题数据的合理假设。处理其他情况有很简单的替代方案。

最后,请注意,如果您的数据看起来更像图片而不是CTE,那么转换它的简单方法是:

select top (1) with ties t.*
from (select cte.*,
             row_number() over (partition by stdid order by date) as seqnum
      from t cross apply
           (values (t.[1]), (t.[2]), . . . ) v(dt)
      where student = @student and v.dt = 'yes'
     ) t
order by (dt - seqnum) desc;