複数のサーバーのアップ/ダウンログを保持するテーブル(SQL Server 2008 R2)があります。サーバーは定期的にpingされ、その状態(上または下)がこの表に書き込まれます。私は与えられた時間の間、特定のサーバーの合計ダウンタイムを見つけようとしているサーバーログテーブルからの停止時間の取得
CREATE TABLE StatusLog
(
LogID INT PRIMARY KEY,
ServerID INT,
QueryDate DATETIME,
ServerStatus VARCHAR(50)
)
サンプル・データ
INSERT INTO StatusLog
VALUES
(1, '1724', '2016-04-16 09:28:00.000', 'up'),
(2, '1724', '2016-04-16 09:29:00.000', 'up'),
(3, '1724', '2016-04-16 09:30:00.000', 'down'),
(6, '1724', '2016-04-16 09:31:00.000', 'down'),
(8, '1724', '2016-04-16 09:32:00.000', 'down'),
(9, '1724', '2016-04-16 09:33:00.000', 'down'),
(17, '1724', '2016-04-16 09:33:40.000', 'up'),
(18, '1724', '2016-04-16 09:34:00.000', 'up')
:それはこのような構造を有しています。 上記のデータ抽出では、IDが1724のサーバーのステータスは09:30:00に "down"になり、09:33:40に "up"に戻ります。これは220秒の合計ダウンタイムです。
私のアプローチは、次のとおりです。それぞれ「ダウンブロック」については
- 、「ダウン」のレコードを検索し、新しい列にダウン開始時間としてそのQueryDateを設定します。これは速いです。
- 別の新しい列では、その開始時間の後に最初の "up"レコードを見つけて、そのQueryDateを停止時間の終わりとして設定します。これは合理的に速いです。
- ただし、これはダウンブロックの最初のダウンレコードでのみ行い、ダウンブロックでは他のダウンでは行いません。そうしないと、同じダウンタイムが複数回誤って計算されます。これを行うには、行番号を調べる必要があります。これは、物事が乱雑で遅くなる場所です。
- 最後に、それらを互いに抽出し、そのブロックの停止時間があります。
- ダウンタイムを合計して合計ダウンタイムを求めます。
は、しかし私はそれが(各サーバーがログレコードの数十万人を持っている)ひどく遅い、以下のスクリプトを書いた
DECLARE @StartDate DATE = '2016-04-01'
DECLARE @EndDate DATE = '2016-04-30'
DECLARE @ServerID INT = '1724'
;WITH CTE_StatusLog AS
(
SELECT LogID, QueryDate, ServerStatus,
ROW_NUMBER() OVER (ORDER BY QueryDate) AS RN
FROM StatusLog
WHERE ServerID = @ServerID
AND QueryDate BETWEEN @StartDate AND @EndDate
)
SELECT LogID,
QueryDate,
ServerStatus,
RN,
DownStarted = CASE WHEN s1.ServerStatus = 'down'
THEN s1.QueryDate END,
DownEnded = (SELECT TOP 1 QueryDate
FROM CTE_StatusLog AS s2
WHERE s2.QueryDate > s1.QueryDate
AND s1.ServerStatus = 'down'
AND s2.ServerStatus = 'up'
AND (SELECT s3.ServerStatus
FROM CTE_StatusLog AS s3
WHERE s3.RN = s1.RN-1) <> 'down'
ORDER BY s2.QueryDate),
DownDuration = DATEDIFF(SECOND,
CASE WHEN s1.ServerStatus = 'down'
THEN s1.QueryDate END,
(SELECT TOP 1 QueryDate
FROM CTE_StatusLog AS s2
WHERE s2.QueryDate > s1.QueryDate
AND s1.ServerStatus = 'down'
AND s2.ServerStatus = 'up'
AND (SELECT s3.ServerStatus
FROM CTE_StatusLog AS s3
WHERE s3.RN = s1.RN-1) <> 'down'
ORDER BY s2.QueryDate))
FROM CTE_StatusLog AS s1
WHERE QueryDate BETWEEN @StartDate AND @EndDate
ORDER BY s1.RN
出力:
LogID QueryDate ServerStatus RN DownStarted DownEnded DownDuration
1 2016-04-16 09:28:00.000 up 1 NULL NULL NULL
2 2016-04-16 09:29:00.000 up 2 NULL NULL NULL
3 2016-04-16 09:30:00.000 down 3 2016-04-16 09:30:00.000 2016-04-16 09:33:40.000 220
6 2016-04-16 09:31:00.000 down 4 2016-04-16 09:31:00.000 NULL NULL
8 2016-04-16 09:32:00.000 down 5 2016-04-16 09:32:00.000 NULL NULL
9 2016-04-16 09:33:00.000 down 6 2016-04-16 09:33:00.000 NULL NULL
17 2016-04-16 09:33:40.000 up 7 NULL NULL NULL
18 2016-04-16 09:34:00.000 up 8 NULL NULL NULL
私はこれを改善するにはどうすればよいですこのテーブル構造に関して時間を計算する良い方法がありますか?
ニースの答え。私はdown_in_secondsでグループを削除し、その代わりに 'MAX(...)' –
@JamieFで集計する必要があると思います。 。 。はい。次の日付は 'group by 'にあることができますが、各行の2回目の変更です。あなたは正しいです。 –
外からの応募は大変助かりました。ありがとうございます。 –