Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

Posso usar uma CTE do SQL Server para mesclar datas de interseção?

Reescrita 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

Agora produz o resultado solicitado e realmente abrange todos os casos possíveis, incluindo subgrupos disjuntos e duplicatas. Dê uma olhada nos comentários aos dados de teste no demonstração de trabalho em data.SE .

  • CTE 1
    Encontre os pontos (únicos!) no tempo em que um novo grupo de intervalos sobrepostos começa.

  • CTE 2
    Conta o início de um novo grupo até (e incluindo) cada intervalo individual, formando assim um número de grupo único por usuário.

  • Final SELECT
    Mesclar os grupos, começar mais cedo e terminar mais tarde para os grupos.

Eu enfrentei alguma dificuldade, porque a janela T-SQL funciona max() ou sum() não aceite um ORDER BY cláusula em a em uma janela. Eles só podem calcular um valor por partição, o que torna impossível calcular uma soma/contagem em execução por partição. Funcionaria no PostgreSQL ou Oracle (mas não no MySQL, é claro - não tem funções de janela nem CTEs).

A solução final usa um CTE extra e deve ser tão rápida quanto.