2017-01-17 5 views
3

私は、サービスがオンラインである時間、サービスがオフラインになった時間、平均などのデータに関する簡単な統計を収集しようとしています。行の先頭に戻る行(ROW_NUMBER -1)や2つの状態しか存在しないなど、特定のものに依存します。複数のステータスを持つタイムスタンプ間の平均を取得する

私のデータは、常に事実の後にログの形式で記録されます(つまり、ライブデータはありません)。わたしにとって最大の問題は、2つ以上の州があることです。現在、4つの異なる状態(Enabled、Disabled、Active、Inactive)の可能性があり、それぞれについてデータを収集できるようにしたいと考えています。

サービス名、古いステータス、新しいステータス、タイムスタンプを含むデータが一度に1行ずつ表示されます。現在、データは単一のテーブルに格納されています。私はデータの提供方法を​​変更することはできませんが、格納方法を変更することができ、私はそのテーブルが主な後戻りだと思っています。ここで

データは現在、私のテーブルに終わる可能性が方法の例です:

CREATE TABLE IF NOT EXISTS statusupdates (
    sid int UNIQUE, 
    fullname VARCHAR(64), 
    oldstatus VARCHAR(16), 
    newstatus VARCHAR(16), 
    time TIMESTAMP); 

INSERT INTO statusupdates VALUES 
(null, 'fictHTTP', 'Off', 'On', '2017-01-01 02:20:00'), 
(null, 'faked', 'On', 'Inactive', '2017-01-01 02:25:00'), 
(null, 'ipsum', 'Inactive', 'On', '2017-01-01 02:30:00'), 
(null, 'resultd', 'On', 'Inactive', '2017-01-01 02:35:00'), 
(null, 'ipsum', 'On', 'Active', '2017-01-01 02:40:00'), 
(null, 'fictHTTP', 'On', 'Active', '2017-01-01 02:45:00'), 
(null, 'faked', 'Inactive', 'Off', '2017-01-01 02:50:00'), 
(null, 'ipsum', 'Active', 'Off', '2017-01-01 02:55:00'), 
(null, 'resultd', 'Inactive', 'Off', '2017-01-01 03:00:00'); 

私は私が見つけた一つの方法は、resultdとして、一つの項目にそれを絞ることであると考えています。 SELECT fullname, newstatus, time FROM statusupdates WHERE fullname='resultd' ORDER BY time DESC;のようなものです。その後、そのデータを使って、同じ方法で別のクエリを実行します(降順であるため)一歩進み、そのレコードからnewstatusを取得します。私がそれをタイプすると、それはちょっと混乱しているようです。

また、oldstatusを取得し、2番目のクエリでは、次のレコードのnewstatusを検索するために使用します。しかし、やはり、これはややこしいかもしれません。

私は、これらの2つの理論的な質問も組み合わせる方法があることを知っています。要約すると、私は私の頭をはるかに超えています、許してください!最後に、各ステータスの合計時間、平均時間などの統計を確認したいと思います。私の最大のハードルは、例えば、前のエントリからの時間を得ることができるような方法で、ipsumのすべてのタイムスタンプエントリのような結果を提供するクエリを取得し、すべてのレコードを通過するまでこれを繰り返すことです。

おそらく、私はこれを完全に考えているのではなく、すべてのデータを1つのテーブルに押し込むことによって複雑すぎることでしょう。これは、これまで無関係なアイテムについては2回行っています。

追加の思考:単一のインスタンス、私はSELECT old_status, new_status, time FROM statusupdates WHERE time = '2017-01-01 03:00:00'その後、私は1例えば私のデータを与える2つのタイムスタンプを引くSELECT old_status, new_status, time FROM statusupdates WHERE time < 'timeStamp' AND new_status = 'oldStatus'次に、このようOLD_STATUS使用することができますを行うことができます。しかし、その後、次のステップのためにそれを行う方法、そして次はそのすべてがヒットするまで続けます。

更新、別の考え:いくつかの素晴らしい提案を組み合わせて、ログを逆戻りするのはどうですか? この時点で、読んでいた方向は関係ありません。ステータスが発生すると、不完全なレコードを作成します。それは、end_timeとしてold_statusとtime_stampを含むでしょう。その後、再びそのサービスに出会うと、new_status = old_statusかどうかをチェックし、start_timeとしてtime_stampでレコードを更新します。

これは多くのオーバーヘッドの地獄を引き起こすように思われる。すべてのレコードが存在するかどうかを確認する必要があります。それともそれほど悪くないのでしょうか? たとえばあなたがいずれかを持っていない場合は

select sum(endTime - startTime) from statusUpdate where oldStatus='active' group by fullName 

を:

+0

これはデータストリームのように聞こえる。ストリームを見ましたか? – efekctive

+0

私がやった方が簡単だと思う。しかし、静的なテキストファイルは書き込まれた後ではありません。 – mrUlrik

+0

