连续缺勤的学生(不包括假期)

Consecutive Absent Students Excluding Holidays

提问人:Deep Blue See 提问时间:11/12/2023 最后编辑:O. JonesDeep Blue See 更新时间:11/13/2023 访问量:160

问:

我想找回 124 名 ID 学生,因为我希望在过去 8 天内连续缺席 4 天的学生,忽略周末 H.124 学生在周末前连续缺席了 3 天,周一周末之后他也缺席了,所以他是要找回的人,因为他连续缺席了 4 天

我试过这个工作正常,但这个查询仅适用于 MySQL 8.1而我使用的是旧版本 5.6.41-84.1 和 PHP 版本:7.4.33。

如何在我的MySQL版本上执行此操作?

attendance_id 时间戳 student_id 地位
1 2023-11-05 124 P
2 2023-11-05 125 P
3 2023-11-06 124 一个
4 2023-11-06 125 P
5 2023-11-07 124 一个
6 2023-11-07 125 P
7 2023-11-08 124 一个
8 2023-11-08 125 P
9 2023-11-09 124 H
10 2023-11-09 125 H
11 2023-11-10 124 H
12 2023-11-10 125 H
13 2023-11-11 124 一个
14 2023-11-11 125 P
15 2023-11-12 124 P
16 2023-11-12 125 P
$query = $this->db->query("
select *,
    student_id,
    min(timestamp) timestamp_start,
    max(timestamp) timestamp_end
from (
    select 
        t.*, 
        row_number() over(partition by student_id order by timestamp) rn1,
        row_number() over(partition by student_id, status order by timestamp) rn2
    from attendance t
) t
where status = A AND timestamp BETWEEN (CURRENT_DATE() - INTERVAL 8 DAY) AND CURRENT_DATE()
group by student_id, rn1 - rn2
having count(*) >= 4");
MySQL 间隙和孤岛

评论

0赞 DarkBee 11/12/2023
请在我编辑后仔细检查您的输入数据。您使用的是PDO还是MySQLi?
0赞 DarkBee 11/12/2023
为什么要作为字符串传递?4count(*) >= '4'
0赞 Deep Blue See 11/12/2023
对不起,我做了更正 4 是整数。谢谢。我正在使用MySQLi。
3赞 Dai 11/12/2023
你仍然运行 MySQL 5 的借口是什么?
2赞 O. Jones 11/12/2023
解决方案大纲:您将需要两个不同的子查询,这两个子查询都模拟 ROW_NUMBER() 函数。mysqltutorial.org/mysql-row_number然后加入他们。模仿它是 xxse 脖子上的巨大痛苦,这就是为什么人们建议你升级。

答:

0赞 Olaf Dietsche 11/12/2023 #1

一种方法是使用会话变量,并计算缺勤天数。在当前日期或学生发生变化时重置计数器,例如

select student_id, timestamp,
       case when status = 'A' and student_id = @student then @absent := @absent + 1
            when status = 'A' and student_id <> @student then @absent := 1
            when status = 'P' then @absent := 0 end as absent,
       case when student_id <> @student then @student := student_id end as tracking_student
from attendance, (select @absent := 0, @student := -1) as init
where status in ('A', 'P')
order by student_id, timestamp

看到这个 db-fiddle

student_id 时间戳 缺席 tracking_student
124 2023-11-05 0 124
124 2023-11-06 1
124 2023-11-07 2
124 2023-11-08 3
124 2023-11-11 4
124 2023-11-12 0
125 2023-11-05 1 125
125 2023-11-06 0
125 2023-11-07 0
125 2023-11-08 0
125 2023-11-11 0
125 2023-11-12 0
2赞 user1191247 11/12/2023 #2

有人建议你应该升级四年前最好的部分。你真的应该停止拖延。

这是一个可怕的解决方案,但我认为它有效。它会创建一个类似于基于 ROW_NUMBER 的查询中的分组:

SELECT
    student_id,
    MIN(timestamp) AS ts_start,
    MAX(timestamp) AS ts_end
FROM (
    SELECT attendance.*,
        IF (@prev_student <> student_id, (@prev_student := student_id) AND (@grp := 0), null) AS reset,
        @grp := IF (status = 'P', @grp + 1, @grp) AS grp
    FROM attendance
    JOIN (SELECT @prev_student := 0, @grp := 0) AS init
    WHERE status IN ('A', 'P')
    AND timestamp BETWEEN CURRENT_DATE - INTERVAL 8 DAY
                      AND CURRENT_DATE
    ORDER BY student_id, timestamp
) a
WHERE status = 'A'
GROUP BY student_id, grp
HAVING COUNT(*) >= 4;

派生的表是:

attendance_id 时间戳 student_id 地位 重置 玻璃钢
1 2023-11-05 124 P 0 1
3 2023-11-06 124 一个 1
5 2023-11-07 124 一个 1
7 2023-11-08 124 一个 1
13 2023-11-11 124 一个 1
15 2023-11-12 124 P 2
2 2023-11-05 125 一个 0 0
4 2023-11-06 125 P 1
6 2023-11-07 125 P 2
8 2023-11-08 125 P 3
14 2023-11-11 125 P 4
16 2023-11-12 125 P 5

完整的查询输出:

student_id ts_start ts_end
124 2023-11-06 2023-11-11

这是一个 db<>小提琴

评论

1赞 Deep Blue See 11/13/2023
可怕!!!!!伙计,你就是那个男人。就我而言,这个解决方案是最好的。只需进行一些修改,我就得到了我想要的东西。我对你感激不尽。你们真是太了不起了。
1赞 user1191247 11/13/2023
很高兴它有帮助。请认真考虑升级到受支持的MariaDB / MySQL版本。