1

当用户连接应用程序时,我有一个系统每 2 到 5 秒 ping 一次数据库。根据他的连接,ping 时间范围可以更大,比如 10 秒左右。

例子:

Pings: 1,4,6,8,9,12,16,20,50,180,187,189,200,203,206,210 ...

我正在运行一个查询以获取 ping 之间不超过 1 分钟的范围,对它们进行分组,这样我就可以知道用户连接了多长时间:

这是我正在运行以选择结果的查询,正如@fancyPants 在这个问题上所建议的那样: MySQL query to group results by date range?

select
userid, groupnum,
min(ping) as start_date,
max(ping) as end_date,
max(ping) - min(ping) as duration
from (
select
*,
@groupnum := if(@prevUser != userId, @groupnum + 1, @groupnum),
@groupnum := if(ping - @prevTS > 60, @groupnum + 1, @groupnum) as groupnum,
@prevUser := userid,
@prevTS := ping
from
Table1 t
, (select @groupnum:=1, @prevTS:=NULL, @prevUser:=NULL) vars
order by userid, ping
) sq
group by userid, groupnum

产生以下结果:

user: X | start_date: 1   | end_date: 50  | duration: 49
user: X | start_date: 180 | end_date: 210 | duration: 30

我需要帮助,在此查询中添加将执行以下操作的语句。

第一个。将选定的行插入到具有查询返回的完全相同架构的新表中:

id: auto_increment| user: X | start_date: 1   | end_date: 50  | duration: 49
id: auto_increment| user: X | start_date: 180 | end_date: 210 | duration: 30

第二。删除在查询中选择并插入到新表中的选定行。

  • 此查询将由服务器上的 cronjob 每 10 分钟运行一次。所以我可以清理 ping 表,它会受到严重影响,并将我们要显示给冲浪者的值存储到一个新表中。

  • 在新查询中,我需要一个子句来过滤未过期的 ping。未过期的 ping 是在 cron 运行的当前时间之前不超过 60 秒完成的。例如,如果 now = 100,则最后一次抓取的 ping 不能小于 41。这样,当 cron 运行时,我不会从仍在 ping 数据库的用户中选择行。

它可以在一个查询中完成,还是我需要两个?

谢谢,

4

2 回答 2

1

(跟进我之前的回答

ping_timestamp 列中究竟存储了什么?Unix时间戳还是其他?我会假设它是unix时间戳。

创建将保存用户活动数据的表:

create table user_activity (
    user_id    int(11) not null
  , start_date int(11) not null
  , end_date   int(11) not null
  , duration   int(11) not null
);

聚合跳过尚未关闭的区间的数据:

set @rnum = 1;
set @cut_off = unix_timestamp() - 60;

insert
  into user_activity
select user_id
     , min(ping_timestamp) start_date
     , max(ping_timestamp) end_date
     , max(ping_timestamp)-min(ping_timestamp) duration
  from ( select user_id
              , ping_timestamp
              , @rnum := if(ping_timestamp - @prev_ping_ts > 60, @rnum+1, @rnum) rnum
              , @prev_ping_ts := ping_timestamp
           from ping_data
          order by user_id, ping_timestamp
       ) t
 group by user_id, rnum
having end_date <= @cut_off
;

之后我们可以根据 user_activity 表中的数据删除处理过的行:

delete t
  from ping_data t
  join ( select user_id
              , max(end_date) max_timestamp
           from user_activity
          group by user_id
       ) ua
    on t.user_id = ua.user_id
 where t.ping_timestamp <= ua.max_timestamp
;
于 2013-10-07T14:57:08.467 回答
0

除此之外,无法组合插入、删除和选择语句,无论如何我都不推荐它。

好吧,一步一步...

第一个。将选定的行插入到具有查询返回的完全相同架构的新表中

这里有个“窍门”就派上用场了。执行你的查询但写

CREATE TABLE new_ping /*or whatever tablename*/ AS
SELECT ...

这将自动创建一个表(并插入数据),但这通常必须进行调整,因为没有创建主键或索引,并且数据类型有时不适合。您的查询会产生类似这样的结果(可能在执行时有些不同,例如引擎或字符集,这些设置取决于默认设置):

CREATE TABLE `new_ping` (
  `userid` int(11) DEFAULT NULL,
  `groupnum` mediumtext,
  `start_date` int(11) DEFAULT NULL,
  `end_date` int(11) DEFAULT NULL,
  `duration` bigint(12) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8

(您可以通过查询获得以上信息SHOW CREATE TABLE ping;

我建议在表中始终有一个主键。这似乎是一个很好useridgroupnum主键。如果您不知道,您也可以坚持使用自动增量列。无论如何,我会像这样调整表格:

DROP TABLE IF EXISTS new_ping;
CREATE TABLE `new_ping` (
  `userid` int(11) DEFAULT NULL,
  `groupnum` mediumtext,
  `start_date` int(11) DEFAULT NULL,
  `end_date` int(11) DEFAULT NULL,
  `duration` int(12) DEFAULT NULL, /*bigint is certainly too big*/
  primary key (userid, groupnum)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

也许您想在其他列上添加索引...

现在您有了新的创建表语句,删除旧表并使用上述语句(或您的调整)重新创建。我通过在DROP TABLE ...声明上方添加声明来做到这一点CREATE

现在您要插入数据。

INSERT INTO new_ping (userid, groupnum, start_date, end_date, duration)
SELECT ... /*the query in your question*/

下一步...

第二。删除在查询中选择并插入到新表中的选定行。

我在这里有点迷路了。您要删除哪些?旧桌子上的那些,对吧?就像在这个sqlfiddle中一样。但究竟是哪些?您的问题中的查询仅将它们显示为组。把它清除并给我写评论,然后我会继续回答......

于 2013-09-28T11:38:35.213 回答