2013-08-14 9 views
9

Mam nadzieję, że możesz mi pomóc.Jak pogrupować wiersze według ich DATEDIFF?

Muszę wyświetlić rekordy w tabeli HH_Solution_Audit - jeśli 2 lub więcej sztabów wejdzie do pokoju w ciągu 10 minut. Oto wymagania:

  1. Wyświetl tylko te zdarzenia, które mają interwał czasowy (LAST_UPDATED) mniejszy lub równy 10 minut. Dlatego muszę porównać bieżący wiersz z następnym wierszem i poprzednim wierszem, aby sprawdzić, czy ich DATEDIFF jest mniejszy lub równy 10 minutom. Skończyłem z tą częścią.
  2. Pokaż rekordy jeżeli liczba różnych STAFF_GUID wewnątrz pomieszczenia mniej niż lub równa się 10 minut wynosi co najmniej 2.

HH_Solution_Audit Tabela Dane:

  1. ID - PK
  2. STAFF_GUID - id personel
  3. LAST_UPDATED - datetime, gdy zespół wchodzi do pokoju

Oto, co dostałem do tej pory. Spełnia to tylko wymagania nr 1.

CREATE TABLE HH_Solution_Audit (
ID INT PRIMARY KEY, 
STAFF_GUID NVARCHAR(1), 
LAST_UPDATED DATETIME 
) 
GO 
INSERT INTO HH_Solution_Audit VALUES (1, 'b', '2013-04-25 9:01') 
INSERT INTO HH_Solution_Audit VALUES (2, 'b', '2013-04-25 9:04') 
INSERT INTO HH_Solution_Audit VALUES (3, 'b', '2013-04-25 9:13') 
INSERT INTO HH_Solution_Audit VALUES (4, 'a', '2013-04-25 10:15') 
INSERT INTO HH_Solution_Audit VALUES (5, 'a', '2013-04-25 10:30') 
INSERT INTO HH_Solution_Audit VALUES (6, 'a', '2013-04-25 10:33') 
INSERT INTO HH_Solution_Audit VALUES (7, 'a', '2013-04-25 10:41') 
INSERT INTO HH_Solution_Audit VALUES (8, 'a', '2013-04-25 11:02') 
INSERT INTO HH_Solution_Audit VALUES (9, 'a', '2013-04-25 11:30') 
INSERT INTO HH_Solution_Audit VALUES (10, 'a', '2013-04-25 11:45') 
INSERT INTO HH_Solution_Audit VALUES (11, 'a', '2013-04-25 11:46') 
INSERT INTO HH_Solution_Audit VALUES (12, 'a', '2013-04-25 11:51') 
INSERT INTO HH_Solution_Audit VALUES (13, 'a', '2013-04-25 12:24') 
INSERT INTO HH_Solution_Audit VALUES (14, 'b', '2013-04-25 12:27') 
INSERT INTO HH_Solution_Audit VALUES (15, 'b', '2013-04-25 13:35') 

DECLARE @numOfPeople INT = 2, 
       --minimum number of people that must be inside 
       --the room for @lengthOfStay minutes 
      @lengthOfStay INT = 10, 
       --number of minutes of stay 
      @dateFrom DATETIME = '04/25/2013 00:00', 
      @dateTo DATETIME = '04/25/2013 23:59'; 
    WITH cteSource AS 
    (
     SELECT ID, STAFF_GUID, LAST_UPDATED, 
       ROW_NUMBER() OVER (ORDER BY LAST_UPDATED) AS row_num 
     FROM HH_SOLUTION_AUDIT 
       WHERE LAST_UPDATED >= @dateFrom AND LAST_UPDATED <= @dateTo 
    ) 
    SELECT [current].ID, [current].STAFF_GUID, [current].LAST_UPDATED 
    FROM 
     cteSource AS [current] 
    LEFT OUTER JOIN 
     cteSource AS [previous] ON [current].row_num = [previous].row_num + 1 
    LEFT OUTER JOIN 
     cteSource AS [next] ON [current].row_num = [next].row_num - 1 
    WHERE 
     DATEDIFF(MINUTE, [previous].LAST_UPDATED, [current].LAST_UPDATED) 
     <= @lengthOfStay 
     OR 
     DATEDIFF(MINUTE, [current].LAST_UPDATED, [next].LAST_UPDATED) 
     <= @lengthOfStay 
    ORDER BY [current].ID, [current].LAST_UPDATED  

