2010-08-13 221 views
11

我有一个表T1,它包含名称值(不唯一)和日期范围(D1和D2是日期) 当NAME相同时,我们将日期范围(例如B)。ORACLE SQL日期范围交集

但作为一个结果,(X),我们需要让所有日期的交集范围

编辑: 表T1

NAME | D1  | D2 
A | 20100101 | 20101211 
B | 20100120 | 20100415 
B | 20100510 | 201
C | 20100313 | 20100610 

结果:

X | 20100313 | 20100415 
X | 20100510 | 20100610 

肉眼看,该将给出以下内容:

NAME  : date range 
A   : [-----------------------]----- 
B   : --[----]---------------------- 
B   : ----------[---------------]--- 
C   : -----[--------]--------------- 

结果:

X   : -----[-]---------------------- 
X   : ----------[---]--------------- 

不知道如何获取使用SQL/PL SQL?

+1

你可以试试重叠 - 但它是无证功能, http://oraclesponge.wordpress.com/2008/06/12/the-overlaps-谓词/及其唯一检查数据范围是否重叠 – 2010-08-13 12:31:53

+0

您能否显示表中列值的实际列/行示例以及此SQL查询返回的数据值 – 2010-08-13 12:33:03

+0

结果中的日期交集是哪两个名字? A和C是不同的名称,B的两个范围似乎没有问题的日期范围。 – 2010-08-13 12:33:12

回答

8

这里是一个快速的解决方案(可能不是最有效的):

SQL> CREATE TABLE myData AS 
    2 SELECT 'A' name, date'2010-01-01' d1, date'2010-12-11' d2 FROM DUAL 
    3 UNION ALL SELECT 'B', date'2010-01-20', date'2010-04-15' FROM DUAL 
    4 UNION ALL SELECT 'B', date'2010-05-10', date'2010-12-30' FROM DUAL 
    5 UNION ALL SELECT 'C', date'2010-03-13', date'2010-06-10' FROM DUAL; 

Table created 

SQL> WITH segments AS (
    2 SELECT dat seg_low, lead(dat) over(ORDER BY dat) seg_high 
    3 FROM (SELECT d1 dat FROM myData 
    4   UNION 
    5   SELECT d2 dat FROM myData) 
    6 ) 
    7 SELECT s.seg_low, s.seg_high 
    8 FROM segments s 
    9 JOIN myData m ON s.seg_high > m.d1 
10     AND s.seg_low < m.d2 
11 GROUP BY s.seg_low, s.seg_high 
12 HAVING COUNT(DISTINCT NAME) = 3; 

SEG_LOW  SEG_HIGH 
----------- ----------- 
13/03/2010 15/04/2010 
10/05/2010 10/06/2010 

我建立所有可能的连续日期范围,加入这个“日历”与样本数据。这将列出所有具有3个值的范围。您可能需要合并的结果,如果你添加行:

SQL> insert into mydata values ('B',date'2010-04-15',date'2010-04-16'); 

1 row inserted 

SQL> WITH segments AS (
    2 SELECT dat seg_low, lead(dat) over(ORDER BY dat) seg_high 
    3 FROM (SELECT d1 dat FROM myData 
    4   UNION 
    5   SELECT d2 dat FROM myData) 
    6 ) 
    7 SELECT MIN(seg_low), MAX(seg_high) 
    8 FROM (SELECT seg_low, seg_high, SUM(gap) over(ORDER BY seg_low) grp 
    9    FROM (SELECT s.seg_low, s.seg_high, 
10       CASE 
11        WHEN s.seg_low 
12         = lag(s.seg_high) over(ORDER BY s.seg_low) 
13        THEN 0 
14        ELSE 1 
15       END gap 
16      FROM segments s 
17      JOIN myData m ON s.seg_high > m.d1 
18         AND s.seg_low < m.d2 
19      GROUP BY s.seg_low, s.seg_high 
20     HAVING COUNT(DISTINCT NAME) = 3)) 
21 GROUP BY grp; 

MIN(SEG_LOW) MAX(SEG_HIGH) 
------------ ------------- 
13/03/2010 16/04/2010 
10/05/2010 10/06/2010 
+0

正是我需要的! Merci encore Vincent;) 我想我可以用myData中的(select count(distinct NAME))替换“3”? – guigui42 2010-08-13 13:47:18

+0

我刚刚看到你的编辑,我真的不明白你的意思是“如果我添加一行”。在我的数据表中,我可能有无限名称(例如ABCDEFG ...),每个名称可能有连续日期范围的重复行(1行,2行或更多行)(不重叠,但可能有间隔,如我的例子B)。那么,您的第二个代码片段是否适用于所有这些情况?我仍然试图弄清楚你的分析器如何在你的查询中工作。再次感谢 ! – guigui42 2010-08-13 14:00:43

+0

@ guigui42:第一个查询是正确的,但结果可能包含连续的时间间隔(例如,在这种情况下,“2010-03-13 2010-04-15”和“2010-04-15 2010-04-16”) 。第二个查询将合并这些间隔。 – 2010-08-13 14:41:50