TSQL计算连续缺失的数量

时间:2011-03-29 04:12:13

标签: tsql subquery

问题: *我正在尝试计算每个学生对特定班级的连续缺席次数。但是,如果该学生上课一天,则需要重新开始计算。

e.g。如果课程 MATH1234 在星期一和星期五上课,而学生 001234 错过星期一,星期五在第1周和星期一第2周,但在星期五第2周出现,则错过星期一和星期五对于第3周,他们对该课程的连续缺席计数将是:

(这是我的课程表的缩减版)

Class Day Week    IsAbsent    ConsecutiveAbs
MATH1234  Mon 1   1       1
MATH1234  Fri 1   1       2
MATH1234  Mon 2   1       3
MATH1234  Fri 2   0       0
MATH1234  Mon 3   1       1
MATH1234  Fri 3   1       2

我有一个名为课程的表,其中包含所有学生及其注册班级的运行列表,以及他们是否缺席任何班级:

课程([学生ID],[班级编号],[行号],[学期],[年],[学期],[周],[日期],[ ClassDate],[IsAbsent],[ReasonCode],[ConsecutiveAbs])

鉴于上表,我目前正在做的是更新Lessons表,更改ConsecutiveAbs的值,如下所示:

UPDATE Lessons
SET ConsecutiveAbs = 
(SELECT ISNULL(SUM(CAST(IsAbsent AS numeric)), 0)
 FROM Lessons AS L3
 WHERE L3.IsAbsent = 1
 AND L1.IsAbsent <> 0
 AND L3.[Student ID] = L1.[Student ID]
 AND L3.[Class Number] = L1.[Class Number]
 AND L3.[Line Number] = L1.[Line Number]
 AND L3.[Year] = L1.[Year]
 AND L3.[ClassDate] <= L1.[ClassDate]
 AND (L3.[ClassDate] > (SELECT MAX(L2.ClassDate)
      FROM Lessons AS L2
      WHERE L2.IsAbsent = 0
      AND L2.[Student ID] = L1.[Student ID]
      AND L2.[Class Number] = L1.[Class Number]
      AND L2.[Line Number] = L1.[Line Number]
      AND L2.[Year] = L1.[Year]
      AND L2.ClassDate < L1.[ClassDate]
 ) OR (SELECT MAX(L2.ClassDate)
       FROM Lessons AS L2
       WHERE L2.IsAbsent = 0
       AND L2.[Student ID] = L1.[Student ID]
       AND L2.[Class Number] = L1.[Class Number]
       AND L2.[Line Number] = L1.[Line Number]
       AND L2.[Year] = L1.[Year]
       AND L2.ClassDate < L1.[ClassDate]
 ) IS NULL))
 FROM Lessons AS L1

