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/>';
    }
}