使用oracle中的分析函数查找月份差异

时间:2015-12-31 11:56:00

标签: sql oracle oracle11g lag lead

我有如下数据。在这里,我需要找到其中几个月的差异应该是> = 6的行。逻辑应该是,我们需要从第一行比较直到我们得到的行,其中diff在几个月是6,然后需要遵循相同的来自新匹配行的逻辑继续运行。

我在下面添加了我的示例数据和预期输出。

Fromdate    LectureiD   StudentID   Diff Months Expected
1-Oct-13    1102          55586         null
15-Oct-13   1102          55586          0
15-Oct-13   1102          55586          0
4-Apr-14    1102          55586          6
19-Dec-14   1102          55586          8
27-Dec-14   1102          55586          0
14-Jan-15   1102          55586          0
14-Jan-15   1102          55586          0
29-Sep-15   1102          55586          8
1-Oct-13    1102          55557          null
15-Oct-13   1102          55557          0
15-Oct-13   1102          55557          0
4-Apr-14    1102          55557          6
19-Dec-14   1102          55557          8

以下是我在oracle中使用分析函数尝试的逻辑。

select lectureid, 
       studentid,
       floor(months_between(fromdate, 
                            lag(fromdate) over (partition by
                                                   lectureid, 
                                                   studentid 
                                                order by fromdate
                                               )
                           )
            ) monthdiff 
 from above_table;

由于滞后函数的默认偏移量为1,它只是在此之前检查行,因为我上面提到的逻辑在这里工作不正常,因为需要根据新的动态检查它们之前的行。匹配的行。所以o / p到达这里如下。

用星号突出显示的行因此而出错,因为它仅与其前一行进行比较。

Fromdate    LectureiD   StudentID   Diff Month
1-Oct-13    1102           55586        null
15-Oct-13   1102           55586        0
15-Oct-13   1102           55586        0
*4-Apr-14   1102           55586        5*
19-Dec-14   1102           55586        8
27-Dec-14   1102           55586        0
14-Jan-15   1102           55586        0
14-Jan-15   1102           55586        0
29-Sep-15   1102           55586        8
1-Oct-13    1102           55557        null
15-Oct-13   1102           55557        0
15-Oct-13   1102           55557        0
*4-Apr-14   1102           55557        5*
19-Dec-14   1102           55557        8

非常感谢任何帮助!!!

1 个答案:

答案 0 :(得分:2)

递归解决方案:

with tmp as (
  select fromdate fd, lectureid lid, studentid sid, null mb,
         row_number() over (partition by lectureid, studentid order by fromdate) rn
    from above_table ),
cte (fd, ld, lid, sid, mb, rn) as (
  select fd, fd, lid, sid, mb, rn from tmp where rn=1
  union all
  select tmp.fd, case when floor(months_between(tmp.fd, cte.ld)) >= 6 
                      then tmp.fd else cte.ld end,
         tmp.lid, tmp.sid, floor(months_between(tmp.fd, cte.ld)), tmp.rn
    from tmp join cte on tmp.lid = cte.lid and tmp.sid = cte.sid and tmp.rn = cte.rn+1)
select to_char(fd, 'yyyy-mm-dd') fromdate, lid lecture, sid student, mb 
  from cte order by sid desc, fd

测试数据和输出:

create table above_table (Fromdate date, LectureiD number(6), StudentID number(6), Diff number(4));
insert into above_table values (date '2013-10-01', 1102, 55586, null);
insert into above_table values (date '2013-10-15', 1102, 55586, 0);
insert into above_table values (date '2013-10-15', 1102, 55586, 0);
insert into above_table values (date '2014-04-04', 1102, 55586, 6);
insert into above_table values (date '2014-12-19', 1102, 55586, 8);
insert into above_table values (date '2014-12-27', 1102, 55586, 0);
insert into above_table values (date '2015-01-14', 1102, 55586, 0);
insert into above_table values (date '2015-01-14', 1102, 55586, 0);
insert into above_table values (date '2015-09-29', 1102, 55586, 8);
insert into above_table values (date '2013-10-01', 1102, 55557, null);
insert into above_table values (date '2013-10-15', 1102, 55557, 0);
insert into above_table values (date '2013-10-15', 1102, 55557, 0);
insert into above_table values (date '2013-10-29', 1102, 55557, 0);
insert into above_table values (date '2014-04-04', 1102, 55557, 6);
insert into above_table values (date '2014-12-19', 1102, 55557, 8);

FROMDATE      LECTURE    STUDENT         MB
---------- ---------- ---------- ----------
2013-10-01       1102      55586            
2013-10-15       1102      55586          0 
2013-10-15       1102      55586          0 
2014-04-04       1102      55586          6 
2014-12-19       1102      55586          8 
2014-12-27       1102      55586          0 
2015-01-14       1102      55586          0 
2015-01-14       1102      55586          0 
2015-09-29       1102      55586          9 
2013-10-01       1102      55557            
2013-10-15       1102      55557          0 
2013-10-15       1102      55557          0 
2013-10-29       1102      55557          0 
2014-04-04       1102      55557          6 
2014-12-19       1102      55557          8

说明:

  1. 子查询tmp仅为每个讲座和学生分别枚举行:

    选择fromdate fd,讲话盖子,studentid sid,null mb,        row_number()结束(按课程划分,学生按订单生成)rn

  2. 这一行是"锚"递归子查询CTE,其中我们在第一步中将两行编号为1

    从tmp中选择fd,fd,lid,sid,mb,rn,其中rn = 1

  3. 在这一步中,我附加了"递归成员"使用条件tmp.rn = cte.rn+1特别重要的是下面的部分,在这里我要检查最后一次记住的日期和当前行的日期之间是否有六个月:

    楼层的情况(months_between(tmp.fd,cte.ld))> = 6然后tmp.fd else cte.ld end

  4. 最后select是语法的一部分。

  5. 一些有用的链接: