Komplett neu geschrieben:
;WITH new_grp AS (
SELECT r1.UserId, r1.StartTime
FROM @requests r1
WHERE NOT EXISTS (
SELECT *
FROM @requests r2
WHERE r1.UserId = r2.UserId
AND r2.StartTime < r1.StartTime
AND r2.EndTime >= r1.StartTime)
GROUP BY r1.UserId, r1.StartTime -- there can be > 1
),r AS (
SELECT r.RequestId, r.UserId, r.StartTime, r.EndTime
,count(*) AS grp -- guaranteed to be 1+
FROM @requests r
JOIN new_grp n ON n.UserId = r.UserId AND n.StartTime <= r.StartTime
GROUP BY r.RequestId, r.UserId, r.StartTime, r.EndTime
)
SELECT min(RequestId) AS RequestId
,UserId
,min(StartTime) AS StartTime
,max(EndTime) AS EndTime
FROM r
GROUP BY UserId, grp
ORDER BY UserId, grp
Erzeugt jetzt das gewünschte Ergebnis und wirklich deckt alle möglichen Fälle ab, einschließlich disjunkter Untergruppen und Duplikate. Werfen Sie einen Blick auf die Kommentare zu den Testdaten im funktionierende Demo bei data.SE .
-
CTE 1
Finde die (einzigartigen!) Zeitpunkte, an denen eine neue Gruppe sich überschneidender Intervalle beginnt. -
CTE 2
Zählen Sie die Anfänge neuer Gruppen bis (einschließlich) zu jedem einzelnen Intervall und bilden Sie so eine eindeutige Gruppennummer pro Benutzer. -
Final SELECT
Merge die Gruppen zusammen, nimm frühen Start und spätestes Ende für Gruppen.
Ich hatte einige Schwierigkeiten, weil T-SQL-Fensterfunktionen max()
oder sum()
Akzeptieren Sie keine ORDER BY
Klausel in einem in einem Fenster. Sie können nur einen Wert pro Partition berechnen, was es unmöglich macht, eine laufende Summe / Anzahl pro Partition zu berechnen. Würde in PostgreSQL oder Oracle funktionieren (aber natürlich nicht in MySQL - es hat weder Fensterfunktionen noch CTEs).
Die endgültige Lösung verwendet einen zusätzlichen CTE und sollte genauso schnell sein.