2013-08-14 13 views
9

Spero che tu possa aiutarmi.Come raggruppare le righe in base al loro DATEDIFF?

Ho bisogno di visualizzare i record nella tabella HH_Solution_Audit - se 2 o più pentagrammi entrano nella stanza entro 10 minuti. Ecco i requisiti:

  1. Visualizza solo gli eventi che hanno un intervallo di data/ora (LAST_UPDATED) inferiore o uguale a 10 minuti. Pertanto, devo confrontare la riga corrente con la riga successiva e la riga precedente per verificare se il loro DATEDIFF è inferiore o uguale a 10 minuti. Ho finito con questa parte.
  2. Mostra solo i record se il numero di STAFF_GUID distinta all'interno della stanza per meno di o uguale a 10 minuti è almeno 2.

HH_Solution_Audit Tabella Dettagli:

  1. ID - PK
  2. STAFF_GUID - personale id
  3. LAST_UPDATED - datetime quando un personale entra in una stanza

Ecco cosa ho ottenuto finora. Questo soddisfa solo il requisito # 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  

Esecuzione query restituisce ID:
1, 2, 3, 5, 6, 7, 10, 11, 12, 13, 14
Tale soddisfa il requisito n. 1 di avere un intervallo inferiore o uguale a 10 minuti tra la riga precedente, la riga corrente e la riga successiva.

Potete aiutarmi con il 2 ° requisito? Se è applicato, gli ID restituiti dovrebbero essere solo:
13, 14

+4

Can si imposta il 'ultimi' un insieme di dati di esempio? Poche righe e risultati attesi su di loro? –

+2

La domanda è davvero dio, e posso vedere che ci provi davvero, ma potremmo aiutarti di più se metti degli esempi. Grazie! – GianlucaBobbio

+0

@NenadZivkovic Ciao Nenad. Ho aggiunto un campione di dati e risultati attesi come richiesto. Grazie. L'ho dimenticato. – Raii

risposta

3

Ecco un'idea. Non hai bisogno di ROW_NUMBER e record precedenti e successivi. Devi solo interrogare i sindacati: uno alla ricerca di tutti quelli che hanno qualcuno controllato X minuti indietro, e un altro in cerca di X minuti in anticipo. Ognuno utilizza una sottoquery correlata e COUNT (*) per trovare il numero di persone corrispondenti. Se il numero è maggiore di @numOfPeople, è tutto.

EDIT: nuova versione: Invece di fare due query con 10 minuti in anticipo e dietro, controlleremo solo per 10 minuti dietro - selezionando quelli che corrispondono a cteLastOnes. Dopodiché andrà in un'altra parte della query per cercare quelli che effettivamente esistono in quei 10 minuti. In definitiva ancora una volta facendo unione di loro e

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

Grazie mille Nenad! Adoro la sua semplicità! – Raii

+1

@Raii NP. Si potrebbe voler sostituire 'COUNT (*)' con 'COUNT (Distinct STAFF_GUID)' - per assicurarsi che contengano solo membri distinti dello staff. In questo esempio è irrilevante, ma potrebbe fare la differenza se hai '@ numOfPeople' per essere più grande di 2. –

+0

Nenad, capisco. Giocherò attorno al tuo script per vedere la differenza in "CONTA". Adoro la sua logica. Non pensavo di fare il mio compito in quel modo. Grazie ancora. – Raii

Problemi correlati