生成介于开始日期和结束日期之间的日期
Posted
技术标签:
【中文标题】生成介于开始日期和结束日期之间的日期【英文标题】:Generate the dates in between Start and End Dates 【发布时间】:2016-02-11 06:17:46 【问题描述】:我需要生成两个给定日期之间的所有日期。这是问题陈述:
输入:
START_DATE END_DATE
---------- ----------
01-FEB-16 03-FEB-16
01-FEB-16 04-FEB-16
01-FEB-16 05-FEB-16
01-FEB-16 03-FEB-16
11-FEB-16 14-FEB-16
输出(开始日期和结束日期之间的所有日期):
BETWEEN_START_AND_END
----------------------
01-FEB-16
02-FEB-16
03-FEB-16
04-FEB-16
05-FEB-16
11-FEB-16
12-FEB-16
13-FEB-16
14-FEB-16
【问题讨论】:
【参考方案1】:试试;
WITH date_tbl AS ( --get (max end_date diff) group by START_DATE
SELECT
Trunc(START_DATE) Min_date,
max(Trunc(END_DATE)) - Trunc(START_DATE) diff
FROM tbl
GROUP BY Trunc(START_DATE)
),
num_tbl AS ( --data to join the table [0 , 1, 2, 3 ..... max(diff) + 1]
SELECT LEVEL - 1 lev
FROM dual
CONNECT BY LEVEL <= (SELECT Max(diff) + 1 FROM date_tbl)
)
SELECT DISTINCT Min_date + lev date_col --adding level to get all date
FROM num_tbl JOIN date_tbl
ON lev <= diff
ORDER BY Min_date + lev
演示
with tbl(start_date, end_date) as (
select Date '2016-02-01', Date '2016-02-03' from dual union all
select Date '2016-02-01', Date '2016-02-04' from dual union all
select Date '2016-02-01', Date '2016-02-05' from dual union all
select Date '2016-02-01', Date '2016-02-03' from dual union all
select Date '2016-02-11', Date '2016-02-14' from dual
),
date_tbl AS ( --get max end_date group by START_DATE
SELECT
Trunc(START_DATE) Min_date,
max(Trunc(END_DATE)) - Trunc(START_DATE) diff
FROM tbl
GROUP BY Trunc(START_DATE)
),
num_tbl AS ( --data to join the table [0 ,1 , 2, 3 ..... max(diff) + 1]
SELECT LEVEL - 1 lev
FROM dual
CONNECT BY LEVEL <= (SELECT Max(diff) + 1 FROM date_tbl)
)
SELECT DISTINCT Min_date + lev date_col
FROM num_tbl JOIN date_tbl
ON lev <= diff
ORDER BY Min_date + lev
输出
DATE_COL
01.02.2016
02.02.2016
03.02.2016
04.02.2016
05.02.2016
11.02.2016
12.02.2016
13.02.2016
14.02.2016
【讨论】:
输出只有01-FEB-16 02-FEB-16 03-FEB-16 04-FEB-16 05-FEB-16【参考方案2】:查询:
with y(start_date, end_date, m) as (
select start_date, end_date, case when start_date > lag(end_date) over (order by start_date, end_date) then 1 else 0 end
from your_table
)
,z(start_date, end_date, group_number) as (
select start_date, end_date, sum(m) over (order by start_date, end_date) from y
)
,a(start_date, end_date, group_number) as (
select min(start_date), max(end_date), group_number
from z
group by group_number
)
select start_date + level - 1
from a
connect by start_date + level - 1 <= end_date
and prior group_number = group_number
and prior SYS_GUID() is not null
order by 1;
例子:
with x(start_date, end_date) as (
select date'2016-02-01', date'2016-02-03' from dual union all
select date'2016-02-01', date'2016-02-05' from dual union all
select date'2016-02-01', date'2016-02-03' from dual union all
select date'2016-02-11', date'2016-02-14' from dual union all
select date'2016-02-02', date'2016-02-07' from dual union all
select date'2016-02-02', date'2016-02-05' from dual union all
select date'2016-02-12', date'2016-02-15' from dual union all
select date'2016-02-11', date'2016-02-17' from dual union all
select date'2016-02-19', date'2016-02-21' from dual
)
,y(start_date, end_date, m) as (
select start_date, end_date, case when start_date > lag(end_date) over (order by start_date, end_date) then 1 else 0 end
from x
)
,z(start_date, end_date, group_number) as (
select start_date, end_date, sum(m) over (order by start_date, end_date) from y
)
,a(start_date, end_date, group_number) as (
select min(start_date), max(end_date), group_number
from z
group by group_number
)
select start_date + level - 1
from a
connect by start_date + level - 1 <= end_date
and prior group_number = group_number
and prior SYS_GUID() is not null
order by 1;
结果:
start_date + level - 1
----------------------
01-FEB-16
02-FEB-16
03-FEB-16
04-FEB-16
05-FEB-16
06-FEB-16
07-FEB-16
11-FEB-16
12-FEB-16
13-FEB-16
14-FEB-16
15-FEB-16
16-FEB-16
17-FEB-16
19-FEB-16
20-FEB-16
21-FEB-16
【讨论】:
【参考方案3】:为了达到最佳性能,最好合并重叠间隔,然后在这些间隔的开始日期和结束日期中为每一天生成一行。以下查询向您展示了如何执行此操作。
SQL> with intervals as (
2 select 1 as interval_id,
3 to_date('01-FEB-16','dd-mon-yy') as start_date,
4 to_date('03-FEB-16','dd-mon-yy') as end_date
5 from dual
6 union all
7 select 2 as interval_id,
8 to_date('02-FEB-16','dd-mon-yy') as start_date,
9 to_date('04-FEB-16','dd-mon-yy') as end_date
10 from dual
11 union all
12 select 3 as interval_id,
13 to_date('03-FEB-16','dd-mon-yy') as start_date,
14 to_date('05-FEB-16','dd-mon-yy') as end_date
15 from dual
16 union all
17 select 4 as interval_id,
18 to_date('01-FEB-16','dd-mon-yy') as start_date,
19 to_date('04-FEB-16','dd-mon-yy') as end_date
20 from dual
21 union all
22 select 5 as interval_id,
23 to_date('11-FEB-16','dd-mon-yy') as start_date,
24 to_date('14-FEB-16','dd-mon-yy') as end_date
25 from dual
26 ), overlapping_intervals as (
27 select case
28 when prev_end is null
29 then idx
30 when start_date <= prev_end
31 then idx - lag(idx) over (order by start_date, interval_id)
32 else -idx
33 end as overlap_interval_id,
34 t.*
35 from (
36 select row_number() over (order by start_date, interval_id) as idx,
37 lag(end_date) over (order by start_date, interval_id) as prev_end,
38 i.*
39 from intervals i
40 ) t
41 order by start_date, interval_id
42 ), non_overlapping_intervals as (
43 select overlap_interval_id,
44 min(start_date) as start_date,
45 max(end_date) as end_date
46 from overlapping_intervals
47 group by overlap_interval_id
48 )
49 select /*+rule*/start_date + level-1 as day, i.*
50 from non_overlapping_intervals i
51 connect by overlap_interval_id = prior overlap_interval_id
52 and level < end_date-start_date+1
53 and prior dbms_random.value is not null -- <-- workaround to bypass ORA-01436 thrown by the DB
54 order by day
55 /
DAY OVERLAP_INTERVAL_ID START_DATE END_DATE
----------- ------------------- ----------- -----------
01/02/2016 1 01/02/2016 05/02/2016
02/02/2016 1 01/02/2016 05/02/2016
03/02/2016 1 01/02/2016 05/02/2016
04/02/2016 1 01/02/2016 05/02/2016
11/02/2016 -5 11/02/2016 14/02/2016
12/02/2016 -5 11/02/2016 14/02/2016
13/02/2016 -5 11/02/2016 14/02/2016
7 rows selected
SQL>
【讨论】:
【参考方案4】:试试这个...我让您更改日期格式以适合您的...
with tbl(start_date,
end_date) as
(select date '2016-02-01'
,date '2016-02-03'
from dual
union all
select date '2016-02-01'
,date '2016-02-04'
from dual
union all
select date '2016-02-01'
,date '2016-02-05'
from dual
union all
select date '2016-02-01'
,date '2016-02-03'
from dual
union all
select date '2016-02-11'
,date '2016-02-14'
from dual)
select distinct start_date + level - 1
from tbl
connect by start_date + level - 1 <= end_date
order by 1;
会输出
01/02/2016
02/02/2016
03/02/2016
04/02/2016
05/02/2016
11/02/2016
12/02/2016
13/02/2016
14/02/2016
【讨论】:
以上是关于生成介于开始日期和结束日期之间的日期的主要内容,如果未能解决你的问题,请参考以下文章
如何选择实体的计数,其中时间序列日期介于该实体的开始日期和结束日期之间
在插入 SQLite 之前检查表 B 中的日期是不是介于表 A 中的日期之间
如何在 BigQuery 的开始日期和结束日期之间复制生成日期的行?
如何使用 pandas.date_range() 在指定的开始日期和结束日期之间获取具有 n 个指定周期(相等)的时间序列