从查询表中报告N个或更多连续缺席的SQL查询

时间:2012-05-12 09:21:46

标签: mysql sql select count aggregate-functions

我有一个看起来像这样的表:

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个或更多连续课程。基于studentID和2个特定日期之间的特定主题。每个班级都有不同的时间。该表的架构是:

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

出勤状态:1 =现在,0 =缺席

编辑:有问题的问题,以便它更准确,真正描述了我的意图。

3 个答案:

答案 0 :(得分:1)

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

  1. 从表格中选择所有行,按学生,科目和日期排序
  2. 为缺席创建一个运行计数器,初始化为0
  3. 迭代每条记录:
    • 如果学生和/或科目与上一行不同
      • 将计数器重置为0(存在)或1(不存在)
    • 否则,就是学生和科目相同的时候
      • 将计数器设置为0(存在)或加1(不存在)
  4. 然后我意识到这个逻辑很容易使用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

答案 1 :(得分:0)

此查询适用于预期结果:

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'(让我们将你的原始表名称)连接到自己,并最终在这些天检查缺席。不同的是确保结果连续3天以上不会包含重复结果。

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

    ON first_day.subjectID = second_day.subjectID

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

答案 2 :(得分:0)

不幸的是,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