我正在计算使用此查询的每个用户的总登录时间:
架构:
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。任何选项?谢谢你的建议
答案 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)
答案 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
事件被记录时,您可以在触发器中填充该字段(在现场计算记录的时间实际用户)。
通过这种方式,您已经有时间差异来汇总您的观点。