Sqlserver
 sql >> Baza danych >  >> RDS >> Sqlserver

Tworzenie grup kolejnych dni spełniających zadane kryteria

W tej odpowiedzi przyjmę, że pole „id” numeruje wiersze kolejno posortowane według rosnącej daty, tak jak to ma miejsce w przykładowych danych. (Taką kolumnę można utworzyć, jeśli nie istnieje).

To jest przykład techniki opisanej tutaj i tutaj .

1) Połącz tabelę ze sobą na sąsiednich wartościach „id”. Paruje sąsiednie rzędy. Wybierz wiersze, w których zmieniło się pole „alokacja”. Przechowuj wynik w tabeli tymczasowej, zachowując również bieżący indeks.

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;

Daje to tabelę zawierającą „koniec poprzedniego okresu”, „początek następnego okresu” i „wartość „alokacji” w poprzednim okresie” w każdym wierszu:

+------+------------+------------+------------+
| 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) Potrzebujemy początku i końca każdego okresu w tym samym rzędzie, więc musimy ponownie połączyć sąsiednie rzędy. Zrób to, tworząc drugą tabelę tymczasową, taką jak boundaries ale mając idx pole 1 większe:

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

Teraz dołącz do idx i otrzymujemy odpowiedź:

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

** Zauważ, że ta odpowiedź poprawnie pobiera okresy „wewnętrzne”, ale pomija dwa okresy „krawędziowe”, gdzie alokacja =0 na początku i alokacja =5 na końcu. Można je pobrać za pomocą UNION klauzul, ale chciałem przedstawić główny pomysł bez komplikacji.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Używanie crypt_gen_random do generowania unikalnych numerów seryjnych

  2. WYBIERZ WYRÓŻNIENIE w jednej kolumnie

  3. ExecuteNonQuery zwraca -1

  4. Wartości oddzielone przecinkami za pomocą zapytania SQL

  5. Czy unikalny klucz serwera SQL jest również indeksem?