【问题标题】:SQL query that reports N or more consecutive absents from attendance table从考勤表中报告 N 次或多次连续缺勤的 SQL 查询
【发布时间】:2012-05-20 16:58:52
【问题描述】:

我有一张如下所示的表格:

studentID | subjectID | attendanceStatus | classDate  | classTime | lecturerID |
12345678    1234        1                  2012-06-05   15:30:00
87654321
12345678    1234        0                  2012-06-08   02:30:00

我想要一个查询来报告学生是否缺席了 3 个或更多连续课程。基于学生 ID 和 2 个特定日期之间的特定主题。每个班级可以有不同的时间。该表的架构是:

PK(`studentID`, `classDate`, `classTime`, `subjectID, `lecturerID`)

出席状态:1 = 出席,0 = 缺席

编辑:措辞问题,以便更准确,更真实地描述我的意图。

【问题讨论】:

  • 您是在数据库中同时记录出席/缺席还是只记录出席?
  • 显然,“0”出席状态表示缺席,“1”表示出席,因此两者都被记录下来。

标签: mysql sql select count aggregate-functions


【解决方案1】:

我无法为此创建 SQL 查询。因此,我尝试了一个 PHP 解决方案:

  1. 从表中选择所有行,按学生、学科和日期排序
  2. 为缺勤创建一个运行计数器,初始化为0
  3. 遍历每条记录:
    • 如果学生和/或科目与前一行不同
      • 将计数器重置为 0(存在)或 1(不存在)
    • 否则,即学生和科目相同时
      • 将计数器设置为 0(存在)或加 1(不存在)

然后我意识到这个逻辑可以很容易地使用 MySQL 变量来实现,所以:

SET @studentID = 0;
SET @subjectID = 0;
SET @absentRun = 0;

SELECT *,
CASE
    WHEN (@studentID  = studentID) AND (@subjectID  = subjectID) THEN @absentRun := IF(attendanceStatus = 1, 0, @absentRun + 1)
    WHEN (@studentID := studentID) AND (@subjectID := subjectID) THEN @absentRun := IF(attendanceStatus = 1, 0, 1)
END AS absentRun
FROM table4
ORDER BY studentID, subjectID, classDate

您可能可以将此查询嵌套在另一个查询中,该查询选择absentRun >= 3 所在的记录。

SQL Fiddle

【讨论】:

    【解决方案2】:

    很遗憾,mysql 不支持 windows 功能。使用 row_number() 或更好的累积总和(Oracle 支持)会更容易。

    我将描述解决方案。假设您的表中有两个额外的列:

    • ClassSeqNum -- 从 1 开始的序列,每个课程日期递增 1。
    • AbsentSeqNum - 每次学生缺课时从 1 开始的序列,然后在每次缺课时加 1。

    关键的观察是这两个值之间的差异对于连续缺席是恒定的。因为您使用的是 mysql,所以您可能会考虑将这些列添加到表中。在查询中添加它们是很大的挑战,这就是为什么这个答案这么长。

    鉴于关键观察,您的问题的答案由以下查询提供:

    select studentid, subjectid, absenceid, count(*) as cnt
    from (select a.*,  (ClassSeqNum - AbsentSeqNum) as absenceid
          from Attendance a
         ) a
    group by studentid, subjectid, absenceid
    having count(*) > 2
    

    (好的,这给出了每个科目的学生缺勤的每个顺序,但我认为您可以弄清楚如何将其缩减为学生列表。)

    如何分配序列号?在 mysql 中,您需要进行自连接。所以,下面添加了 ClassSeqNum:

    select a.StudentId, a.SubjectId, count(*) as ClassSeqNum
    from Attendance a join
         Attendance a1
         on a.studentid = a1.studentid and a.SubjectId = a1.Subjectid and
            a.ClassDate >= s1.classDate
    group by a.StudentId, a.SubjectId
    

    以下添加缺席序列号:

    select a.StudentId, a.SubjectId, count(*) as AbsenceSeqNum
    from Attendance a join
         Attendance a1
         on a.studentid = a1.studentid and a.SubjectId = a1.Subjectid and
            a.ClassDate >= a1.classDate
    where AttendanceStatus = 0
    group by a.StudentId, a.SubjectId
    

    所以最终的查询看起来像:

    with cs as (
        select a.StudentId, a.SubjectId, count(*) as ClassSeqNum
        from Attendance a join
             Attendance a1
             on a.studentid = a1.studentid and a.SubjectId = a1.Subjectid and
                a.ClassDate >= s1.classDate
        group by a.StudentId, a.SubjectId
    ),
        a as (
        select a.StudentId, a.SubjectId, count(*) as AbsenceSeqNum
        from Attendance a join
             Attendance a1
             on a.studentid = a1.studentid and a.SubjectId = a1.Subjectid and
                a.ClassDate >= s1.classDate
        where AttendanceStatus = 0
        group by a.StudentId, a.SubjectId
    )
    select studentid, subjectid, absenceid, count(*) as cnt
    from (select cs.studentid, cs.subjectid,
                 (cs.ClassSeqNum - a.AbsentSeqNum) as absenceid 
          from cs join
               a
               on cs.studentid = a.studentid and cs.subjectid = as.subjectid
         ) a
    group by studentid, subjectid, absenceid
    having count(*) > 2
    

    【讨论】:

    • 好的,我修正了查询中的拼写错误,但仍然无法执行。似乎最终的 select 语句失败了。它在第 1 行给出了错误,但它在其他地方。
    【解决方案3】:

    此查询适用于预期结果:

    SELECT DISTINCT first_day.studentID 
    FROM student_visits first_day
    LEFT JOIN student_visits second_day
        ON first_day.studentID = second_day.studentID
        AND DATE(second_day.classDate) - INTERVAL 1 DAY = date(first_day.classDate)
    LEFT JOIN student_visits third_day
        ON first_day.studentID = third_day.studentID
        AND DATE(third_day.classDate) - INTERVAL 2 DAY = date(first_day.classDate)
    WHERE first_day.attendanceStatus = 0 AND second_day.attendanceStatus = 0 AND third_day.attendanceStatus = 0
    

    它在每个学生的连续 3 个日期上逐步将表“student_visits”(让我们这样命名您的原始表)连接到自身,最后检查这些天的缺勤情况。 Distinct 确保结果不会包含超过连续缺勤 3 天的重复结果。

    此查询不考虑特定科目的缺勤 - 只是每个学生连续缺勤 3 天或更长时间。要考虑主题,只需在每个 ON 子句中添加 .subjectID:

        ON first_day.subjectID = second_day.subjectID
    

    P.S.:不确定这是最快的方式(至少不是唯一的)。

    【讨论】:

    • 嗯,解决方案有效,但似乎有问题。这主要是因为我用错了这个问题。它应该是连续 3 节课而不是几天。查询确实有效,但我的问题本身是错误的。感谢您的解决方案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-07-20
    • 2017-02-10
    • 1970-01-01
    • 1970-01-01
    • 2012-02-27
    • 2021-08-01
    • 2012-04-21
    相关资源
    最近更新 更多