对不起标题......最好用一个例子描述问题...
我有一个事件列表和每个事件的两个日期,我需要在各自的几个月内“中断”或“分发”这些日期。
示例1:
活动:活动A
开始日期:12/15/2017 - MM / DD / YYYY
结束日期:01/17/2018 - MM / DD / YYYY
如果我在我的桌子上搜索此事件,我会得到一个包含该数据的结果行。
但我需要两个结果,如下所示:
结果1:事件A> 15至31
结果2:事件A> 01至17
示例2:
活动:活动B
开始日期:02/07/2018 - MM / DD / YYYY
结束日期:04/22/2018 - MM / DD / YYYY
结果1:事件B> 07至28
结果2:事件B> 01至31
结果3:事件B> 01至22
最有效的方法是什么?
答案 0 :(得分:1)
我没有完整的解决方案(如果你为它创建一个可行的SQLFiddle测试平台,我可能会解决它),但我认为这是需要一个CONNECT BY子句的东西,它会是非常接近this solution from Ask Tom。
它基本上是这样的(来自Ask Tom的例子):
variable sdate varchar2(30);
variable edate varchar2(30);
exec :sdate := '01-mar-2011'; :edate := '31-dec-2011';
select level r,
greatest( add_months(trunc(sdate,'mm'),level-1), sdate ),
least( last_day( add_months(sdate,level-1) ), edate )
from (select to_date( :sdate, 'dd-mon-yyyy' ) sdate,
to_date( :edate, 'dd-mon-yyyy' ) edate
from dual)
connect by level <= months_between( trunc( edate,'mm'), trunc(sdate,'mm') ) + 1;
R GREATEST( LEAST(LAS
------ --------- ---------
1 01-MAR-11 31-MAR-11
2 01-APR-11 30-APR-11
3 01-MAY-11 31-MAY-11
4 01-JUN-11 30-JUN-11
5 01-JUL-11 31-JUL-11
6 01-AUG-11 31-AUG-11
7 01-SEP-11 30-SEP-11
8 01-OCT-11 31-OCT-11
9 01-NOV-11 30-NOV-11
10 01-DEC-11 31-DEC-11
10 rows selected.
答案 1 :(得分:1)
可以使用Oracle 12c cross apply
子句:
create table e_vents(
name varchar2(10),
startdate date,
enddate date
);
insert all
into e_vents values( 'A', date '2017-12-15', date '2018-01-17' )
into e_vents values( 'B', date '2017-12-15', date '2017-12-22' )
into e_vents values( 'C', date '2017-12-15', date '2018-05-22' )
select null from dual;
commit;
select e.name,
case when e.startdate > x.s_date then e.startdate else x.s_date end as start_date,
case when e.enddate < x.e_date then e.enddate else x.e_date end as end_date
from e_vents e
cross apply (
select
trunc( e.startdate, 'mm') + (level-1) * interval '1' month as s_date,
trunc( e.startdate + (level) * interval '1' month, 'mm') -1 as e_date
from dual
connect by level <= months_between( trunc( e.enddate, 'mm'),trunc( e.startdate, 'mm')) + 1
) x
NAME START_DATE END_DATE
---------- ---------- ----------
A 2017-12-15 2017-12-31
A 2018-01-01 2018-01-17
B 2017-12-15 2017-12-22
C 2017-12-15 2017-12-31
C 2018-01-01 2018-01-31
C 2018-02-01 2018-02-28
C 2018-03-01 2018-03-31
C 2018-04-01 2018-04-30
C 2018-05-01 2018-05-22
9 rows selected.
答案 2 :(得分:1)
这个问题可以使用两种解决方案
在Oracle 12C中,您可以使用以下查询
SELECT DISTINCT e.name,
CASE
WHEN e.startdate > x.sdate
THEN e.startdate
ELSE x.sdate
END AS startdate,
CASE
WHEN e.enddate < x.edate
THEN e.enddate
ELSE x.edate
END AS enddate
FROM e_vents e CROSS apply
(SELECT TRUNC( e.startdate, 'mm') + (level-1) * interval '1' MONTH AS sdate,
TRUNC( e.startdate + (level) * interval '1' MONTH, 'mm') -1 AS edate
FROM e_vents
CONNECT BY level <= months_between( TRUNC( e.enddate, 'mm'),TRUNC( e.startdate, 'mm')) + 1
) x
ORDER BY 1 ASC;
旧版本的oracle中的使用以下查询
SELECT e.name,
greatest(e.startdate,x.sdate) AS startdate,
least(e.enddate,x.edate) AS enddate
FROM e_vents e,
(SELECT TRUNC( e.min_startdate, 'mm') + (level-1) * interval '1' MONTH AS sdate,
TRUNC( e.min_startdate + (level) * interval '1' MONTH, 'mm') -1 AS edate
FROM
(SELECT MIN(startdate) min_startdate,MAX(enddate) max_enddate FROM e_vents
) e
CONNECT BY level<= months_between( TRUNC( e.max_enddate, 'mm'),TRUNC( e.min_startdate, 'mm')) + 1
) x
WHERE e.startdate BETWEEN x.sdate AND x.edate
OR e.enddate BETWEEN x.sdate AND x.edate
OR x.sdate BETWEEN e.startdate AND e.enddate
OR x.edate BETWEEN e.startdate AND e.enddate
ORDER BY 1 ASC ;