web-dev-qa-db-ja.com

同じ値を持つ「連続した」行を選択する

次のデータを含むテーブルがあります。

userID  tStamp                status
------  -------------------   ------
Jason   2017-10-18 03:20:00   idle  
Brown   2017-10-18 03:20:28   idle  
Brown   2017-10-18 03:25:28   idle  
Brown   2017-10-18 04:00:28   active    
Brown   2017-10-18 04:10:28   active    
Brown   2017-10-18 04:35:28   idle  
Brown   2017-10-18 04:45:28   idle  

連続同じステータスの行を抽出したい。たとえば、「アイドル」のユーザーが費やした時間を表示したいステータスの後に「アクティブ」ステータスが続き、 'idle' statusなど。

単一のSQLクエリでこれを行うにはどうすればよいですか?

私の望ましい出力は次のとおりです:

userID        staus          Duration_in_this_status (min)
------  -------------------   ------
Jason         idle             ---  
Brown         idle              5
Brown         active           10   
Brown         idle             10   
6
user3719749
SELECT userID, status, TIMESTAMPDIFF(minute, MIN(tStamp), MAX(tStamp)) AS duration
FROM (
    SELECT
    t.*
    , @groupNumber := IF(@prev_userID != userID OR @prev_status != status, @groupNumber + 1, @groupNumber) AS gn
    , @prev_userID := userID
    , @prev_status := status
    FROM t
    , (SELECT @groupNumber := 0, @prev_userID := NULL, @prev_status := NULL) var_init_subquery
    ORDER BY userID, tStamp
) sq
GROUP BY gn, userID, status
  • sqlfiddle で実際に動作することを確認してください

以下がその仕組みです。 3つの変数を定義します。 1つはグループ番号を保持し、2つはstatusおよびuserIdの前の行の値を保持します。リレーショナルデータベースでは、指定しない限り順序はありません。これは非常に重要です。 select句では、変数の値が現在の行と異なるかどうかを最初に確認します。ある場合はグループ番号をインクリメントし、ない場合はそのままにします。その後、現在の行の値を割り当てます。したがって、次の行が処理されるときに変数が評価されても、変数は前の行の値を保持します。したがって、ここでも順序は重要です。外側のクエリでは、このグループ番号でグループ化するだけで、タイムスタンプの最小値と最大値を取得できます。

  • ユーザー定義変数についての詳細を読む ここ
3
tombom