Een manier om dit te doen is door het gebruik van gecorreleerde subquery's:
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
De WHERE
predikaten van de gecorreleerde subquery's:
t2.opens <= t1.closes AND t2.closes >= t1.opens
alle overlappende records met betrekking tot de huidige record retourneren. Door aggregatie van een van deze records uit te voeren, kunnen we de start- / einddatums van elk interval vinden:de startdatum van het interval is het minimum opens
datum tussen alle overlappende records, terwijl de einddatum het maximum is closes
datum.
BEWERKEN:
De bovenstaande oplossing werkt niet met een reeks intervallen zoals de volgende:
1. |-----------|
2. |----|
3. |-----|
Opname nr. 2, wanneer verwerkt, zal een gebrekkig begin/eind interval produceren.
Hier is een oplossing die variabelen gebruikt:
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
Het idee is om te beginnen vanaf de meest linkse opens/closes
interval. Variabelen @start
, @end
worden gebruikt om het incrementeel uitbreidende (terwijl nieuwe overlappende rijen worden verwerkt) geconsolideerde interval in de intervalketen te verspreiden. Zodra een niet-overlappend interval is gevonden, [@start - @end]
wordt geïnitialiseerd om overeen te komen met dit nieuwe interval en grp
wordt met één verhoogd.