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、用户名、状态的行。

因此,对于给定的用户,我希望我的查询跳过行,直到出现新的状态......而且我需要能够放置一个忽略状态更改的时间间隔。

有人可以帮忙吗?

4

1 回答 1

0

尽管您无法区分帖子中列出的实际“用户”(例如通过用户 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 子句以查看与我在这里类似的模式。

于 2013-09-28T13:23:29.807 回答