需要在mysql中使用if条件和autoincrement编写此更新查询的帮助吗

需要在mysql中使用if条件和autoincrement编写此更新查询的帮助吗,mysql,if-statement,sql-update,Mysql,If Statement,Sql Update,在我们开始之前 表架构: 用户ID |活动日期|时间|差异 其中ActivityDate是用户的活动时间戳 Time_diff是下一个活动和当前活动之间的时间差,以秒为单位 一般来说,但对于用户上次记录的活动,由于没有下一个活动,我将Time_diff设置为-999 例: 我刚刚添加了现场会话id alter table so_time_diff add column session_id int11不为空 我真正的问题是 我想根据以下逻辑为上述每个记录更新此字段: for first reco

在我们开始之前

表架构:

用户ID |活动日期|时间|差异

其中ActivityDate是用户的活动时间戳 Time_diff是下一个活动和当前活动之间的时间差,以秒为单位 一般来说,但对于用户上次记录的活动,由于没有下一个活动,我将Time_diff设置为-999

例:

我刚刚添加了现场会话id

alter table so_time_diff add column session_id int11不为空

我真正的问题是

我想根据以下逻辑为上述每个记录更新此字段:

for first record: set session_id = 1
from second record:
    if previous_record.UserId == this_record.UserId AND previous_record.time_diff <=3600
         set this_record.session_id = previous_record.session_id
    else if previous_record.UserId == this_record.UserId AND previous_record.time_diff >3600
         set this_record.session_id = previous_record.session_id + 1
    else if previous_record.UserId <> this_record.UserId 
         set session_id = 1 ## for a different user, restart
简言之

如果同一用户的两条记录在3600秒的时间间隔内,请分配相同的sessionid,如果不增加sessionid,如果是不同的用户,请重新启动sessionid计数


我以前从未在更新查询中编写过逻辑。这可能吗?非常感谢您的指导

是的,这是可能的。如果时间_diff在后面的记录上,而不是在前面的记录上,这会更容易,但是我们可以让它工作。我们并不真正需要存储的时间差

让它工作的诀窍是真正编写SELECT语句。如果您有一个SELECT语句,它返回要更新的行的键和要分配的值,那么将其转换为更新是很简单的

获取SELECT语句的技巧是使用MySQL用户变量,这取决于MySQL的非保证行为

这是该声明的框架:

SELECT @prev_userid                         AS prev_userid
     , @prev_activitydate                   AS prev_activitydate
     , @sessionid                           AS sessionid
     , @prev_userid := t.userid             AS userid
     , @prev_activitydate := t.activitydate AS activitydate
  FROM (SELECT @prev_userid := NULL, @prev_activitydate := NULL, @sessionid := 1) i
  JOIN so_time_diff t
 ORDER BY t.userid, t.activitydate
我们希望在mytable用户ID activitydate上有一个索引,这样就可以通过索引满足查询,而无需使用昂贵的filesort操作

让我们把它打开一点。首先,三个MySQL用户变量由别名为i的内联视图初始化。我们并不真正关心它返回什么,我们只关心它初始化用户变量。因为我们在联接操作中使用它,所以我们还关心它只返回一行

当处理第一行时,我们有以前分配给用户变量的值,并且我们将当前行的值分配给它们。处理下一行时,上一行的值位于用户变量中,我们将当前行值分配给它们,依此类推

查询中的排序依据很重要;以正确的顺序处理行是至关重要的

但这只是一个开始

下一步是比较当前行和前一行的userid和activitydate值,并确定我们是在同一个sessionid中,还是在不同的会话中,我们需要将sessionid增加1

SELECT @sessionid := @sessionid +
       IF( t.userid = @prev_userid AND
           TIMESTAMPDIFF(SECOND,@prev_activitydate,t.activitydate) <= 3600
       ,0,1) AS sessionid
     , @prev_userid := t.userid             AS userid
     , @prev_activitydate := t.activitydate AS activitydate
  FROM (SELECT @prev_userid := NULL, @prev_activitydate := NULL, @sessionid := 1) i
  JOIN so_time_diff t
 ORDER BY t.userid, t.activitydate
注意:选择列表中表达式的顺序很重要;表达式将按其出现的顺序进行计算。。。如果在检查用户变量之前将当前行中的userid值分配给该用户变量,那么这将不起作用。。。这就是为什么这些作业在选择列表中排在最后

