没有子查询的MySQL查询优化

时间:2014-04-28 07:09:34

标签: mysql sql performance optimization

我正在计算使用此查询的每个用户的总登录时间:

架构:

CREATE TABLE `EventLog` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `eventType` varchar(45) DEFAULT NULL,
  `time` datetime DEFAULT NULL,
  `userId` int(11) DEFAULT NULL,
  `timeZone` VARCHAR(45) NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;


INSERT INTO `EventLog` (`eventType`, `time`, `userId`,`timeZone`) VALUES ('LOGIN', '2014-04-27 09:00:04', 1,'GMT-7');
INSERT INTO `EventLog` (`eventType`, `time`, `userId`,`timeZone`) VALUES ('LOGIN', '2014-04-27 10:00:04', 1,'GMT-7');
INSERT INTO `EventLog` (`eventType`, `time`, `userId`,`timeZone`) VALUES ('LOGIN', '2014-04-27 10:00:04', 2,'GMT-6');
INSERT INTO `EventLog` (`eventType`, `time`, `userId`,`timeZone`) VALUES ('LOGOUT', '2014-04-27 09:49:04', 1,'GMT-7');
INSERT INTO `EventLog` (`eventType`, `time`, `userId`,`timeZone`) VALUES ('LOGOUT', '2014-04-27 10:30:04', 1,'GMT-7');
INSERT INTO `EventLog` (`eventType`, `time`, `userId`,`timeZone`) VALUES ('LOGOUT', '2014-04-27 10:30:04', 2,'GMT-6');
INSERT INTO `EventLog` (`eventType`, `time`, `userId`,`timeZone`) VALUES ('LOGIN', '2014-04-27 11:49:04', 3,'GMT-5');
INSERT INTO `EventLog` (`eventType`, `time`, `userId`,`timeZone`) VALUES ('LOGIN', '2014-04-27 08:30:04', 4,'GMT-2');

查询:

select userId,
sum(loginTimeInHour) as loginTimeInHour
from(
    select e1.userId, (TIME_TO_SEC(TIMEDIFF(IFNULL(e2.time,NOW()),e1.time)))/(60*60) as loginTimeInHour from 
    EventLog e1
    left join EventLog e2
    on e1.userId = e2.userId
    and e2.eventType = 'LOGOUT'
    and e1.time < e2.time
    where e1.eventType = 'LOGIN'
    group by e1.id having min(IFNULL(e2.time,NOW()) - e1.time)
) temp
group by userId;

(fiddle)。是否可以在不使用SubQuery的情况下获得相同的结果。实际上我想创建mysql view ,并且查看不要假设SubQuery。任何选项?谢谢你的建议

3 个答案:

答案 0 :(得分:0)

我得到了答案,

SELECT e1.userid, SUM(UNIX_TIMESTAMP(COALESCE(e2.time, NOW())) - 
                      UNIX_TIMESTAMP(e1.time))/3600 total 
FROM eventlog e1 
LEFT JOIN eventlog e2
  ON e1.userid = e2.userid AND e2.eventType='LOGOUT' AND e1.time < e2.time
LEFT JOIN eventlog e3
  ON e1.userid = e3.userid AND e1.time < e3.time AND e3.time < e2.time
WHERE e1.eventType='LOGIN' AND e3.time IS NULL
GROUP BY e1.userid

答案 1 :(得分:-1)

您可以避免子查询获得相同的结果:

select e1.userId,
sum((TIME_TO_SEC(TIMEDIFF(IFNULL(e2.time,NOW()),e1.time)))/(60*60)) 
as loginTimeInHour from
EventLog e1
left join EventLog e2
on e1.userId = e2.userId
and e2.eventType = 'LOGOUT'
and e1.time < e2.time
 where e1.eventType = 'LOGIN'
group by e1.id having min(IFNULL(e2.time,NOW()) - e1.time)

SQL Fiddle

答案 2 :(得分:-1)

您只需以这种方式加入表格即可优化您的查询:

select e1.userId, 
    sum((TIME_TO_SEC(TIMEDIFF(IFNULL(e2.time,NOW()), e1.time)))/(60*60)) as loginTimeInHour
from EventLog e1 left outer join EventLog e2 on
    (e1.userId = e2.userId and e1.time < coalesce(e2.time, NOW()))
    where e1.eventType = 'LOGIN' and coalesce(e2.eventType, 'LOGOUT') = 'LOGOUT'
group by e1.userId; 

<强>更新

由于上面的查询总结了login-time / logout-time的所有变体,因此它不适用于您。

为了满足您的要求,查询将需要双重聚合(例如sum(min())),这是mysql不允许的。必须将此类查询拆分为子查询,就像问题中的查询一样。

不幸的是,您无法在其FROM子句中创建包含子查询的视图,因此我建议您考虑让您的视图记录每个登录会话的持续时间,并在查询时将其汇总到检索中。来自它的数据:

select distinct e1.userId, 
    TIME_TO_SEC(TIMEDIFF(coalesce(e2.time,NOW()), e1.time))/(60*60) as LoginTimeHour
from EventLog e1 left outer join EventLog e2 on
    (e1.userId = e2.userId and e1.time < coalesce(e2.time, NOW()))
    where e1.eventType = 'LOGIN' and coalesce(e2.eventType, 'LOGOUT') = 'LOGOUT'
group by  e1.userId, e1.time;

您还可以考虑使用loggedInTime字段扩展EventLog表,当LOGOUT事件被记录时,您可以在触发器中填充该字段(在现场计算记录的时间实际用户)。

通过这种方式,您已经有时间差异来汇总您的观点。