2010-04-19 14 views
7

Ho un set di dati che mi dice se sono disponibili un paio di sistemi o meno ogni 5 o 15 minuti di incrementi. Per ora, l'incremento del tempo non dovrebbe avere importanza.Trova righe consecutive e durata del calcolo

I dati si presenta così:

Status  Time   System_ID 
T   10:00   S01 
T   10:15   S01 
F   10:30   S01 
F   10:45   S01 
F   11:00   S01 
T   11:15   S01 
T   11:30   S01 
F   11:45   S01 
F   12:00   S01 
F   12:15   S01 
T   12:30   S01 

F   10:00   S02 
F   10:15   S02 
F   10:30   S02 
F   10:45   S02 
F   11:00   S02 
T   11:15   S02 
T   11:30   S02 

Voglio creare una vista che dice quando un sistema non è disponibile (ad esempio quando si tratta di F), da ciò che il tempo, a che ora, e la durata che è da - da.

risultati desiderati:

System_ID From   To   Duration 
S01   10:30   11:00   00:30 
S01   11:45   12:15   00:30 
S02   10:00   11:00   01:00 

Ecco i dati script:

DROP SCHEMA IF EXISTS Sys_data CASCADE; 
CREATE SCHEMA Sys_data; 

CREATE TABLE test_data (
      status BOOLEAN, 
      dTime TIME, 
      sys_ID VARCHAR(10), 
      PRIMARY KEY (dTime, sys_ID) 
); 

INSERT INTO test_data (status, dTime, sys_ID) VALUES (TRUE, '10:00:00', 'S01'); 
INSERT INTO test_data (status, dTime, sys_ID) VALUES (TRUE, '10:15:00', 'S01'); 
INSERT INTO test_data (status, dTime, sys_ID) VALUES (FALSE, '10:30:00', 'S01'); 
INSERT INTO test_data (status, dTime, sys_ID) VALUES (FALSE, '10:45:00', 'S01'); 
INSERT INTO test_data (status, dTime, sys_ID) VALUES (FALSE, '11:00:00', 'S01'); 
INSERT INTO test_data (status, dTime, sys_ID) VALUES (TRUE, '11:15:00', 'S01'); 
INSERT INTO test_data (status, dTime, sys_ID) VALUES (TRUE, '11:30:00', 'S01'); 
INSERT INTO test_data (status, dTime, sys_ID) VALUES (FALSE, '11:45:00', 'S01'); 
INSERT INTO test_data (status, dTime, sys_ID) VALUES (FALSE, '12:00:00', 'S01'); 
INSERT INTO test_data (status, dTime, sys_ID) VALUES (FALSE, '12:15:00', 'S01'); 
INSERT INTO test_data (status, dTime, sys_ID) VALUES (TRUE, '12:30:00', 'S01'); 
INSERT INTO test_data (status, dTime, sys_ID) VALUES (FALSE, '10:00:00', 'S02'); 
INSERT INTO test_data (status, dTime, sys_ID) VALUES (FALSE, '10:15:00', 'S02'); 
INSERT INTO test_data (status, dTime, sys_ID) VALUES (FALSE, '10:30:00', 'S02'); 
INSERT INTO test_data (status, dTime, sys_ID) VALUES (FALSE, '10:45:00', 'S02'); 
INSERT INTO test_data (status, dTime, sys_ID) VALUES (FALSE, '11:00:00', 'S02'); 
INSERT INTO test_data (status, dTime, sys_ID) VALUES (TRUE, '11:15:00', 'S02'); 
INSERT INTO test_data (status, dTime, sys_ID) VALUES (TRUE, '11:30:00', 'S02'); 

Grazie in anticipo!

+1

no vuoi interrogare per passare dalla prima F dopo una T alla successiva T? Il sistema non è necessariamente disponibile tra l'ultima F in una sequenza e la successiva T. –

+0

Hai ragione. Dovrebbe essere al prossimo T. – MannyKo

risposta

2

Forse non è ottimale, ma funziona :)

select sys_id, first_time as down_from, max(dTime) as down_to 
from (select status, sys_id, dTime, 
      (select min(td_add2.dTime) 
       from test_data td_add2 
       where td_add2.dtime <= x.dTime 
       and td_add2.dtime >= COALESCE(x.prev_time,x.min_time) 
       and td_add2.status = x.status  
       and td_add2.sys_id = x.sys_id) as first_time 
     from (select td_main.status, td_main.sys_id, td_main.dTime,  
           (select max(td_add.dTime) 
            from test_data td_add 
            where td_add.dtime < td_main.dTime 
            and td_add.status != td_main.status  
            and td_add.sys_id = td_main.sys_id) as prev_time, 
           (select min(td_add.dTime) 
            from test_data td_add 
            where td_add.dtime < td_main.dTime 
            and td_add.sys_id = td_main.sys_id) as min_time                          
       from test_data td_main) x 
    ) y 
