2016-05-11 9 views
1

複数のサーバーのアップ/ダウンログを保持するテーブル(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秒の合計ダウンタイムです。

私のアプローチは、次のとおりです。それぞれ「ダウンブロック」については

  1. 、「ダウン」のレコードを検索し、新しい列にダウン開始時間としてそのQueryDateを設定します。これは速いです。
  2. 別の新しい列では、その開始時間の後に最初の "up"レコードを見つけて、そのQueryDateを停止時間の終わりとして設定します。これは合理的に速いです。
  3. ただし、これはダウンブロックの最初のダウンレコードでのみ行い、ダウンブロックでは他のダウンでは行いません。そうしないと、同じダウンタイムが複数回誤って計算されます。これを行うには、行番号を調べる必要があります。これは、物事が乱雑で遅くなる場所です。
  4. 最後に、それらを互いに抽出し、そのブロックの停止時間があります。
  5. ダウンタイムを合計して合計ダウンタイムを求めます。

は、しかし私はそれが(各サーバーがログレコードの数十万人を持っている)ひどく遅い、以下のスクリプトを書いた

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 

私はこれを改善するにはどうすればよいですこのテーブル構造に関して時間を計算する良い方法がありますか?

答えて

1

私はそれぞれのダウンレコードの次のアップタイムを取得することでこれにアプローチします。SQL Server 2008では、これはouter applyを使用しています。

select sl.*, slup.querydate as next_update, 
     datediff(second, sl.querydate, slup.querydate) as down_in_seconds 
from statuslog sl outer apply 
    (select top 1 sl2.* 
     from statuslog sl2 
     where sl2.serverid = sl.serverid and 
      sl2.querydate >= sl.querydate and 
      sl2.serverstatus = 'up' 
     order by sl2.querydate asc 
    ) slup 
where sl.serverstatus = 'down'; 

あなたはダウンタイムによる要約を望んでいた場合、私は集約を使用します。

select servid, min(querydate) as down_date, next_update, 
     max(down_in_seconds) 
from (select sl.*, slup.querydate as next_update, 
      datediff(second, sl.querydate, slup.querydate) as down_in_seconds 
     from statuslog sl outer apply 
      (select top 1 sl2.* 
      from statuslog sl2 
      where sl2.serverid = sl.serverid and 
        sl2.querydate >= sl.querydate and 
        sl2.serverstatus = 'up' 
      order by sl2.querydate asc 
      ) slup 
     where sl.serverstatus = 'down' 
    ) slud 
group by serverid, next_update; 
+0

ニースの答え。私はdown_in_secondsでグループを削除し、その代わりに 'MAX(...)' –

+0

@JamieFで集計する必要があると思います。 。 。はい。次の日付は 'group by 'にあることができますが、各行の2回目の変更です。あなたは正しいです。 –

+0

外からの応募は大変助かりました。ありがとうございます。 –

1

ダウンタイムが必要な場合は、各行の意味を把握することができます。各ダウンローは、そのサーバーの最終チェックからのダウンタイムの秒数を表しているとします。そして、それらの行を合計:前の行だけでなく、反対の状態で前の行と結合

DECLARE @StartDate DATE = '2016-04-01' 
DECLARE @EndDate DATE = '2016-04-30' 
DECLARE @ServerID INT = '1724' 

SELECT 
individualRows.ServerId, 
individualRows.ServerStatus, 
SUM(secondsInState) AS TotalTime 
FROM 
(Select 
statusLog.ServerId, 
statusLog.QueryDate, 
statusLog.ServerStatus, 
DateDiff(second, PreviousStatus.QueryDate, statusLog.QueryDate) as secondsInState 
FROM 
StatusLog 
left outer join 
StatusLog AS PreviousStatus 
ON StatusLog.ServerId = PreviousStatus.ServerId 
AND PreviousStatus.QueryDate < StatusLog.QueryDate 
AND PreviousStatus.QueryDate = (SELECT Max(QueryDate) FROM statusLog sl2 where sl2.ServerId= StatusLog.ServerId and sl2.QueryDate < StatusLog.QueryDate) 
WHERE StatusLog.QueryDate > @StartDate 
AND StatusLog.QueryDate < @EndDate 
AND StatusLog.ServerId = @ServerID) AS individualRows 
GROUP BY 
individualRows.ServerId, 
individualRows.ServerStatus 

あなたが本当に各停電の時間が必要な場合は、私はそれぞれの行に一時テーブルを試してみてください。あなたの結果に似ています。それから私はその一時テーブルをフィルタリングして集計します。

私の経験では、テンポラリテーブルは、テーブルが多くのデータ行を取得するとすぐにCTEよりもはるかに高速です。

+0

あなたは一時テーブルについて正しい、私は行くつもりでしたゴードンのアプローチは十分だった。ありがとうございました。 –

関連する問題