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

Criar grupos de dias consecutivos atendendo a um determinado critério


Nesta resposta, assumirei que o campo "id" numera as linhas consecutivamente quando classificadas por data crescente, como nos dados de exemplo. (Essa coluna pode ser criada se não existir).

Este é um exemplo de uma técnica descrita aqui e aqui .

1) Junte a tabela a ela mesma nos valores "id" adjacentes. Isso emparelha linhas adjacentes. Selecione as linhas em que o campo "alocação" foi alterado. Armazene o resultado em uma tabela temporária, mantendo também um índice em execução.
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;

Isso fornece uma tabela com "o final do período anterior", "o início do próximo período" e "o valor de 'alocação' no período anterior" em cada linha:
+------+------------+------------+------------+
| 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) Precisamos do início e do fim de cada período na mesma linha, então precisamos combinar as linhas adjacentes novamente. Faça isso criando uma segunda tabela temporária como boundaries mas tendo um idx campo 1 maior:
+------+------------+------------+
| 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 |
+------+------------+------------+

Agora junte-se ao idx campo e obtemos a resposta:
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 |
+------------+------------+------------+

** Observe que esta resposta obtém os períodos "internos" corretamente, mas perde os dois períodos de "borda" em que alocação =0 no início e alocação =5 no final. Esses podem ser puxados usando UNION cláusulas, mas eu queria apresentar a ideia central sem essa complicação.