where status = false 
and first_time is not null 
group by sys_id, first_time 
order by sys_id, first_time 
+--------+-----------+----------+ 
| sys_id | down_from | down_to | 
+--------+-----------+----------+ 
| S01 | 10:30:00 | 11:00:00 | 
| S01 | 11:45:00 | 12:15:00 | 
| S02 | 10:00:00 | 11:00:00 | 
+--------+-----------+----------+ 
3 rows in set (0.00 sec) 
+0

+1 per la soluzione testata (nota secondaria: ordine per è ridondante; "Se si utilizza GROUP BY, le righe di output vengono ordinate in base alle colonne GROUP BY come se fosse stato ordinato un ORDER BY per le stesse colonne.") – Unreason

+0

I don ' So che MySQL funziona in modo strano :). PostgreSQL e Oracle non garantiscono l'ordinamento durante l'utilizzo di GROUP BY. L'ordinamento in GROUP BY è un effetto collaterale. –

+0

Grazie mille! Questo ha funzionato! – MannyKo

0

Poco più lungo, tuttavia, sembra funzionare in PostgreSQL. Principio di base:

  1. volte trovare dove lo stato del sistema cambia
  2. ottenere solo prima e ultima volta - dove lo scorso Stato era stato diverso e successivo sta per essere diverso (o nessuno)
  3. differenza di calcolo

Ecco il codice:

SELECT sys_id, 
    status, 
    coalesce(end_time, end_time2) - start_time duration 
FROM (
SELECT sys_id, status, start_time, end_time, 
lead(end_time) over (partition by sys_id order by dtime) end_time2 
FROM ( 
    SELECT sys_id, status, dtime, start_time, end_time 
    FROM (
     SELECT sys_id, status, dtime, 
     CASE WHEN last_status != status OR last_status IS NULL THEN dtime ELSE NULL END start_time, 
     CASE WHEN next_status != status OR next_status IS NULL THEN dtime ELSE NULL END end_time 
     FROM (
     SELECT sys_id, status, dtime, 
      LAG(status) OVER (PARTITION BY sys_id ORDER BY sys_id, dtime) last_status, 
      LEAD(status) OVER (PARTITION BY sys_id ORDER BY sys_id, dtime) next_status 
      FROM test_data 
      ORDER BY sys_id, dtime 
     ) surrounding_status 
    ) last_next_times 

    WHERE start_time IS NOT NULL OR end_time IS NOT NULL 
    ORDER BY sys_id, dtime 
) start_end_times 
) find_last_time 
WHERE start_time IS NOT NULL AND status = FALSE 
ORDER BY sys_id, start_time; 

E 'proprio codice rapido, ci potrebbe essere semplice r soluzione, penso.

+0

Oh, mi scuso, ho trascurato il tag mysql. Questo non funzionerà in MySQL perché non ha funzioni analitiche/di windowing per quanto ne so. – Stiivi

1

Ecco la soluzione basata su cursore, non so se MySQL supporta la partizione Quindi il motivo di un cursore. Questo è stato testato in SQL 2008 e funziona, spero che funziona in MySQL, ma almeno vi darà un'idea

CREATE TABLE #offline_data 
    (
    dTime DATETIME 
    ,sys_ID VARCHAR(50) 
    ,GroupID INTEGER 
    ) 


DECLARE @status BIT 
DECLARE @dTime DATETIME 
DECLARE @sys_ID VARCHAR(50) 

DECLARE @GroupID INTEGER = 0 


DECLARE test_cur CURSOR 
FOR SELECT 
[status] 
,[dTime] 
,[sys_ID] 
FROM 
[dbo].[test_data] 

OPEN test_cur 
FETCH NEXT FROM test_cur INTO @status, @dTime, @sys_ID 

WHILE @@FETCH_STATUS = 0 
    BEGIN 

     IF @status = 0 
      INSERT [#offline_data] 
        ([dTime] , [sys_ID] , [GroupID]) 
      VALUES 
        (@dTime , @sys_ID , @GroupID) 
     ELSE 
      SET @GroupID += 1 

     FETCH NEXT FROM test_cur INTO @status, @dTime, @sys_ID 
    END 

CLOSE test_cur 
DEALLOCATE test_cur 

SELECT 
    [sys_ID] 'SYSTEM_ID' 
    ,CONVERT(VARCHAR(8) , MIN([dTime]) , 108) 'FROM' 
    ,CONVERT(VARCHAR(8) , MAX([dTime]) , 108) 'TO' 
    ,CONVERT(VARCHAR(8) , DATEADD(mi , DATEDIFF(mi , MIN([dTime]) , MAX([dTime])) , '1900-01-01T00:00:00.000') , 108) 'DURATION' 
FROM 
    #offline_data 
GROUP BY 
    [sys_ID] 
    ,[GroupID] 
Problemi correlati