TSQL计算连续缺勤的次数

发布于 2024-10-27 04:43:24 字数 1892 浏览 3 评论 0原文

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

例如,如果班级MATH1234在周一和周五上课,学生001234错过了第一周的周一、周五和第二周的周一,但参加了第二周的周五,然后错过了周一和周五第三周的星期五,他们该课程的连续缺席计数为:(

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

上课日周缺席连续 Abs
MATH1234 星期一 1 1 1
MATH1234 星期五 1 1 2
MATH1234 周一 2 1 3
MATH1234 星期五 2 0 0
MATH1234 周一 3 1 1
MATH1234 周五 3 1 2

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

课程([学生 ID ]、[班级编号]、[行号]、[学期间]、[年份]、[学期]、[周]、[日期间]、[班级日期]、[缺席]、[原因代码]、[连续Abs])

鉴于上表,我目前正在做的是更新 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

但这忽略了学生实际参加的课程,只是继续计数:(

上课日周缺席连续 Abs

MATH1234 星期一 1 1 1
MATH1234 星期五 1 1 2
MATH1234 周一 2 1 3
MATH1234 星期五 2 0 4
MATH1234 周一 3 1 5
MATH1234 周五 3 1 6

有什么想法吗?

Problem:* I'm trying to calculate the number of consecutive absence each student have for a particular class. However, the count need to be restarted if that student did attend class for one day.

e.g. If class MATH1234 has classes on Monday and Friday and student 001234 missed Monday, Friday for Week 1 and Monday for Week 2 but attended Friday for Week 2, then missed Monday and Friday for Week 3, their consecutive absence count for that class would be:

(this is a cut down version of my Lessons table)

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

I have a table called Lessons that contain a running list of all the students and the classes they are enrolled in and whether they were absent for any classes:

Lessons([Student ID], [Class Number], [Line Number], [Academic Period], [Year], [Term], [Week], [Day Period], [ClassDate], [IsAbsent], [ReasonCode], [ConsecutiveAbs])

Given the table above, what I am currently doing is Updating the Lessons table change the value of ConsecutiveAbs like this:

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

But that disregard the classes where the student actually attended the class and just kept counting :(

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

Any idea?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

白首有我共你 2024-11-03 04:43:24

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

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

Similar to the answer to your previous question, only this time it looks for a class that has been attended, instead of just limiting the search by a week.

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
谁把谁当真 2024-11-03 04:43:24

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

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

This may not be useful depending on your scenario but it may help you get to the solution

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
妄想挽回 2024-11-03 04:43:24

像这样的东西吗?

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

编辑:
添加您的更新。

编辑2:添加了更新

Something like this?

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

Edit:
Add your update around.

Edit2: Added the update

何时共饮酒 2024-11-03 04:43:24

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

所以不允许我将代码粘贴到答案中。所以我把代码片段粘贴在这里。 http://pastebin.com/ybesdX2G

这是向您展示如何使用光标的文章的参考。 http://msdn.microsoft.com/en-us/library/ms180169.aspx编辑


游标方法一次只处理一行,并记住最后一行是否缺失/存在。因此正确排序数据很重要。

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

在你的数据库中,如果你的数据没有以预先排序的方式存储(我怀疑是这样),你将需要在第 8 行添加一个 ORDER BY,以将数据排序到序列中。

希望这有帮助。

Not sure if you are looking to stick to the subquery idea. This problem type -IMO -is better solved (quicker, less complex) using cursors. Here is what the SQL would look like if you choose to go that route.

So didn't let me to paste the code in the answer. So I've pasted the code snippet here. http://pastebin.com/ybesdX2G

Here's a reference to the article showing you how to do cursors. http://msdn.microsoft.com/en-us/library/ms180169.aspx

Edit:
The cursor approach works on one row at a time and remembers whether the last row was absent/present. So sorting the data correctly is important.

Notice that there is no ORDER BY in the code snippet. I tested the code snippet against data sample you had provided in the question. Your data was pre-sorted. So, it worked like a charm.

In your database if your data isn't stored in a pre-sorted fashion(which I doubt it is) you will want to add an ORDER BY to line #8, to sort the data into the sequence.

Hope this helps.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文