尽管您无法区分帖子中列出的实际“用户”(例如通过用户 ID),但如果您有两个“约翰·史密斯”名称会发生什么。
首先,介绍一下 MySQL @variables。您可以将它们视为在查询处理行时运行的内联程序。您创建变量,然后在处理每一行时更改它们,以与字段选择中的 := 分配相同的顺序发生,这很关键。我将很快介绍这一点。
拳头一个初始前提。您有一个字段值表,其中包含可以/确实记录的所有可能字段。其中,存在两个......一个是用户名,另一个是您正在查看的状态日志已更改。我不知道那些内部“ID”号码是什么,但它们必须是每个现有表的固定值。在我的场景中,我假设字段 ID = 1 用于用户名,字段 ID 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
现在,发生了什么事。最里面的查询(结果别名 PQ 表示“PreQuery”)只是询问所有存在 field_id = 2(状态列)的日志值。从该日志条目中,转到日志表以了解它的创建时间......当我们这样做时,再次将同一日志 ID 上的日志值表加入到日志值表中,但这次还要查找 field_id = 1 以便我们可以得到用户名。
完成后,获取日志 ID、创建时间、状态值以及所有用户的日志 ID,并按时间顺序对所有用户进行排序。这是关键的一步。数据必须按用户/时间预先组织,以将给定用户的“上一次”时间与其日志状态更改的“下一次”时间进行比较。
现在,MySQL @variables。将预查询加入另一个选择的@variables,它被赋予“sqlvars”查询别名。这将预初始化@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
注意比尔的第一条记录。标志相同的用户= 0,因为在他之前没有人。最后一次更改是 9:05(在创建 sqlvars 变量时通过 NOW()),然后查看“进行到下一行比较”。这是在根据需要比较当前行之后设置@lastTime 和@lastUser。
比尔的下一行。它看到他与上一行的最后一个用户相同,因此 SameUser 标志设置为 1。我们现在知道我们有一个很好的“上次时间”来与当前记录“创建时间”进行比较。因此,从 7:34 到 7:52 是 18 分钟,比我们的 20 分钟间隔少,因此 20 分钟标志设置为 0。现在,我们保留当前的 7:52 和第三行的 Bill。
比尔的第三行。仍然是同一用户(标志 = 1),与现在的 8:16 相比,最后一次更改是 7:52,我们有 24 分钟......所以 20 分钟标志 = 1。保留 8:16 和下一行的账单。
马克的第一行。自上一个用户是比尔以来,相同用户 = 0。使用相同的 9:05 忽略时间并且不关心 20 分钟标志,但现在保存 7:44 并标记下一行比较。
到莫妮卡。与 Mark 不同,因此 SameUser = 0 等完成类似于 Bill。
所以,现在我们已经考虑了所有的部分和行。现在,将所有这些作为查询的“FinalAlias”包装起来,我们所做的就是为“SameUser = 1”和“20 分钟标志”应用一个 WHERE 子句。
您可以根据需要删除最后的列列表,并删除 where 子句以查看结果,但请确保为 name/create_time 添加外部 ORDER BY 子句以查看与我在这里类似的模式。