sql >> Base de Datos >  >> RDS >> Sqlserver

¿Puedo usar un CTE de SQL Server para fusionar fechas que se cruzan?

Reescritura completa:

;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

Ahora produce el resultado solicitado y realmente cubre todos los casos posibles, incluidos subgrupos separados y duplicados. Eche un vistazo a los comentarios de los datos de prueba en demostración en funcionamiento en data.SE .

  • CTE 1
    Encuentre los puntos (¡únicos!) en el tiempo donde comienza un nuevo grupo de intervalos superpuestos.

  • CTE 2
    Cuente los inicios del nuevo grupo hasta (e incluyendo) cada intervalo individual, formando así un número de grupo único por usuario.

  • SELECCIÓN final
    Fusionar los grupos, tomar el inicio más temprano y el final más tardío para los grupos.

Enfrenté algunas dificultades, porque la ventana de T-SQL funciona max() o sum() no acepte un ORDER BY cláusula en una en una ventana. Solo pueden calcular un valor por partición, lo que hace que sea imposible calcular una suma/recuento continuo por partición. Funcionaría en PostgreSQL u Oracle (pero no en MySQL, por supuesto, no tiene funciones de ventana ni CTE).

La solución final utiliza un CTE adicional y debería ser igual de rápida.