给定给定用户的特定记录,我想获得具有相同列值的该用户的下一条记录,然后找到该用户的上一条记录。
+----+--------+--------+
| id | userid | action |
+----+--------+--------+
| 1 | 2 | a |
| 20 | 2 | b |
| 21 | 2 | c |
| 22 | 2 | c |
| 23 | 2 | d |
| 59 | 2 | a |
| 60 | 2 | b |
| 71 | 2 | c |
| 72 | 2 | c |
| 83 | 2 | d |
| 99 | 2 | a |
+----+--------+--------+
所以我想返回以下内容:
+--------+---------+----------+
| userid | left.id | right.id |
+--------+---------+----------+
| 2 | 1 | 23 |
| 2 | 59 | 83 |
+--------+---------+----------+
我试图从Moodle的日志表中估算会话持续时间以进行报告。
例如,用户将登录,生成带有module = user和action = login的日志。如果他们注销,那么这将创建一个带有module = user和action = logout的日志,但这仅在大约20%的情况下发生。登录后将发生一系列其他日志。
可以使用此20%作为平均持续时间计算的样本,但报告要求对每个用户进行近似。
当前的报表工具集成是MySQL驱动的,它提示纯粹在SQL而不是PHP中执行此操作。
所以我将它构建为使用子查询的查询,如下所示:
这似乎有效,但整个数据集的性能相当差。总共有几百万行,但通常报告会对每周或每月总结感兴趣。
我的问题是,是否有更好的解决方法?
根据课程,部门等,有更广泛的要求将从此演变为总持续时间报告,因此最佳SQL对此至关重要。
使用子查询:http://sqlfiddle.com/#!2/42d5ce/6
SELECT l.userid, FROM_UNIXTIME(l.time) as start,
FROM_UNIXTIME(r.time) as end, (r.time - l.time) AS duration
FROM mdl_log AS l
INNER JOIN mdl_log AS r ON r.id = (
SELECT n.id
FROM mdl_log n
WHERE n.id < (
SELECT id
FROM mdl_log t
WHERE l.userid = t.userid
AND t.time > l.time
AND t.module = 'user'
AND t.action = 'login'
LIMIT 0,1
)
AND l.userid = n.userid
ORDER BY n.id DESC
LIMIT 0,1
)
WHERE l.module = 'user'
AND l.action = 'login'
答案 0 :(得分:0)
也许有点像PHP。注销是关键,因此只会使用注销之前的登录 - 当然它需要处于用户ID +时间顺序。
然后我会使用flexi表来显示结果 - 但是你需要为分页做更多的工作。
我也使用get_recordset_sql()而不是get_records(),因为可能会有很多记录。
$sql = "SELECT l.userid,
l.time AS timeaction,
CASE WHEN l.action = 'login' THEN l.time ELSE 0 END AS timestart,
CASE WHEN l.action = 'logout' THEN l.time ELSE 0 END AS timeend
FROM {log} l
WHERE l.module = 'user'
AND l.action IN ('login', 'logout')
ORDER BY l.userid, l.time";
$logs = $DB->get_recordset_sql($sql);
$sessions = array();
if ($logs->valid()) {
$userid = 0;
$timestart = 0;
$timeend = 0;
foreach ($logs as $log) {
if (!empty($log->timestart)) {
// Logged in.
$userid = $log->userid;
$timestart = $log->timestart;
} else if (!empty($log->timeend)) {
// Logged out.
$session = new stdClass();
$session->userid = $userid;
$session->timestart = $timestart;
$session->timeend = $log->timeend;
$sessions[] = $session;
}
}
$logs->close(); // Required for recordset.
// Use a flexitable to display the results properly with paging.
foreach ($sessions as $session) {
echo 'Userid : ' . $session->userid .
' Time start ' . gmdate("Y-m-d H:i:s", $session->timestart) .
' Time end ' . gmdate("Y-m-d H:i:s", $session->timeend) .
' Duration ' . gmdate("H:i:s", $session->timeend - $session->timestart) . '<br/>';
}
}