ファイルデータを集計できますか? – efekctive

答えて

3

データベースのウィンドウ関数にアクセスできますか?もしそうなら、あなたは(フルネームで仕切られた)各レコードの次の行の値を取得することができます

select fullname, 
      newstatus, 
      avg(time_diff) as avg_time 
    from (
      select fullname, 
        oldstatus, 
        newstatus, 
        /* get the time value of the next row for this fullname record */ 
        lead(time) over( 
         partition by fullname 
         order by time 
         rows between 1 following and 1 following 
        ) as next_time, 
        time, 
        next_time - time as time_diff 
      from statusupdates 
     ) as a 
    group by fullname, 
      newstatus 

EDIT

ウィンドウ関数がない場合、あなたはわずかでnext_timeを得ることができますより複雑な方法:

select a.*, 
     b.next_time 
from statusupdates as a 
     left join 
     (
     select a.fullname, 
       a.time, 
       min(b.time) as next_time 
     from statusupdates as a 
       left join 
       statusupdates as b 
       on a.fullname = b.fullname 
       and a.time < b.time 
     group by a.fullname, 
       a.time 
     ) as b 
     on a.fullname = b.fullname 
     and a.time = b.time 
; 
+0

残念ながら私はHSQLDBがLEADをサポートしているとは思いません。 :( – mrUlrik

+0

鉛を使わないものを試して更新しました:-) – Alex

+0

ありがとうございました!実際にはサブクエリに関する素晴らしいチュートリアル。私は1つのステータス更新のすべてのレコードの完全なデータで終わると思いますが、私はすべての行に対してそのクエリを実行しなければならないと思いますか?現在私は約63,000レコードです。もちろん、私がこれを理解すると、範囲を指定することでその数字を減らします。私は、SQLに何らかの形のループがあるのか​​、ループを引き起こすために何かトリッキーなことがあるのか​​どうかはわかりません。 – mrUlrik

1

あなたは今、あなたは簡単にあなたの統計情報を取得するために、SQLクエリを撃つこと

statusUpdate { 
    fullName, 
    oldStatus, 
    newStatus, 
    startTime, 
    endTime 
} 

としてあなたにこのためのデータ構造を見直すことデータベースを制御すると、1つをメモリに入れることができますが、このデータの量が多い場合は非常にコストがかかります。アレックスから

編集

ソリューションは、これまでの最高であるように思われるが、データベースが外に完全にある場合は、ログファイルの保証はソートされたレコードを一覧表示することを指定したログファイルを解析中にあなたがあなたの統計を構築しようとするかもしれ制御します時間によって これは、より少ないメモリスペースを使用し、さらに微調整することができます。

public class Aggregation { 

    String fullName; 
    String prevStatus; 
    String currStatus; 
    Date prevTime; 
    Date currTime; 

    Map<String, List<Long>> timePeriodListMap = new HashMap<>(); 
    Map<String, Long> totalTimeMap = new HashMap<>(); 

    public void add(Status status) { 
     if(!fullName.equals(status.fullName)) { 
      throw new RuntimeException("Wrong "+fullName); 
     } 
     if(!currStatus.equals(status.oldStatus)) { 
      throw new RuntimeException("Previous record's newStatus is not this record's oldStatus"); 
     } 
     if(prevTime.compareTo(status.time) > 0){ 
      throw new RuntimeException("Unsorted by time"); 
     } 

     if(currTime == null) { 
      fullName = status.fullName; 
      prevTime = status.time;    
     } else { 
      if(!timePeriodListMap.containsKey(prevStatus)) { 
       timePeriodListMap.put(prevStatus, new ArrayList<Long>()); 
      } 
      timePeriodListMap.get(prevStatus).add(status.time.getTime() - currTime.getTime()); 
      prevTime = currTime; 
      currTime = status.time; 
     }   
     prevStatus = status.oldStatus; 
     currStatus = status.newStatus;   
    } 

} 

Map<String, Aggregation> statusDB = new HashMap<String, TestClass.Aggregation>(); 
//read from the file as status one by one 
public void process(Status status) {   
    if(!statusDB.containsKey(status.oldStatus)) { 
     Aggregation aggregation = new Aggregation(); 
     statusDB.put(status.fullName, aggregation); 
    } 
    statusDB.get(status.fullName).add(status); 
} 
+0

これは素晴らしいです。ネストされたクエリと結合についての簡単なチュートリアルをありがとう。この情報を使用して、開始日と終了日を含む表を作成できます。これはこれを劇的に単純化するでしょう。今、私は、同じ方法を使用して、データベースが関与する前にこれを行う方法を理解しなければならないと思っています。しかし、私の頭の上から外すと、ファイル全体を最初にメモリにロードしなければならないと思います。なぜなら、私は先に飛び越すからです。 – mrUlrik

+0

私はあなたのソリューションをパフォーマンスの問題のために使用するかもしれません。ありがとうございました。 – mrUlrik

関連する問題