Oracle SQL intersezioni Periodo
-
28-09-2019 - |
Domanda
Ho un T1 tavolo, contiene un valore NAME (non unico), e un intervallo di date (D1 e D2, che sono le date) Quando NAME sono gli stessi, facciamo un'unione degli intervalli di date (ad esempio B).
Ma come risultato (X), abbiamo bisogno di fare intersezione di tutti gli intervalli di date
Modifica: Tabella T1
NAME | D1 | D2
A | 20100101 | 20101211
B | 20100120 | 20100415
B | 20100510 | 20101230
C | 20100313 | 20100610
Risultato:
X | 20100313 | 20100415
X | 20100510 | 20100610
Visivamente, questo darà la seguente:
NAME : date range
A : [-----------------------]-----
B : --[----]----------------------
B : ----------[---------------]---
C : -----[--------]---------------
Risultato:
X : -----[-]----------------------
X : ----------[---]---------------
Qualsiasi idea di come ottenere che l'uso di SQL / PL SQL?
Soluzione
qui è una soluzione rapida (non può essere la più efficiente):
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
I costruire tutti i possibili intervalli di date successive e aderire a questo "calendario" con i dati di esempio. Questo elenco di tutte le gamme che hanno 3 valori. Potrebbe essere necessario unire il risultato se si aggiungono righe:
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