Mysql查询跳过行并检查状态更改

时间:2013-09-28 10:53:34

标签: mysql

我正在构建一个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。

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

有人可以帮忙吗?

1 个答案:

答案 0 :(得分:0)

虽然您无法区分帖子中列出的实际“用户”(例如用户ID),但如果您有两个“John Smith”名称会发生​​什么。

首先,介绍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

现在,发生了什么。最内层查询(表示“PreQuery”的结果别名PQ)只询问field_id = 2(状态列)存在的所有日志值。从该日志条目开始,转到日志表创建时间...当我们在它时,将AGAIN加入到相同日志ID的日志值表中,但这次也查找field_id = 1以便我们可以获得用户名。

完成后,获取每个用户预先排序的所有日志ID,创建时间,状态值和人员,并按时间顺序排序。这是关键的一步。必须按用户/时间预先组织数据,以将给定用户的“最后”时间与其日志状态更改的“下一个”时间进行比较。

现在,MySQL @variables。将prequery加入到@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

注意Bill的第一条记录。标志相同的用户= 0,因为在他之前没有人。最后一次更改是9:05(在创建sqlvars变量时通过NOW()),但是然后查看“进行到下一行比较”。这是在完成当前行完成后根据需要设置@lastTime和@lastUser。

Bill的下一行。它看到他与上一个用户前一行相同,因此SameUser标志设置为1.我们现在知道我们有一个很好的“上次”与当前记录“创建时间”进行比较。因此,从7:34到7:52是18分钟,比我们的20分钟间隔少,所以20分钟标志设置为0.现在,我们保留当前7:52和Bill为第三排。

Bill的第三行。仍然相同的用户(标志= 1),最后一次更改7:52与现在的8:16相比,我们有24分钟...所以20分钟标志= 1.保留8:16和比尔下一行。

Mark的第一行。自上次用户为Bill以来,User User = 0。使用相同的9:05忽略时间并且不关心20分钟的旗帜,但是现在保存7:44并且标记为下一行比较。

来到莫妮卡。与Mark不同,所以SameUser = 0等与Bill相似。

所以,现在我们考虑了所有的部分和行。现在,取出所有这些并将它们作为查询的“FinalAlias”包装起来,我们所做的就是为“SameUser = 1”和“20分钟标志”应用WHERE子句。

您可以根据需要删除最终列列表,并删除where子句以查看结果,但请确保为name / create_time添加外部ORDER BY子句以查看与此处类似的模式。