Mysql 如何获取具有特定值的下一条记录,然后返回一条记录以根据日志计算会话持续时间? 一般问题
给定给定用户的特定记录,我希望获得该用户具有相同列值的下一条记录,然后找到该用户的上一条记录Mysql 如何获取具有特定值的下一条记录,然后返回一条记录以根据日志计算会话持续时间? 一般问题,mysql,sql,subquery,moodle,Mysql,Sql,Subquery,Moodle,给定给定用户的特定记录,我希望获得该用户具有相同列值的下一条记录,然后找到该用户的上一条记录 +----+--------+--------+ | id | userid | action | +----+--------+--------+ | 1 | 2 | a | | 20 | 2 | b | | 21 | 2 | c | | 22 | 2 | c | | 23 | 2 | d | | 5
+----+--------+--------+
| 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对于这一点至关重要
SQLFiddle
使用子查询:
MySQL
也许需要一点php。注销是关键的,因此只有在注销之前立即登录才会被使用——当然,它需要按照userid+时间顺序 然后,我将使用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/>';
}
}
感谢Russell,我正在寻找特定于MySQL的东西,以便将其插入现有的报告工具,而不是为此编写新的PHP模块。这提供了日程安排、灵活的视图、导出、多过滤器等。为了反映这一点,我稍微更新了我的问题,不过,我会尝试设置它,并检查它是否适用于Moodle数据,因为这对其他对Moodle有特定报告要求的用户很有用。这基本上是正确的,尽管不是我所希望的纯MySQL答案-我们无法将查询时间缩短到性能可以接受的程度。在PHP中有多种实现方法,但从一开始,这种方法似乎是明智的。
$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/>';
}
}