投稿にリストされている実際の「ユーザー」(ユーザー 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 句を追加して、ここにあるのと同様のパターンを確認してください。