【问题标题】:Running "Group By" Ordinal Counter Based on a "Flip" Column基于“翻转”列运行“分组依据”序数计数器
【发布时间】:2015-04-27 01:59:21
【问题描述】:

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

我有以下几行。 MyOrdinal 将“按顺序”...并且第二列(MyMarker)将在有值和为空之间交替。每当在 MyMarker 上发生这种“翻转”时,我想将“分组依据”序数计数器加一。只要“翻转”值不为空或为空,它们就会组合在一起作为一个集合。

我已经尝试了几件事,但它太丑了,无法发布。自从转向 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 |

【问题讨论】:

    标签: sql-server sql-server-2008-r2 sql-server-2012


    【解决方案1】:

    试试这个:

    首先,这为连续的非NULL MyMarker 分配相同的ROW_NUMBERROW_NUMBERNULL 对于 NULL MyMarkers。之后,您想为NULL MyMarkers 添加一个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
    

    【讨论】:

      【解决方案2】:

      对于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。然后运行所有行的总和直到当前。 小提琴http://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
      

      小提琴http://sqlfiddle.com/#!6/4191d/6

      【讨论】:

      • 我喜欢在这里使用 LAG .....并希望我能使用它。我的 2008 R2 要求意味着我不能使用这个(这个到处走),但这进入了技巧包。谢谢。 Upvote.. 但由于 2008 R2 的要求,我会将另一个标记为答案。
      猜你喜欢
      • 1970-01-01
      • 2021-11-04
      • 1970-01-01
      • 2023-03-13
      • 2021-12-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多