Jednym ze sposobów na to jest użycie skorelowanych podzapytań:
SELECT DISTINCT
(SELECT MIN(opens)
FROM mytable AS t2
WHERE t2.opens <= t1.closes AND t2.closes >= t1.opens) AS start,
(SELECT MAX(closes)
FROM mytable AS t2
WHERE t2.opens <= t1.closes AND t2.closes >= t1.opens) AS end
FROM mytable AS t1
ORDER BY opens
WHERE
predykaty skorelowanych podzapytań:
t2.opens <= t1.closes AND t2.closes >= t1.opens
zwraca wszystkie nakładające się rekordy związane z bieżącym rekordem. Wykonując agregację jednego z tych rekordów możemy znaleźć daty rozpoczęcia/zakończenia każdego interwału:data rozpoczęcia interwału to minimum opens
data między wszystkimi nakładającymi się rekordami, podczas gdy data zakończenia to maksymalne closes
data.
EDYTUJ:
Powyższe rozwiązanie nie będzie działać z zestawem interwałów, takim jak:
1. |-----------|
2. |----|
3. |-----|
Nr rekordu 2, po przetworzeniu da wadliwy interwał początkowy/końcowy.
Oto rozwiązanie wykorzystujące zmienne:
SELECT MIN(start) AS start, MAX(end) AS end
FROM (
SELECT @grp := IF(@start = '1900-01-01' OR
(opens <= @end AND closes >= @start), @grp, @grp+1) AS grp,
@start := IF(@start = '1900-01-01', opens,
IF(opens <= @end AND closes >= @start,
IF (@start < opens, @start, opens), opens)) AS start,
@end := IF(@end = '1900-01-01', closes,
IF (opens <= @end AND closes >= @start,
IF (@end > closes, @end, closes), closes)) AS end
FROM mytable
CROSS JOIN (SELECT @grp := 1, @start := '1900-01-01', @end := '1900-01-01') AS vars
ORDER BY opens, DATEDIFF(closes, opens) DESC) AS t
GROUP BY grp
Pomysł polega na tym, aby zacząć od najbardziej po lewej stronie opens/closes
interwał. Zmienne @start
, @end
są używane do propagowania stopniowo rozszerzającego się (w miarę przetwarzania nowych nakładających się wierszy) skonsolidowanego interwału w dół łańcucha interwałów. Po napotkaniu nienakładającego się interwału [@start - @end]
jest inicjowany tak, aby pasował do tego nowego interwału i grp
jest zwiększany o jeden.