Prowadzenie kwerendy zwraca ID:
1, 2, 3, 5, 6, 7, 10, 11, 12, 13, 14
że spełnia wymóg nr 1, polegający na tym, że odstęp między wierszem poprzednim, bieżącym i następnym jest mniejszy lub równy 10 minutom.

Czy możesz mi pomóc z drugim wymogiem? Jeśli jest stosowana, zwracane identyfikatory powinny być wyłącznie:
13, 14

+4

Można ustawić 'ostatniej ones' a przykładowe dane? Kilka wierszy i oczekiwany wynik na nich? –

+2

Pytanie jest naprawdę bóg, i widzę, że naprawdę próbujesz, ale moglibyśmy ci pomóc bardziej, gdybyś podał kilka przykładów. Dzięki! – GianlucaBobbio

+0

@NenadZivkovic Hi Nenad. Dodałem przykładowe dane i oczekiwany wynik, o który prosiłeś. Dziękuję Ci. Zapomniałem o tym. – Raii

Odpowiedz

3

Oto pomysł. Nie potrzebujesz ROW_NUMBER i poprzednich i następnych rekordów. Trzeba tylko zapytać zjednoczone - jeden szuka wszystkich, którzy mają kogoś sprawdzone X minut w tyle, a drugi szuka X minut z góry. Każdy używa skorelowanej pod-zapytania i COUNT (*), aby znaleźć liczbę pasujących osób. Jeśli liczba jest większa niż twoja @numOfPeople - to wszystko.

EDIT: nowa wersja: Zamiast robić dwie kwerendy z 10 minut z góry i tyłu, będziemy sprawdzać tylko przez 10 minut za - wybranie tych, które pasują w cteLastOnes. Następnie przejdzie do innej części zapytania, aby wyszukać te, które faktycznie istnieją w ciągu tych 10 minut. Ostatecznie ponownie czyni związek z nimi i

WITH cteSource AS 
(
    SELECT ID, STAFF_GUID, LAST_UPDATED 
    FROM HH_SOLUTION_AUDIT 
    WHERE LAST_UPDATED >= @dateFrom AND LAST_UPDATED <= @dateTo 
) 
,cteLastOnes AS 
(
    SELECT * FROM cteSource c1 
    WHERE @numOfPeople -1 <= (SELECT COUNT(DISTINCT STAFF_GUID) 
           FROM cteSource c2 
           WHERE DATEADD(MI,@lengthOfStay,c2.LAST_UPDATED) > c1.LAST_UPDATED 
           AND C2.LAST_UPDATED <= C1.LAST_UPDATED 
           AND c1.STAFF_GUID <> c2.STAFF_GUID) 
) 
SELECT * FROM cteLastOnes 
UNION 
SELECT * FROM cteSource s 
WHERE EXISTS (SELECT * FROM cteLastOnes l 
       WHERE DATEADD(MI,@lengthOfStay,s.LAST_UPDATED) > l.LAST_UPDATED 
       AND s.LAST_UPDATED <= l.LAST_UPDATED 
       AND s.STAFF_GUID <> l.STAFF_GUID) 

SQLFiddle DEMO - new version

SQLFiddle DEMO - old version

+0

Dziękuję bardzo Nenad! Uwielbiam jego prostotę! – Raii

+1

@Raii NP. Możesz zamienić 'COUNT (*)' na 'COUNT (Distinct STAFF_GUID)' - aby upewnić się, że liczą się tylko różni członkowie personelu. W tym przykładzie nie ma znaczenia, ale może to mieć znaczenie, jeśli '@ numOfPeople' będzie większy niż 2. –

+0

Nenad, rozumiem. Będę odtwarzał twój skrypt, żeby zobaczyć różnicę w "COUNT". Uwielbiam jego logikę. Nie myślałem o zrobieniu tego w ten sposób. Dzięki jeszcze raz. – Raii