Пересечения диапазона ORACLE SQL Date

У меня есть таблица T1, она содержит значение NAME (не уникальное) и диапазон дат (D1 и D2, которые являются датами). Когда NAME совпадают, мы объединяем диапазоны дат (например, B).

Но в результате (X) нам нужно сделать пересечение всех диапазонов дат

Изменить: Таблица T1

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

Результат:

 X | 20100313 | 20100415 X | 20100510 | 20100610 

Визуально это даст следующее:

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

Результат:

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

Любая идея, как получить это, используя SQL / PL SQL?

вот быстрое решение (возможно, не самое эффективное):

 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