运行 "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_NUMBER
。 ROW_NUMBER
是 NULL
NULL
MyMarker
秒。之后,你想为 NULL
MyMarker
添加一个 ROW_NUMBER
,使得值介于前一个 NON-NULL
和下一个 NON-NULL
之间.然后用DENSE_RANK
最后赋值MyGroupNumber
:
;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 标记从 null
到 not null
或从 not null
到 null
的行。其他列标记为 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
通常我擅长处理基于集合的 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_NUMBER
。 ROW_NUMBER
是 NULL
NULL
MyMarker
秒。之后,你想为 NULL
MyMarker
添加一个 ROW_NUMBER
,使得值介于前一个 NON-NULL
和下一个 NON-NULL
之间.然后用DENSE_RANK
最后赋值MyGroupNumber
:
;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 标记从 null
到 not null
或从 not null
到 null
的行。其他列标记为 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