Sqlserver
 sql >> Base de données >  >> RDS >> Sqlserver

Création de groupes de jours consécutifs répondant à un critère donné

Dans cette réponse, je supposerai que le champ "id" numérote les lignes consécutivement lorsqu'il est trié par date croissante, comme c'est le cas dans l'exemple de données. (Une telle colonne peut être créée si elle n'existe pas).

Ceci est un exemple de technique décrite ici et ici .

1) Joignez la table à elle-même sur des valeurs "id" adjacentes. Cela associe des lignes adjacentes. Sélectionnez les lignes où le champ "allocation" a changé. Stockez le résultat dans une table temporaire, en conservant également un index courant.

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;

Cela vous donne un tableau ayant "la fin de la période précédente", "le début de la période suivante" et "la valeur de 'l'allocation' dans la période précédente" dans chaque ligne :

+------+------------+------------+------------+
| 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) Nous avons besoin du début et de la fin de chaque période dans la même ligne, nous devons donc combiner à nouveau les lignes adjacentes. Pour ce faire, créez une deuxième table temporaire comme boundaries mais ayant un idx champ 1 supérieur :

+------+------------+------------+
| 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 |
+------+------------+------------+

Rejoignez maintenant le idx champ et nous obtenons la réponse :

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 |
+------------+------------+------------+

** Notez que cette réponse obtient correctement les périodes "internes" mais manque les deux périodes "périphériques" où allocation =0 au début et allocation =5 à la fin. Ceux-ci peuvent être extraits en utilisant UNION clauses mais je voulais présenter l'idée de base sans cette complication.