一旦我们有了一个看起来不错的查询,它返回了一个sessionid值,我们想用匹配的userid和activitydate分配给该行,我们就可以在多任务update语句中使用它

UPDATE (
         -- query that generates sessionid for userid, activityid goes here
       ) s
  JOIN so_time_diff t
    ON t.userid = s.userid
   AND t.activitydate = s.activity_date 
   SET t.sessionid = s.sessionid
如果有很多行,这可能会拖很长时间。对于MySQL 5.6之前的版本,我相信别名为s的派生表不会在其上创建任何索引。希望MySQL将使用派生表s作为连接操作的驱动表,并对目标表进行索引查找

后续行动

我完全没有满足每个用户在1时重新启动sessionid的要求。为此,我要修改分配给@sessionid的表达式,只需拆分userid和activitydate的条件测试。如果userid与前一行不同,则返回1。否则,根据activitydate的比较结果,返回@sessionid的当前值,或者返回递增1的当前值

像这样:

SELECT @sessionid := 
       IF( t.userid = @prev_userid 
         , IF( TIMESTAMPDIFF(SECOND,@prev_activitydate,t.activitydate) <= 3600
             , @sessionid
             , @sessionid + 1 )
         , 1 ) 
       AS sessionid
     , @prev_userid := t.userid             AS userid
     , @prev_activitydate := t.activitydate AS activitydate
  FROM (SELECT @prev_userid := NULL, @prev_activitydate := NULL, @sessionid := 1) i
  JOIN so_time_diff t
 ORDER BY t.userid, t.activitydate

注意:没有对这些报表进行测试,这些报表仅经过桌面检查;我已经成功地使用了这个模式无数次。

这是我写的,这很有效

SELECT @sessionid := @sessionid +
   CASE WHEN @prev_userid IS NULL THEN 0 
        WHEN t.UserId <> @prev_userid THEN 1-@sessionid
        WHEN t.UserId = @prev_userid AND
       TIMESTAMPDIFF(SECOND,@prev_activitydate,t.ActivityDate) <= 3600
       THEN 0 ELSE 1
   END          
  AS sessionid 
 , @prev_userid := t.UserId             AS UserId
 , @prev_activitydate := t.ActivityDate AS ActivityDate,
 time_diff
FROM (SELECT @prev_userid := NULL, @prev_activitydate := NULL, @sessionid := 1) i
JOIN example t
ORDER BY t.UserId, t.ActivityDate;

再次感谢@spencer7593为我提供了正确方向的描述性答案

感谢你的努力!!但是您编写的select查询不是以sessionid 1开始的,并且不会随着用户的更改而更改。它将基于以前的用户继续。我已经在下面编写了查询:@rk567:是的,我完全错过了逻辑中的最后一步,将每个用户的sessionid重置回1。我更关心的是概述一种方法,首先是一个SELECT,演示MySQL变量的使用,以及在处理当前行时如何从上一行获得可用的值,然后演示如何添加一些条件测试,以比较 当前行的值与上一行的值相同。
SELECT @sessionid := 
       IF( t.userid = @prev_userid 
         , IF( TIMESTAMPDIFF(SECOND,@prev_activitydate,t.activitydate) <= 3600
             , @sessionid
             , @sessionid + 1 )
         , 1 ) 
       AS sessionid
     , @prev_userid := t.userid             AS userid
     , @prev_activitydate := t.activitydate AS activitydate
  FROM (SELECT @prev_userid := NULL, @prev_activitydate := NULL, @sessionid := 1) i
  JOIN so_time_diff t
 ORDER BY t.userid, t.activitydate
SELECT @sessionid := @sessionid +
   CASE WHEN @prev_userid IS NULL THEN 0 
        WHEN t.UserId <> @prev_userid THEN 1-@sessionid
        WHEN t.UserId = @prev_userid AND
       TIMESTAMPDIFF(SECOND,@prev_activitydate,t.ActivityDate) <= 3600
       THEN 0 ELSE 1
   END          
  AS sessionid 
 , @prev_userid := t.UserId             AS UserId
 , @prev_activitydate := t.ActivityDate AS ActivityDate,
 time_diff
FROM (SELECT @prev_userid := NULL, @prev_activitydate := NULL, @sessionid := 1) i
JOIN example t
ORDER BY t.UserId, t.ActivityDate;