问题: *我正在尝试计算每个学生对特定班级的连续缺席次数。但是,如果该学生上课一天,则需要重新开始计算。
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
有什么想法吗?
答案 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