运行 "Group By" 基于 "Flip" 列的序数计数器

Running "Group By" Ordinal Counter Based on a "Flip" Column

通常我擅长处理基于集合的 tsql 问题。但是这个打败了我。 我已经工作了 3 天,将 while-loop 过程转换为基于 set 的过程。我已经到了下面的点.......但是无法进行最后的跳跃。

我有以下几行。 MyOrdinal 将为 "in order" ...,第二列 (MyMarker) 将在具有值和为空之间交替。每当 MyMarker 上出现此 "flip" 时,我想将 "group by" 序数计数器加一。每当 "flip" 值非空或空时,这些值将组合在一起作为一个集合。

我试过好几样东西,但都太丑了 post。自从转向 ORM 后,我不再花那么多时间在 tsql 上了。

declare @Holder table (   MyOrdinal int not null , MyMarker int , MyGroupNumber int   )

INSERT INTO @Holder (MyOrdinal, MyMarker)
Select 1 , 1 
union all Select 2, 2
union all Select 3, null
union all Select 4, 3
union all Select 5, 4
union all Select 6, 5
union all Select 7, 6
union all Select 8, 7
union all Select 9, 8
union all Select 10, 9
union all Select 11, 10
union all Select 12, 11
union all Select 13, 12
union all Select 14, 13
union all Select 15, 14
union all Select 16, 15
union all Select 17, null
union all Select 18, null
union all Select 19, null
union all Select 20, 16
union all Select 21, 17
union all Select 22, 18
union all Select 23, null
union all Select 24, null
union all Select 25, 19
union all Select 26, 20
union all Select 27, null
union all Select 28, 21

Select * from @Holder

期望的输出

| MyOrdinal | MyMarker | MyGroupNumber |
|-----------|----------|---------------|
|         1 |        1 |             1 |
|         2 |        2 |             1 |
|         3 |    null  |             2 |
|         4 |        3 |             3 |
|         5 |        4 |             3 |
|         6 |        5 |             3 |
|         7 |        6 |             3 |
|         8 |        7 |             3 |
|         9 |        8 |             3 |
|        10 |        9 |             3 |
|        11 |       10 |             3 |
|        12 |       11 |             3 |
|        13 |       12 |             3 |
|        14 |       13 |             3 |
|        15 |       14 |             3 |
|        16 |       15 |             3 |
|        17 |    null  |             4 |
|        18 |    null  |             4 |
|        19 |    null  |             4 |
|        20 |       16 |             5 |
|        21 |       17 |             5 |
|        22 |       18 |             5 |
|        23 |    null  |             6 |
|        24 |    null  |             6 |
|        25 |       19 |             7 |
|        26 |       20 |             7 |
|        27 |    null  |             8 |
|        28 |       21 |             9 |

试试这个:

首先,这为连续的非NULL MyMarker分配了相同的ROW_NUMBERROW_NUMBERNULL NULL MyMarker 秒。之后,你想为 NULL MyMarker 添加一个 ROW_NUMBER,使得值介于前一个 NON-NULL 和下一个 NON-NULL 之间.然后用DENSE_RANK最后赋值MyGroupNumber:

SQL Fiddle

;WITH Cte AS(
    SELECT *,
        RN = ROW_NUMBER() OVER(ORDER BY MyOrdinal) - MyMarker + 1
    FROM @Holder
),
CteApply AS(
    SELECT
        t.MyOrdinal,
        t.MyMarker,
        MyGroupNumber = 
            CASE
                WHEN RN IS NULL THEN x.NewRN
                ELSE RN
            END
    FROM Cte t
    OUTER APPLY(
        SELECT TOP 1 RN * 1.1 AS NewRN
        FROM Cte
        WHERE 
            t.MyOrdinal > MyOrdinal
            AND MyMarker IS NOT NULL
        ORDER BY MyOrdinal DESC
    )x
)
SELECT 
    MyOrdinal,
    MyMarker,
    MyGroupNumber = DENSE_RANK() OVER(ORDER BY MyGroupNumber)
FROM CteApply

对于Sql Server 2012

select *, sum(b) over(order by myordinal) 
from(select *,  
            case when (lag(mymarker) over(order by myordinal) is not null 
                   and mymarker is null) or 
                   (lag(mymarker) over(order by myordinal) is null 
                   and mymarker is not null)
            then 1 else 0 end as b
from @Holder) t

首先,您用 1 标记从 nullnot null 或从 not nullnull 的行。其他列标记为 0。然后 运行 所有行的总和直到当前。 Fiddlehttp://sqlfiddle.com/#!6/9eecb/5015

对于 Sql Server 2008:

with cte1 as (select *,
case when (select max(enddate) from t ti
           where ti.ruleid = t.ruleid and ti.startdate < t.startdate) = startdate 
           then 0 else 1 end as b
from t),
cte2 as(select *, sum(b) over(partition by ruleid order by startdate) as s
from cte1)
select RuleID, 
       Name, 
       min(startdate), 
       case when count(*) = count(enddate) 
            then max(enddate) else null end from cte2
group by s, ruleid, name

Fiddle http://sqlfiddle.com/#!6/4191d/6