2016-08-09 15 views
8

Mam problem z grupowaniem mojego zestawu danych w MS SQL Server.Grupowanie SQL według datetime z maksymalną różnicą x minut

Moja tabela wygląda

# | CustomerID | SalesDate   | Turnover 
---| ---------- | ------------------- | --------- 
1 | 1   | 2016-08-09 12:15:00 | 22.50 
2 | 1   | 2016-08-09 12:17:00 | 10.00 
3 | 1   | 2016-08-09 12:58:00 | 12.00 
4 | 1   | 2016-08-09 13:01:00 | 55.00 
5 | 1   | 2016-08-09 23:59:00 | 10.00 
6 | 1   | 2016-08-10 00:02:00 | 5.00 

Teraz chcę grupy wiersze gdzie różnica SalesDate do następnego wiersza jest maksymalnie 5 minut. Tak więc ten wiersz 1 & 2, 3 & 4 i 5 & 6 to każda jedna grupa.

My podejście się protokół z funkcją DATEPART() i podzielenie wyniku przez 5:

(DATEPART(MINUTE, SalesDate)/5) 

dla rzędu 1 i 2 wynika, że ​​3 do grupowania się tutaj będzie działać doskonale. Jednak w przypadku innych wierszy, w których występuje zmiana w godzinie lub nawet w części dziennej wartości SalesDate, wyniku nie można użyć do grupowania.

Więc to jest, gdzie utknąłem. Byłbym wdzięczny, gdyby ktoś wskazał mi właściwy kierunek.

+0

wyboru [to] (http://stackoverflow.com/questions/17560829/how-can-i-subtract-a-previous-row-in-sql) lub [to] (http: // stackoverflow.com/questions/13734976/sql-best-way-to-subtract-a-value-of-previous-row-in--query) – techspider

+0

Możesz napisać zapytanie wykorzystujące LEAD lub LAG do sprawdzenia następny wiersz i znajdź różnicę czasu, a następnie użyj DENSE_RANK, aby przypisać numer grupowania, partycjonowanie z różnicą> 5 minut. Następnie zgrupuj na tym numerze grupowym. Jest to intensywny proces, jeśli zamierzasz zapisać go w jednym zapytaniu w wielu wierszach. –

+0

Myślę, że to dobre pierwsze pytanie. –

Odpowiedz

5

Chcesz pogrupować sąsiednie transakcje w oparciu o czas między nimi. Chodzi o to, aby przypisać jakiś rodzaj identyfikatora grupowania, a następnie użyć go do agregacji.

Oto podejście:

  • Określenie grupa zaczyna używać lag() i arytmetyki dat.
  • Łączna suma grupy rozpoczyna się w celu zidentyfikowania każdej grupy.
  • Kruszywo

Kwerenda wygląda następująco:

select customerid, min(salesdate), max(saledate), sum(turnover) 
from (select t.*, 
      sum(case when salesdate > dateadd(minute, 5, prev_salesdate) 
         then 1 else 0 
       end) over (partition by customerid order by salesdate) as grp 
     from (select t.*, 
        lag(salesdate) over (partition by customerid order by salesdate) as prev_salesdate 
      from t 
      ) t 
    ) t 
group by customerid, grp; 
+0

Awesome! Dziękuję bardzo, działa to doskonale i jest nawet dość szybko! :) – Felix

2

EDIT

Dzięki @JoeFarrell za wskazanie mi odpowiedziałeś niewłaściwe pytanie. OP szuka dynamicznych różnic czasowych między wierszami, ale to podejście tworzy ustalone granice.

Original Odpowiedź

Można by utworzyć tabelę czasową. Jest to tabela zawierająca jeden rekord na każdą sekundę dnia. Twoja tabela będzie miała drugą kolumnę, za pomocą której możesz wykonywać pomijanie grup.

CREATE TABLE [Time] 
    (
     TimeId  TIME(0) PRIMARY KEY, 
     TimeGroup TIME 
    ) 
; 

-- You could use a loop here instead. 
INSERT INTO [Time] 
    (
     TimeId, 
     TimeGroup 
    ) 
VALUES 
    ('00:00:00', '00:00:00'), -- First group starts here. 
    ('00:00:01', '00:00:00'), 
    ('00:00:02', '00:00:00'), 
    ('00:00:03', '00:00:00'), 
    ... 
    ('00:04:59', '00:00:00'), 
    ('00:05:00', '00:05:00'), -- Second group starts here. 
    ('00:05:01', '00:05:00') 
; 

podejście działa najlepiej, gdy:

  1. trzeba ponownie użyć niestandardowego grupowania w kilku różnych zapytań.
  2. Masz dwie lub więcej grup niestandardowych, z których często korzystasz.

Po wypełnieniu wystarczy po prostu dołączyć do tabeli i uzyskać pożądany wynik.

/* Using the time table. 
*/ 
SELECT 
    t.TimeGroup, 
    SUM(Turnover) AS SumOfTurnover 
FROM 
    Sales AS s 
     INNER JOIN [Time] AS t  ON t.TimeId = CAST(s.SalesDate AS Time(0)) 
GROUP BY 
    t.TimeGroup 
; 
+1

Nie sądzę, że to zadziała. Jeśli masz dwa wpisy, których czasy wynoszą 00:04:59 i 00:05:00, twoje rozwiązanie podzieli je osobno, mimo że są one od siebie oddalone. Jest to ta sama wada, którą OP zgłosił widząc we własnej pierwszej próbie. –

+0

Tak, masz rację. Źle zrozumiałem to pytanie. PO nie chce ściśle zakodowanych granic. Zamiast tego szukają dynamicznej różnicy. –

Powiązane problemy