Sqlserver
 sql >> Database >  >> RDS >> Sqlserver

Posso utilizzare un CTE di SQL Server per unire le date che si intersecano?

Riscrittura 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

Ora produce il risultato richiesto e davvero copre tutti i casi possibili, inclusi sottogruppi e duplicati disgiunti. Dai un'occhiata ai commenti ai dati del test in dimostrazione di lavoro su data.SE .

  • CTE 1
    Trova i punti (unici!) nel tempo in cui inizia un nuovo gruppo di intervalli sovrapposti.

  • CTE 2
    Conta gli inizi di un nuovo gruppo fino a (incluso) ogni singolo intervallo, formando così un numero di gruppo univoco per utente.

  • SELEZIONA FINALE
    Unisci i gruppi, prendi l'inizio anticipato e l'ultima fine per i gruppi.

Ho incontrato alcune difficoltà, perché le funzioni della finestra T-SQL max() o sum() non accettare un ORDER BY clausola in a in una finestra. Possono calcolare solo un valore per partizione, il che rende impossibile calcolare una somma/conteggio corrente per partizione. Funzionerebbe in PostgreSQL o Oracle (ma non in MySQL, ovviamente - non ha né funzioni di finestra né CTE).

La soluzione finale utilizza un CTE aggiuntivo e dovrebbe essere altrettanto veloce.