我有下表:
ID START_DATE END_DATE
1 01.06.2015 20.06.2015
2 05.06.2015 25.06.2015
3 03.06.2015 10.06.2015
4 07.06.2015 23.06.2015
5 21.06.2015 30.06.2015
6 02.06.2015 10.06.2015
7 05.06.2015 15.06.2015
8 05.06.2015 08.06.2015
9 16.06.2015 20.06.2015
表格中有重叠的时间表。例如,03/06/2015-10/06/2015
位于1/06/2015-20/06/2015
之间。事实上,非重叠时间轴是1/06/2015
,05/06/2015
和21/06/2015
。我必须检索这些值。我知道我必须使用嵌套循环来比较特定日期和每隔一个日期。我所做的是:
DECLARE
min_sd DATE;
max_ed DATE;
sd DATE;
ed DATE;
i INT:=1;
j INT:=1;
PROCEDURE date_block
IS
BEGIN
WHILE i<=9 loop
SELECT start_date,end_date INTO min_sd,max_ed FROM sd_ed WHERE id=i;
WHILE j<=9 loop
SELECT start_date,end_date INTO sd,ed FROM sd_ed WHERE id=j;
IF min_sd<=sd AND max_ed>=ed THEN
j:=j+1;
ELSE
Dbms_Output.put_line(sd||' - '||ed);
j:=j+1;
END IF;
i:=i+1;
END LOOP;
END loop;
END;
BEGIN
date_block();
END;
我得到的输出是:
05-JUN-15 - 25-JUN-15
07-JUN-15 - 23-JUN-15
21-JUN-15 - 30-JUN-15
我认为这些日期没有与表格中的日期进行比较。任何人都可以提供帮助吗?我正在使用oracle。
仅使用SQL,
SELECT a.*
FROM (SELECT *
FROM sd_ed
ORDER BY id) a
WHERE NOT EXISTS (SELECT 1
FROM sd_ed_test b
WHERE b.start_date <= a.start_date
AND b.end_date >= a.end_date
AND b.id < a.id
);
答案 0 :(得分:1)
这是一个纯SQL解决方案:
Oracle 11g R2架构设置:
create table sd_ed as with sd_ed(ID, START_DATE, END_DATE) as (
select 1, to_date('01.06.2015','dd.mm.yyyy'), to_date('20.06.2015','dd.mm.yyyy') from dual union all
select 2, to_date('05.06.2015','dd.mm.yyyy'), to_date('25.06.2015','dd.mm.yyyy') from dual union all
select 3, to_date('03.06.2015','dd.mm.yyyy'), to_date('10.06.2015','dd.mm.yyyy') from dual union all
select 4, to_date('07.06.2015','dd.mm.yyyy'), to_date('23.06.2015','dd.mm.yyyy') from dual union all
select 5, to_date('21.06.2015','dd.mm.yyyy'), to_date('30.06.2015','dd.mm.yyyy') from dual union all
select 6, to_date('02.06.2015','dd.mm.yyyy'), to_date('10.06.2015','dd.mm.yyyy') from dual union all
select 7, to_date('05.06.2015','dd.mm.yyyy'), to_date('15.06.2015','dd.mm.yyyy') from dual union all
select 8, to_date('05.06.2015','dd.mm.yyyy'), to_date('08.06.2015','dd.mm.yyyy') from dual union all
select 9, to_date('16.06.2015','dd.mm.yyyy'), to_date('20.06.2015','dd.mm.yyyy') from dual
) select * from sd_ed;
查询1 :
with super as(
-- Select the super ranges. All other ranges are
-- completely contained in at least one super range
select id, start_date, end_date
from sd_ed a
where not exists (select 1
from sd_ed b
where a.id <> b.id
and b.start_date <= a.start_date
and a.end_date <= b.end_date)
), hier(id, start_date, end_date) as (
-- Select all record with a start date not between
-- any other records start and end dates
select id, start_date, end_date from super
where not exists(select 1 from super d1
where d1.id <> super.id
and super.start_date between d1.start_date and d1.end_date
and super.start_date <> d1.start_date)
-- Recursively select records that overlap the current range
-- but with end dates after the end date of the current range
union all
select sd_ed.id
, prev.start_date
, greatest(sd_ed.end_date, prev.end_date)
from hier prev
join sd_ed
on sd_ed.id <> prev.id
and sd_ed.start_date <= prev.end_date
and prev.end_date < sd_ed.end_Date
)
-- Get the max end_date for each start date.
-- Start Dates are already minimum for any range.
select start_date, max(end_Date) end_date
from hier
group by start_date
<强> Results 强>:
| START_DATE | END_DATE |
|------------------------|------------------------|
| June, 01 2015 00:00:00 | June, 30 2015 00:00:00 |
对于此数据集,确实不需要识别超级范围,因为分层查询可以很好地处理它,但对于较大的数据集,这种初始修剪将减少分层查询需要完成的工作量。
答案 1 :(得分:0)
您尚未清楚地解释如何识别不重叠的日期范围。这是一个解决方案,列出每个范围之间的所有日期,然后丢弃所有重复的日期(只留下那些只出现在单个范围内的日子),然后将它们分组为连续几天的数据块。
Oracle 11g R2架构设置:
create table sd_ed (ID, START_DATE, END_DATE) as
select 1, to_date('01.06.2015','dd.mm.yyyy'), to_date('20.06.2015','dd.mm.yyyy') from dual union all
select 2, to_date('05.06.2015','dd.mm.yyyy'), to_date('25.06.2015','dd.mm.yyyy') from dual union all
select 3, to_date('03.06.2015','dd.mm.yyyy'), to_date('10.06.2015','dd.mm.yyyy') from dual union all
select 4, to_date('07.06.2015','dd.mm.yyyy'), to_date('23.06.2015','dd.mm.yyyy') from dual union all
select 5, to_date('21.06.2015','dd.mm.yyyy'), to_date('30.06.2015','dd.mm.yyyy') from dual union all
select 6, to_date('02.06.2015','dd.mm.yyyy'), to_date('10.06.2015','dd.mm.yyyy') from dual union all
select 7, to_date('05.06.2015','dd.mm.yyyy'), to_date('15.06.2015','dd.mm.yyyy') from dual union all
select 8, to_date('05.06.2015','dd.mm.yyyy'), to_date('08.06.2015','dd.mm.yyyy') from dual union all
select 9, to_date('16.06.2015','dd.mm.yyyy'), to_date('20.06.2015','dd.mm.yyyy') from dual;
查询1 :
WITH days AS (
SELECT COLUMN_VALUE AS Day
FROM sd_ed s,
TABLE(
CAST(
MULTISET(
SELECT s.START_DATE + LEVEL - 1
FROM DUAL
CONNECT BY s.START_DATE + LEVEL - 1 <= s.END_DATE
)
AS SYS.ODCIDATELIST
)
) d
GROUP BY COLUMN_VALUE
HAVING COUNT(1) = 1
),
Group_Changes AS (
SELECT Day,
CASE WHEN LAG( Day ) OVER ( ORDER BY Day ) + INTERVAL '1' DAY = Day THEN 0 ELSE 1 END AS Change_Group
FROM Days
),
Groups AS (
SELECT Day,
SUM( Change_Group ) OVER ( ORDER BY Day ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ) AS group_id
FROM Group_Changes
)
SELECT MIN( Day ) AS start_date,
MAX( Day ) AS end_date
FROM Groups
GROUP BY Group_Id
ORDER BY 1,2
<强> Results 强>:
| START_DATE | END_DATE |
|------------------------|------------------------|
| June, 01 2015 00:00:00 | June, 01 2015 00:00:00 |
| June, 26 2015 00:00:00 | June, 30 2015 00:00:00 |
但是,如果您只需要表格中的日期范围列表,那么您可以这样做(与上面没有HAVING COUNT(1) = 1
行的情况相同):
查询2 :
WITH days AS (
SELECT COLUMN_VALUE AS Day
FROM sd_ed s,
TABLE(
CAST(
MULTISET(
SELECT s.START_DATE + LEVEL - 1
FROM DUAL
CONNECT BY s.START_DATE + LEVEL - 1 <= s.END_DATE
)
AS SYS.ODCIDATELIST
)
) d
GROUP BY COLUMN_VALUE
),
Group_Changes AS (
SELECT Day,
CASE WHEN LAG( Day ) OVER ( ORDER BY Day ) + INTERVAL '1' DAY = Day THEN 0 ELSE 1 END AS Change_Group
FROM Days
),
Groups AS (
SELECT Day,
SUM( Change_Group ) OVER ( ORDER BY Day ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ) AS group_id
FROM Group_Changes
)
SELECT MIN( Day ) AS start_date,
MAX( Day ) AS end_date
FROM Groups
GROUP BY Group_Id
ORDER BY 1,2
<强> Results 强>:
| START_DATE | END_DATE |
|------------------------|------------------------|
| June, 01 2015 00:00:00 | June, 30 2015 00:00:00 |