使用oracle中的分析函数查找月份差异
Finding difference in months using analytical function in oracle
我有如下数据。在这里我需要找到他们月份的差异应该> = 6的行。逻辑应该是,我们需要从第一行进行比较直到我们得到月份差异为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
如有任何帮助,我们将不胜感激!!!
递归求解:
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
解释:
子查询tmp
只为每个讲座和学生分别枚举行:
select fromdate fd, lectureid lid, studentid sid, null mb,
row_number() over (partition by lectureid, studentid order by fromdate) rn
这一行是递归子查询CTE
的"anchor",第一步取两行编号为1的行
select fd、fd、lid、sid、mb、rn 来自 tmp 其中 rn=1
在这一步中,我使用条件 tmp.rn = cte.rn+1
附加 "recursive member" 下面的部分特别重要,这里我检查上次记住的日期和日期之间是否有六个月当前行:
case when floor(months_between(tmp.fd, cte.ld)) >= 6 then tmp.fd else cte.ld end
最后 select
是语法的必需部分。
一些有用的链接:
我有如下数据。在这里我需要找到他们月份的差异应该> = 6的行。逻辑应该是,我们需要从第一行进行比较直到我们得到月份差异为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
如有任何帮助,我们将不胜感激!!!
递归求解:
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
解释:
子查询
tmp
只为每个讲座和学生分别枚举行:select fromdate fd, lectureid lid, studentid sid, null mb, row_number() over (partition by lectureid, studentid order by fromdate) rn
这一行是递归子查询
CTE
的"anchor",第一步取两行编号为1的行select fd、fd、lid、sid、mb、rn 来自 tmp 其中 rn=1
在这一步中,我使用条件
tmp.rn = cte.rn+1
附加 "recursive member" 下面的部分特别重要,这里我检查上次记住的日期和日期之间是否有六个月当前行:case when floor(months_between(tmp.fd, cte.ld)) >= 6 then tmp.fd else cte.ld end
最后
select
是语法的必需部分。
一些有用的链接: