当用户连接应用程序时,我有一个系统每隔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
第二。删除在查询中选择并插入新表的选定行。
此查询将每隔10分钟由服务器上的cronjob运行。所以我可以清理ping表,它会被重击,并将我们要显示给我们冲浪者的值存储到新表中。
在新查询中,我需要一个子句来过滤非过期的ping。非过期的ping是在cron运行的当前时间之前不超过60秒完成的。例如,如果now = 100,则最后一次抓取的ping不能小于41.这样,当cron运行时,我不会从仍在ping数据库的用户中选择行。
可以在一个查询中完成,还是需要两个?
谢谢,
答案 0 :(得分:1)
(跟进my previous answer)
什么存储在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
;
答案 1 :(得分:0)
除此之外,不可能将insert,delete和select语句组合在一起,我不推荐它。
好的,一步一步......
第一。将选定的行插入到新表中,并使用查询返回的令人兴奋的相同模式
这里的“技巧”派上用场了。执行您的查询但写
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;
)
我建议在表格中始终使用主键。 userid
和groupnum
似乎是一个很好的主键。如果你不知道你也可以坚持使用自动增量柱。无论如何,我会像这样调整表格:
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
也许您想在其他列上添加索引......
现在您有了新的create table语句,删除旧表并使用上面的语句(或使用您的调整)重新创建。我是通过在DROP TABLE ...
语句上添加CREATE
语句来实现的。
现在要插入数据。
INSERT INTO new_ping (userid, groupnum, start_date, end_date, duration)
SELECT ... /*the query in your question*/
下一步......
第二。删除在查询中选择并插入新表的选定行。
我在这里有点失落。你想删除哪些?来自旧桌子的那个,对吗?就像在这sqlfiddle中一样。但究竟是哪些?您问题中的查询只会将其显示为组。清除它并写我评论,然后我会继续回答......