Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

Erstellen von Gruppen aufeinanderfolgender Tage, die bestimmte Kriterien erfüllen

In dieser Antwort gehe ich davon aus, dass das Feld "id" die Zeilen fortlaufend nummeriert, wenn es nach aufsteigendem Datum sortiert wird, wie es in den Beispieldaten der Fall ist. (Eine solche Spalte kann erstellt werden, falls sie nicht existiert).

Dies ist ein Beispiel für eine Technik, die hier beschrieben wird und hier .

1) Verbinden Sie die Tabelle mit sich selbst bei benachbarten "id"-Werten. Dadurch werden benachbarte Reihen gepaart. Wählen Sie Zeilen aus, in denen sich das Feld "Zuordnung" geändert hat. Speichern Sie das Ergebnis in einer temporären Tabelle und führen Sie auch einen laufenden Index.

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;

Dadurch erhalten Sie eine Tabelle, die in jeder Zeile „das Ende des vorherigen Zeitraums“, „den Beginn des nächsten Zeitraums“ und „den Wert der ‚Zuweisung‘ im vorherigen Zeitraum“ enthält:

+------+------------+------------+------------+
| 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) Wir brauchen den Beginn und das Ende jeder Periode in derselben Reihe, also müssen wir benachbarte Reihen wieder kombinieren. Erstellen Sie dazu eine zweite temporäre Tabelle wie boundaries aber mit einem idx Feld 1 größer:

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

Treten Sie jetzt dem idx bei Feld und wir erhalten die Antwort:

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

** Beachten Sie, dass diese Antwort die "internen" Perioden korrekt erhält, aber die beiden "Edge"-Perioden verfehlt, bei denen die Zuweisung =0 am Anfang und die Zuweisung =5 am Ende ist. Diese können mit UNION eingezogen werden Klauseln, aber ich wollte die Kernidee ohne diese Komplikation darstellen.