MySQL查询选择 - >插入和删除所选行

时间:2013-09-27 23:13:15

标签: mysql sql select insert subquery

当用户连接应用程序时,我有一个系统每隔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数据库的用户中选择行。

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

谢谢,

2 个答案:

答案 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;

来获得上述内容

我建议在表格中始终使用主键。 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

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

现在您有了新的create table语句,删除旧表并使用上面的语句(或使用您的调整)重新创建。我是通过在DROP TABLE ...语句上添加CREATE语句来实现的。

现在要插入数据。

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

下一步......

  

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

我在这里有点失落。你想删除哪些?来自旧桌子的那个,对吗?就像在这sqlfiddle中一样。但究竟是哪些?您问题中的查询只会将其显示为组。清除它并写我评论,然后我会继续回答......