有条件地在数据周期内递增字段值 - 有没有一种方法只有SQL?

时间:2015-06-12 05:24:02

标签: sql oracle plsql window-functions

有没有办法根据数据中的周期获得条件增量,并在查询字段中将其前移?这适用于Oracle,11G2。

我有个人的月度活动数据(40多年),跟踪会员的变化。个人可以加入服务,无限期保留,退出并稍后重新加入。这些更改具有离开服务时的特定代码。我试图找到一种方法来跟踪每个人的这些退出/重新加入周期,并增加一个列值,显示他们在服务中的次数,并在此期间关联历史记录。

使用分析功能,我可以检测到他们的第一次出现,他们何时离开,以及何时他们在离开后重新进入。我还没有找到一种方法来使用它来增加一个列并将该值传递到该个人的最后。

这不是一个序列,因为每个人都从" 1"开始。对于我尝试过的逻辑/分区组合,行号对我来说并不起作用。我已经尝试使用CASE语句对更改代码进行子查询,然后使用LAST_VALUE在外部查询中进行转发 - 但我还没有找到合适的方法增加或推进它。我只是没有得到它。

我已经用我刚开始的核心查询来摆弄这个问题 http://sqlfiddle.com/#!4/65d49/1/0

select recno, uniq, 
   row_number() over (PARTITION by uniq 
                      order by sym , mchty ) histrec,
   sym, 
   mchty, 
   lag( mchty, 1, '99')  over ( PARTITION by uniq 
                                  order by sym ) premchty, 
   (case
      when lag( mchty, 1, '99') over ( PARTITION by uniq 
                                       order by sym  ) = '99'
          then 1
      end ) join_svc,
   (case
      when  lag( mchty, 1, '99') over ( PARTITION by uniq 
                                        order by sym  ) = '6'
          then 1
      end ) rejoin_svc,
      svc svc_num
 from demo_history ;
  • RecNo - 源表中的记录号。

    UNIQ - 个人的唯一标识符。

    SYM - 更改日期,ALPHANUMERIC日期字符串,' YYYYMM' - 我在其他代码中处理了一些无效的月份。

    MCHTY - 活动更改代码。活动代码可以是' 0' 0到' 6', 用' 6'作为"离开服务"指示器。

记录按更改日期排序,然后按更改类型排序。

在示例查询结果中,

  • HISTREC - 单个历史记录的行号

    PREMCHTY - 最新的更改代码(滞后)

    JOIN_SVC - 集合中的第一条记录

    REJOIN_SVC - 离开后的第一张记录

    SVC(或示例中的SVC_NUM)是我尝试生成的内容 - "第n"我觉得应该是服务的时间。

如何查询/计算/生成SVC字段的内容,在个人离开(' 6')后的每个新时间段内递增它?

最终,每个人​​在服务中时,将使用唯一ID和递增的svc数组合来创建主记录。

背景:我试图替换PL / 1程序日遗留下来的大量程序代码和逻辑,但是"更新了"将它放在带有多嵌套游标的PL / SQL过程中,并通过IN / OUT参数在一组记录中共享字段值。
数据大小约为5百万条记录,约270k个人ID。我希望将个人的历史记录作为一个集合来处理,使用SQL来替换大多数字段转换。如果我接近这个错误,或者有更好的方法,请告诉我。

2 个答案:

答案 0 :(得分:2)

您接近解决方案。您只需要在rejoin_svc列上使用SUM作为分析函数。但这会给你svc号码 从0开始。所以,只需添加1。

select recno,
uniq,
sym,
mchty,
sum(rejoin_svc) over (PARTITION by uniq order by sym) + 1 svc_num,
svc
from
(
  select recno, uniq, 
       sym, 
       mchty, 
       (case
          when  lag( mchty, 1, '99') over ( PARTITION by uniq 
                                            order by sym  ) = '6'
              then 1
              else 0
          end ) rejoin_svc,
        svc
from demo_history
)
order by recno, uniq, svc;

sqlfiddle

答案 1 :(得分:1)

递归解决方案:

with t as (
    select recno, uniq, sym, mchty, svc, 
        row_number() over (partition by uniq order by sym, recno) rn
      from demo_history),
  u (recno, uniq, sym, mchty, rn, svc, new_svc) as (
    select recno, uniq, sym, mchty, rn, svc, 1 new_svc from t where rn = 1
    union all 
    select t.recno, t.uniq, t.sym, t.mchty, t.rn, t.svc, 
        case when u.mchty= '6' then u.new_svc+1 else u.new_svc end
      from t join u on t.uniq = u.uniq and t.rn = u.rn+1 )
select recno, uniq, sym, mchty, rn, svc, new_svc
  from u order by uniq, recno

SQLFiddle

用户@EatÅPeach给出的答案可能就是你应该在这里使用的,它很快并且适合你的需要。

但是还有其他可能值得一提和回答你的问题:有没有办法根据数据中的周期获得条件增量, 并在查询字段中继续前进? - 在Oracle 11g中引入recursive CTE

主要部分是子查询u为每个uniq合并第一行,并使用row_number循环下一行。对于每一行,我要检查mchty的上一个值是否为'6',如果是,则递增new_svc