Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

Kann ich einen SQL Server-CTE verwenden, um sich überschneidende Daten zusammenzuführen?

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.