但是,这忽视了学生实际参加课程的课程并且只是在计算:(

Class Day Week    IsAbsent    ConsecutiveAbs

MATH1234  Mon 1   1       1
MATH1234  Fri 1   1       2
MATH1234  Mon 2   1       3
MATH1234  Fri 2   0       4
MATH1234  Mon 3   1       5
MATH1234  Fri 3   1       6

有什么想法吗?

4 个答案:

答案 0 :(得分:1)

与上一个问题的答案类似,只是这次它会查找已经参加的课程,而不是仅仅将搜索限制为一周。

UPDATE allLessons
SET ConsecutiveAbs = results.ConsecutiveAbs
FROM 
    Lessons allLessons JOIN
(
    SELECT 
        LessonsAbsent.[Student ID],
        LessonsAbsent.[Class Number],
        LessonsAbsent.[Line Number],
        LessonsAbsent.[Year],
        LessonsAbsent.ClassDate,
        ISNULL(SUM(CAST(IsAbsent AS numeric)), 0) AS ConsecutiveAbs
    FROM 
        Lessons LessonsAbsent JOIN
        Lessons RunningTotalAbsent ON 
            RunningTotalAbsent.IsAbsent = 1
            AND LessonsAbsent.[Student ID] = RunningTotalAbsent.[Student ID]
            AND LessonsAbsent.[Class Number] = RunningTotalAbsent.[Class Number]
            AND LessonsAbsent.[Line Number] = RunningTotalAbsent.[Line Number]
            AND LessonsAbsent.[Year] = RunningTotalAbsent.[Year]
            AND LessonsAbsent.ClassDate >= RunningTotalAbsent.ClassDate

            -- Only include this date in the running total only if the student has not attended a class in-between the absences.
            AND NOT EXISTS (
                SELECT *
                FROM Lessons notAbsent
                WHERE 
                    LessonsAbsent.[Student ID] = notAbsent.[Student ID]
                    AND LessonsAbsent.[Class Number] = notAbsent.[Class Number]
                    AND LessonsAbsent.[Line Number] = notAbsent.[Line Number]
                    AND LessonsAbsent.[Year] = notAbsent.[Year]
                    AND notAbsent.IsAbsent = 0
                    AND notAbsent.ClassDate <= LessonsAbsent.ClassDate
                HAVING MAX(ClassDate) > RunningTotalAbsent.ClassDate
        )
    WHERE LessonsAbsent.IsAbsent = 1   
    GROUP BY
        LessonsAbsent.[Student ID],
        LessonsAbsent.[Class Number],
        LessonsAbsent.[Line Number],
        LessonsAbsent.[Year],
        LessonsAbsent.ClassDate
) results ON
    results.[Student ID] = allLessons.[Student ID]
    AND results.[Class Number] = allLessons.[Class Number]
    AND results.[Line Number] = allLessons.[Line Number]
    AND results.[Year] = allLessons.[Year]
    AND results.ClassDate = allLessons.ClassDate

答案 1 :(得分:0)

这样的东西?

update L2
set L2.ConsecutiveAbs = 
        case 
        when L2.IsAbsent = 0 then 0
        else (  select  TOP 1 L1.ConsecutiveAbs 
                from    Lessons L1
                where   L2.[Student ID] = L1.[Student ID]
                    AND L2.[Class Number] = L1.[Class Number]
                    AND L2.[Line Number] = L1.[Line Number]
                    AND L2.[Year] = L1.[Year]
                    AND L2.ClassDate > L1.[ClassDate]
                ORDER BY L1.ClassDate desc
              )
        end as ConsecutiveAbs
from    Lessons L2

编辑: 添加您的更新。

Edit2:添加了更新

答案 2 :(得分:0)

不确定您是否希望坚持子查询的想法。此问题类型-IMO-使用游标更好地解决(更快,更简单)。如果您选择走这条路线,那么SQL就是这样的。

所以没有让我在答案中粘贴代码。所以我在这里粘贴了代码片段。 http://pastebin.com/ybesdX2G

以下是对文章的介绍,向您展示如何进行游标。 http://msdn.microsoft.com/en-us/library/ms180169.aspx

编辑: 光标方法一次在一行上工作,并记住最后一行是否缺席/存在。因此,正确排序数据非常重要。

请注意,代码段中没有ORDER BY。我针对您在问题中提供的数据样本测试了代码段。您的数据已预先排序。所以,它就像一个魅力。

在您的数据库中,如果您的数据未以预先排序的方式存储(我怀疑它是),您需要在第8行添加ORDER BY,以便将数据排序到序列中。

希望这有帮助。

答案 3 :(得分:0)

根据您的情况,这可能没用,但它可以帮助您找到解决方案

select * 
into #orderedlessons
from Lessons
order by [Student ID], [Class Number], [Line Number], [Year], [ClassDate]

declare @tot int
set @tot=0
update #orderedlessons
set @tot = ConsecutiveAbs = Case when IsAbsent=0 then 0 else @tot+1 END;

update lessons
set lessons.ConsecutiveAbs = ordered.ConsecutiveAbs
from lessons inner join  #orderedlessons ordered on
lessons.[Student ID] = ordered.[Student ID]
and lessons.[Class Number] = ordered.[Class Number]
and lessons.[Line Number] = ordered.[Line Number]
and lessons.[Year] = ordered.[Year]
and lessons.ClassDate = ordered.[ClassDate]

drop table #orderedlessons