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

Creazione di gruppi di giorni consecutivi che soddisfano un determinato criterio

In questa risposta, presumo che il campo "id" numera le righe consecutivamente quando ordinate per data crescente, come nei dati di esempio. (Una tale colonna può essere creata se non esiste).

Questo è un esempio di una tecnica descritta qui e qui .

1) Unisci la tabella a se stessa sui valori "id" adiacenti. Questo accoppia le righe adiacenti. Seleziona le righe in cui il campo "allocazione" è cambiato. Archivia il risultato in una tabella temporanea, mantenendo anche un indice in esecuzione.

SET @idx = 0;
CREATE TEMPORARY TABLE boundaries
SELECT
   (@idx := @idx + 1) AS idx,
   a1.date AS prev_end,
   a2.date AS next_start,
   a1.allocation as allocation
FROM allocations a1
JOIN allocations a2
ON (a2.id = a1.id + 1)
WHERE a1.allocation != a2.allocation;

Questo ti dà una tabella con "la fine del periodo precedente", "l'inizio del periodo successivo" e "il valore di 'allocazione' nel periodo precedente" in ogni riga:

+------+------------+------------+------------+
| idx  | prev_end   | next_start | allocation |
+------+------------+------------+------------+
|    1 | 2012-01-01 | 2012-01-02 |          0 |
|    2 | 2012-01-02 | 2012-01-03 |          2 |
|    3 | 2012-01-05 | 2012-01-06 |          0 |
+------+------------+------------+------------+

2) Abbiamo bisogno dell'inizio e della fine di ogni periodo nella stessa riga, quindi dobbiamo combinare nuovamente le righe adiacenti. Fallo creando una seconda tabella temporanea come boundaries ma con un idx campo 1 maggiore:

+------+------------+------------+
| idx  | prev_end   | next_start |
+------+------------+------------+
|    2 | 2012-01-01 | 2012-01-02 |
|    3 | 2012-01-02 | 2012-01-03 |
|    4 | 2012-01-05 | 2012-01-06 |
+------+------------+------------+

Ora unisciti a idx campo e otteniamo la risposta:

SELECT
  boundaries2.next_start AS start,
  boundaries.prev_end AS end,
  allocation
FROM boundaries
JOIN boundaries2
USING(idx);

+------------+------------+------------+
| start      | end        | allocation |
+------------+------------+------------+
| 2012-01-02 | 2012-01-02 |          2 |
| 2012-01-03 | 2012-01-05 |          0 |
+------------+------------+------------+

** Si noti che questa risposta ottiene correttamente i periodi "interni" ma manca i due periodi "bordo" in cui allocazione =0 all'inizio e allocazione =5 alla fine. Questi possono essere inseriti utilizzando UNION clausole, ma volevo presentare l'idea centrale senza quella complicazione.