2010-04-19 14 views
7

Tengo un conjunto de datos que me dice si hay un par de sistemas disponibles o no cada 5 o 15 minutos. Por ahora, el incremento de tiempo no debería importar.Buscar filas consecutivas y calcular la duración

Los datos se parece a esto:

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 

Quiero crear una vista que dice que cuando un sistema no está disponible (es decir, cuando es F), a partir de qué momento, a qué hora y duración durante la cual es - de.

resultados deseados:

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 

Estos son los datos de la escritura:

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'); 

gracias de antemano!

+1

no lo haría ¿Quieres hacer una consulta para pasar de la primera F después de una T a la siguiente T? El sistema no está necesariamente disponible entre la última F en una secuencia y la siguiente T. –

+0

Tiene razón. Debería ser al próximo T. – MannyKo

Respuesta

2

Tal vez no es óptima, pero funciona :)

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 para la solución probada (nota menor: ordenar por es redundante; "Si utiliza GROUP BY, las filas de salida se ordenan según las columnas GROUP BY como si tuviera un ORDER BY para las mismas columnas.") – Unreason

+0

I don ' Sé que MySQL funciona muy extraño :). PostgreSQL y Oracle no garantizan la clasificación mientras usan GROUP BY. Ordenar en GROUP BY es un efecto secundario. –

+0

¡Muchas gracias! Esto funcionó! – MannyKo

0

Poco más tiempo, sin embargo, parece funcionar en PostgreSQL. Principio básico:

  1. tiempos localizar el punto donde el estado del sistema cambia
  2. obtener sólo la primera y última vez - en último estado era estatus diferente y el próximo va a ser diferente (o ninguno en absoluto)
  3. diferencia de cómputo

Aquí está el código:

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; 

es sólo código rápido, no puede ser muy sencilla r solución, creo.

+0

Oh, me disculpo, he pasado por alto la etiqueta de mysql. Esto no funcionará en MySQL ya que no tiene funciones de análisis/ventanas, hasta donde yo sé. – Stiivi

1

Aquí está la solución basada en cursor, no sé si MySQL admite la partición Por lo tanto, el motivo de un cursor. Esto ha sido probado en SQL 2008 y funciona, espero que funcione en MySQL pero al menos le dará una 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] 
Cuestiones relacionadas