2010-04-19 20 views
7

Tôi có một tập hợp dữ liệu cho tôi biết nếu một vài hệ thống có sẵn hoặc không phải cứ tăng 5 hoặc 15 phút. Hiện tại, thời gian tăng lên không thành vấn đề.Tìm Hàng Liên tiếp & Tính Thời lượng

Dữ liệu trông như thế này:

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 

Tôi muốn tạo ra một cái nhìn mà nói khi một hệ thống là không có sẵn (tức là khi nó là F), bất cứ điều gì, những gì thời gian, và thời gian mà là - từ.

kết quả mong muốn:

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 

Đây là dữ liệu kịch bản:

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

Cảm ơn bạn trước!

+1

sẽ không bạn muốn truy vấn để đi từ F đầu tiên sau khi T đến T tiếp theo? Hệ thống không nhất thiết phải có sẵn giữa F cuối cùng trong một chuỗi và tiếp theo T. –

+0

Bạn nói đúng. Nó sẽ được tiếp theo T. – MannyKo

Trả lời

2

Có lẽ không tối ưu, nhưng nó hoạt động :)

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 cho giải pháp thử nghiệm (lưu ý nhỏ: theo thứ tự là dư thừa; "Nếu bạn sử dụng GROUP BY, các hàng đầu ra được sắp xếp theo các cột THEO NHÓM THEO như thể bạn có một ORDER BY cho cùng một cột.") – Unreason

+0

I don ' Tôi biết rằng MySQL hoạt động rất lạ :). PostgreSQL và Oracle không garantee phân loại trong khi sử dụng GROUP BY. Sắp xếp trong GROUP BY là một tác dụng phụ. –

+0

Cảm ơn bạn rất nhiều! Điều này đã làm việc! – MannyKo

0

Ít lâu hơn một chút, tuy nhiên dường như hoạt động trong PostgreSQL. Nguyên tắc cơ bản:

  1. find lần mà tình trạng hệ thống thay đổi
  2. chỉ nhận được đầu tiên và lần cuối cùng - nơi tình trạng cuối cùng là trạng thái khác nhau và tiếp theo sẽ là khác nhau (hoặc không có gì cả)
  3. chênh lệch tính toán

đây là mã:

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; 

Nó chỉ là mã nhanh chóng, có thể được đơn giản r giải pháp tôi nghĩ.

+0

Ồ, tôi xin lỗi, tôi đã bỏ qua thẻ mysql. Điều này sẽ không hoạt động trong MySQL vì nó không có chức năng phân tích/cửa sổ theo như tôi biết. – Stiivi

1

Đây là giải pháp dựa trên con trỏ, tôi không biết liệu MySQL có hỗ trợ Phân vùng Do đó lý do cho con trỏ hay không. Điều này đã được thử nghiệm trong SQL 2008 và nó hoạt động, hy vọng nó hoạt động trong MySQL nhưng ít nhất nó sẽ cung cấp cho bạn một ý tưởng

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] 
Các vấn đề liên quan