iT邦幫忙

3

趣味SQL 260222 伺服器停機時段分析

  • 分享至 

  • xImage
假設有一個網管系統監控server運作情況,每分鐘紀錄一次狀態(up/down)
為了簡化起見,這裡只以一台server為例.
要找出每段 down的:
起始時間(down_start)
結束時間(down_end)
持續時長(duration)
連續 down 的記錄筆數(record_count)

--
create table server_status (
  recorded_at timestamp primary key
, status      text not null check (status in ('up', 'down'))
);

insert into server_status (recorded_at, status) values
-- 正常運作
('2025-01-01 10:00:00', 'up'),
('2025-01-01 10:01:00', 'up'),
('2025-01-01 10:02:00', 'up'),
-- 第一次停機 (3 分鐘)
('2025-01-01 10:03:00', 'down'),
('2025-01-01 10:04:00', 'down'),
('2025-01-01 10:05:00', 'down'),
-- 恢復
('2025-01-01 10:06:00', 'up'),
('2025-01-01 10:07:00', 'up'),
-- 第二次停機 (1 分鐘, 短暫)
('2025-01-01 10:08:00', 'down'),
-- 恢復
('2025-01-01 10:09:00', 'up'),
('2025-01-01 10:10:00', 'up'),
('2025-01-01 10:11:00', 'up'),
('2025-01-01 10:12:00', 'up'),
-- 第三次停機 (5 分鐘)
('2025-01-01 10:13:00', 'down'),
('2025-01-01 10:14:00', 'down'),
('2025-01-01 10:15:00', 'down'),
('2025-01-01 10:16:00', 'down'),
('2025-01-01 10:17:00', 'down'),
-- 恢復
('2025-01-01 10:18:00', 'up'),
('2025-01-01 10:19:00', 'up'),
('2025-01-01 10:20:00', 'up');

---
-- 先來做編號,依照紀錄時間,分兩種,第一種是全部的編號,第二種是依狀態編號

select *
     , row_number() over(order by recorded_at) as rn_all
     , row_number() over(partition by status order by recorded_at) as rn_status
  from server_status;
  
     recorded_at     | status | rn_all | rn_status
---------------------+--------+--------+-----------
 2025-01-01 10:00:00 | up     |      1 |         1
 2025-01-01 10:01:00 | up     |      2 |         2
 2025-01-01 10:02:00 | up     |      3 |         3
 2025-01-01 10:03:00 | down   |      4 |         1
 2025-01-01 10:04:00 | down   |      5 |         2
 2025-01-01 10:05:00 | down   |      6 |         3
 2025-01-01 10:06:00 | up     |      7 |         4
 2025-01-01 10:07:00 | up     |      8 |         5
 2025-01-01 10:08:00 | down   |      9 |         4
 2025-01-01 10:09:00 | up     |     10 |         6
 2025-01-01 10:10:00 | up     |     11 |         7
 2025-01-01 10:11:00 | up     |     12 |         8
 2025-01-01 10:12:00 | up     |     13 |         9
 2025-01-01 10:13:00 | down   |     14 |         5
 2025-01-01 10:14:00 | down   |     15 |         6
 2025-01-01 10:15:00 | down   |     16 |         7
 2025-01-01 10:16:00 | down   |     17 |         8
 2025-01-01 10:17:00 | down   |     18 |         9
 2025-01-01 10:18:00 | up     |     19 |        10
 2025-01-01 10:19:00 | up     |     20 |        11
 2025-01-01 10:20:00 | up     |     21 |        12
(21 筆資料)

-- 再來利用 rn_all, rn_status 計算後分組.
-- 當 down 記錄是連續的,rn_all 和 rn_down 都各自 +1,差值不變。一旦被 up 記錄中斷
-- rn_all 繼續增長但 rn_down 停住,差值就改變了,形成新的 group

with rn as (
select *
     , row_number() over(order by recorded_at) as rn_all
     , row_number() over(partition by status order by recorded_at) as rn_status
  from server_status
)
select recorded_at
     , rn_all - rn_status as grp
  from rn
 where status = 'down'
 order by recorded_at;

     recorded_at     | grp
---------------------+-----
 2025-01-01 10:03:00 |   3
 2025-01-01 10:04:00 |   3
 2025-01-01 10:05:00 |   3
 2025-01-01 10:08:00 |   5
 2025-01-01 10:13:00 |   9
 2025-01-01 10:14:00 |   9
 2025-01-01 10:15:00 |   9
 2025-01-01 10:16:00 |   9
 2025-01-01 10:17:00 |   9
(9 筆資料)

-- 分組之後就簡單了, min(), max(), count() 搭配使用

with rn as (
select *
     , row_number() over(order by recorded_at) as rn_all
     , row_number() over(partition by status order by recorded_at) as rn_status
  from server_status
), grps as (
select recorded_at
     , rn_all - rn_status as grp
  from rn
 where status = 'down'
)
select min(recorded_at) as down_start
     , max(recorded_at) as down_end
     , max(recorded_at) - min(recorded_at)
       + interval '1 minute' as duration
     , count(*) as record_count
  from grps
 group by grp
 order by down_start;
 
     down_start      |      down_end       | duration | record_count
---------------------+---------------------+----------+--------------
 2025-01-01 10:03:00 | 2025-01-01 10:05:00 | 00:03:00 |            3
 2025-01-01 10:08:00 | 2025-01-01 10:08:00 | 00:01:00 |            1
 2025-01-01 10:13:00 | 2025-01-01 10:17:00 | 00:05:00 |            5
(3 筆資料)

與趣味SQL 260217 最長連續登入天數, 其實是一樣的手法.傳送門

圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友回答

立即登入回答