SQL 函数到 运行 一个在标志改变时增加的计数器?

SQL function to run a counter that increases when a flag changes?

我有看起来像前三列的数据,我想为 Cycle 添加第四列,当 Flag 为零时它为空,否则计算给定 x 的该标志的出现次数, 按日期排序。我正在使用 db2,但不确定如何修改 row_number() 函数来获得此结果。

  name  date           Flag        Cycle
    x     2014            0          Null
    x     2015            0          Null
    x     2016            1          1
    x     2017            1          1
    x     2018            0          Null
    x     2019            1          2
    x     2020            0          Null
    x     2021            1          3 
    y     2014            0          Null
    y     2016            1          1
    y     2017            0          Null
    y     2018            1          2

我正在寻找行为类似于 Rank 函数的东西,但允许基于分区语句中的重复项而不是排序依据进行并列排名。

您可以使用 lag() 和 window sum():

select
    name,
    date,
    flag,
    case when flag = 1 
        then sum(cycle_start) over(partition by name order by date) 
    end cycle 
from (
    select
        t.*,
        case
            when lag(flag) over(partition by name order by date) = 0 and flag = 1
            then 1
        end cycle_start
    from mytable t
) t
order by name, date

内部查询检测到循环开始是从 01 的过渡。外部查询对循环开始进行 window 求和,并仅在标志打开时显示它。

Demo on DB Fiddlde:

NAME | DATE | FLAG | CYCLE
---: | ---: | ---: | ----:
   x | 2014 |    0 |  null
   x | 2015 |    0 |  null
   x | 2016 |    1 |     1
   x | 2017 |    1 |     1
   x | 2018 |    0 |  null
   x | 2019 |    1 |     2
   x | 2020 |    0 |  null
   x | 2021 |    1 |     3
   y | 2014 |    0 |  null
   y | 2016 |    1 |     1
   y | 2017 |    0 |  null
   y | 2018 |    1 |     2

好像是一个累计数:

select t.*,
       (case when flag = 1
             then sum(flag) over (partition by name order by date)
        end) as cycle_start
from t;

这假设标志仅采用值 01,如示例数据中所示。如果标志采用其他非 0 值,则可以轻松修改它。

编辑:

根据您的评论,我误解了问题。您想要识别相邻的 "groups" 条记录。为此,使用先前 NULL 值的数量定义一个组,然后进行累加和:

select t.*,
       (case when flag = 1
             then dense_rank() over (partition by name order by grp)
        end) as cycle_start
from (select t.*,
             sum(1 - flag) over (partition by name order by date) as grp
      from t
     ) t