0

mysql クエリを作成していますが、行き詰っています... (毎分ログを記録しています) 3 つのテーブルがあります。ログ、log_field、log_value。

logs -> id, create_time
log_value -> id, log_id,log_field_id,value
log_field -> id, name   (one on the entries is  status and username)

ステータスの値は、オンライン、オフライン、およびアイドル状態にすることができます...

私が見たいのは私のクエリからです: 私のログで誰かがステータスから変更されたとき、create_time、username、status の行が必要です。

したがって、特定のユーザーに対して、新しいステータスが表示されるまでクエリで行をスキップする必要があります...そして、ステータスの変更が無視される時間間隔を設定できる必要があります。

誰か助けてくれませんか?

4

1 に答える 1

0

投稿にリストされている実際の「ユーザー」(ユーザー ID など) を区別するものは何もありませんが、「John Smith」という名前が 2 つある場合はどうなりますか。

まず、MySQL @variables の紹介です。これらは、クエリが行を処理している間に実行されるインライン プログラムと考えることができます。変数を作成し、各行が処理されるたびにそれらを変更します。フィールド選択での := 割り当てと同じ順序で、これは重要です。それについては後ほど説明します。

最初の前提を握ります。ログに記録できる/実行できるすべてのフィールドのフィールド値テーブルがあります。そのうちの 2 つが存在します... 1 つはユーザーの名前用で、もう 1 つはログの変更を探しているステータス用です。これらの内部「ID」番号が何であるかはわかりませんが、既存のテーブルごとに固定値にする必要があります。私のシナリオでは、フィールド ID = 1 はユーザー名用であり、フィールド ID 2 = ステータス列であると想定しています...そうでない場合は、どのフィールドがあなたのフィールドであるかを確認するためだけに、フィールド テーブルを取得するためにさらに 2 つの結合が必要になります。欲しかった。明らかに、私の「ID」フィールドの値は本番テーブルと一致しないため、それに応じて変更してください。

ここにクエリがあります...

select FinalAlias.*
from (
select
      PQ.*,
      if( @lastUser = PQ.LogUser, 1, 0 ) as SameUser,
      @lastTime := if( @lastUser = PQ.LogUser, @lastTime, @ignoreTime ) as lastChange,
      if( PQ.create_time > @lastTime + interval 20 minute, 1, 0 ) as BeyondInterval,
      @lastTime := PQ.create_time as chgTime,
      @lastUser := PQ.LogUser as chgUser
   from
      ( select 
              ByStatus.id,
              l.create_time,
              ByStatus.Value LogStatus,
              ByUser.Value LogUser
           from
              log_value as ByStatus
                 join logs l
                    on ByStatus.log_id = l.id
                 join log_value as ByUser
                    on ByStatus.log_id = ByUser.log_id
                   AND ByUser.log_field_id = 1
           where
              ByStatus.log_field_id = 2
           order by
              ByUser.Value,
              l.create_time ) PQ,
      ( select @lastUser := '',
               @lastTime := now(),
               @ignoreTime := now() ) sqlvars 
         ) FinalAlias
   where
          SameUser = 1 
      and BeyondInterval = 1

さて、どうしたものか。最も内側のクエリ (「PreQuery」を表す結果エイリアス PQ) は、field_id = 2 (ステータス列) が存在するすべてのログ値を要求しています。そのログ エントリから、作成時間のログ テーブルに移動します。その間に、同じログ ID のログ値テーブルに再度参加しますが、今回も field_id = 1 を探して、取得できるようにします。ユーザー名。

それが完了したら、ログ ID、作成時間、ステータス値、およびユーザーごとに事前に並べ替えられたすべてのログを取得し、順番に時間指向にします。これは重要なステップです。データは、特定のユーザーの「前回」の時刻とログ ステータスが変更された「次回」の時刻を比較するために、ユーザー/時刻ごとに事前に編成する必要があります。

さて、MySQL @variables. 「sqlvars」クエリ エイリアスが与えられた @variables の別の選択にプレクエリを結合します。これにより、@lastUser、@lastTime、および @ignoreTime の変数が事前に初期化されます。次に、セクションを介してフィールドリストで何をしているかを見てください

      if( @lastUser = PQ.LogUser, 1, 0 ) as SameUser,
      @lastTime := if( @lastUser = PQ.LogUser, @lastTime, @ignoreTime ) as lastChange,
      if( PQ.create_time > @lastTime + interval 20 minute, 1, 0 ) as BeyondInterval,
      @lastTime := PQ.create_time as chgTime,
      @lastUser := PQ.LogUser as chgUser

これは、次の疑似コードをすべてのレコードに対してループで実行するようなものです (同じ人物とそれぞれのログ時間によって既に順番に並べられています)。

FOR EACH ROW IN RESULT SET
    Set a flag "SameUser" = 1 if the value of the @lastUser is the same 
    as the current person record we are looking at

    if the last user is the same as the previous record
       use the @lastTime field as the "lastChange" column
    else
       use the @ignore field as the last change column

    Now, build another flag based on the current record create time
    and whatever the @lastTime value is based on a 20 minute interval.
    set it to 1 if AT LEAST the 20 minute interval has been meet.

    Now the key to the cycling the next record.
    force the @lastTime = current record create_time
    force the @lastUser = current user
END FOR LOOP

したがって、事前クエリの結果として次の場合...(日付部分はオフのまま)

create status   user   sameuser  lastchange  20minFlag   carry to next row compare
07:34  online   Bill     0       09:05       0           07:34   Bill
07:52  idle     Bill     1       07:34       0           07:52   Bill
08:16  online   Bill     1       07:52       1           08:16   Bill
07:44  online   Mark     0       09:05       0           07:44   Mark
07:37  idle     Monica   0       09:05       0           07:37   Monica
08:03  online   Monica   1       07:37       1           08:03   Monica

Bill の最初のレコードに注目してください。彼の前には誰もいなかったので、同じユーザーのフラグ = 0 です。最後の変更は 9:05 (sqlvars 変数の作成時に NOW() を介して) でしたが、「carry to next row compare」を見てください。これは、現在の行が必要に応じて比較された後に @lastTime と @lastUser を設定しています。

Bill の次の行。彼は前の行の最後のユーザーと同じであることがわかるため、SameUser フラグは 1 に設定されます。これで、現在のレコードの「作成時間」と比較するのに適切な「最終時間」があることがわかりました。したがって、7:34 から 7:52 までは 18 分であり、20 分のインターバルよりも短いため、20 分のフラグは 0 に設定されます。現在の 7:52 と 3 行目の Bill を保持します。

Bill の 3 行目。まだ同じユーザー (フラグ = 1)、現在の 8:16 と比較して 7:52 の最終変更、24 分あります... したがって、20 分のフラグ = 1. 8:16 を保持し、次の行に請求します。

マークの最初の行。最後のユーザーが Bill だったので、同じユーザー = 0。同じ 9:05 の無視時間を使用し、20 分フラグを気にしませんが、7:44 を保存し、次の行の比較のためにマークします。

モニカへ。Mark とは違うので、SameUser=0 などで Bill と同じように仕上げます。

これで、すべてのピースと行が考慮されました。ここで、これらすべてを取得して、クエリの「FinalAlias」としてラップします。「SameUser = 1」および「20 分フラグ」に到達した場合に WHERE 句を適用するだけです。

必要に応じて最後の列リストを削除し、where 句を削除して結果を確認できますが、name/create_time の外側の ORDER BY 句を追加して、ここにあるのと同様のパターンを確認してください。

于 2013-09-28T13:23:29.807 に答える