我完全重写我的问题以简化它。对不起,如果您阅读以前的版本。 (此问题的先前版本包含一个非常复杂的查询示例,它会分散我真正需要的内容。)我正在使用SQL Express。
我有一个课程表。
LessonID StudentID StudentName LengthInMinutes
1 1 Chuck 120
2 2 George 60
3 2 George 30
4 1 Chuck 60
5 1 Chuck 10
这些将按日期排序。 (当然,实际的表是数千条带有日期和其他课程相关数据的记录,但这是一种简化。)
我需要查询此表,以便获取所有行(或按日期范围或学生获取行的子集),但我需要在查询中添加一个新列,我们可以调用PriorLessonMinutes。也就是说,同一学生在PRIOR日期课程中所有课程的所有分钟总和。
因此查询将返回:
LessonID StudentID StudentName LengthInMinutes PriorLessonMinutes
1 1 Chuck 120 0
2 2 George 60 0
3 2 George 30 60 (The sum Length from row 2 only)
4 1 Chuck 60 120 (The sum Length from row 1 only)
5 1 Chuck 10 180 (The sum of Length from rows 1 and 4)
从本质上讲,我需要为每个学生提供课前会议记录总和的运行记录。理想情况下,计数器不应该包含当前行,但如果确实如此,则没有什么大不了的,因为我可以在接收查询的代码中进行减法。
此外,(这很重要)如果我只检索记录的子集(例如,按日期范围),则PriorLessonMinutes必须是考虑未返回的行的总和。
我的第一个想法是使用SUM()和GROUP BY学生,但这是不对的,因为除非我弄错了,否则它将包括每个学生所有行的分钟总和,包括之后的行与我需要的总和无关的行。
我正在拒绝的选项:我可以扫描接收它的代码中的所有行,(虽然这会迫使我不必要地检索所有行)但这显然效率低下。我还可以在其中放置一个真实的数据字段并填充它,但是当其他记录被删除或更改时,这也会出现问题。
我不知道如何一起编写这样的查询。有什么指导吗?
答案 0 :(得分:1)
这是使用窗口聚合的绝佳机会。诀窍是您需要SQL Server 2012 Express。如果你能得到它,那么这就是你要查找的查询:
select *,
sum(LengthInMinutes)
over (partition by StudentId order by LessonId
rows between unbounded preceding and 1 preceding)
as PriorLessonMinutes
from Lessons
请注意,它返回NULL而不是0(零)。如果你坚持使用零,请使用COALESCE函数将NULL转换为零。
我建议使用嵌套查询来限制返回的行数:
select * from
(
select *,
sum(LengthInMinutes)
over (partition by StudentId order by LessonId
rows between unbounded preceding and 1 preceding)
as PriorLessonMinutes
from Lessons
) as NestedLessons
where LessonId > 3 -- this is an example of a filter
这样,在聚合完成后应用过滤器。
现在,如果要应用不影响聚合的过滤器(例如仅查询某个学生的数据),则应将过滤器应用于内部查询,因为修剪不影响该行的查询的行早期计算(如其他学生的数据)将提高绩效。
答案 1 :(得分:0)
我觉得以下代码将满足您的目的。检查一下: -
select Students.StudentID ,Students.First, Students.Last,sum(Lessons.LengthInMinutes)
as TotalPriorMinutes from lessons,students
where Lessons.StartDateTime < getdate()
and Lessons.StudentID = Students.StudentID
and StartDateTime >= '20090130 00:00:00' and StartDateTime < '20790101 00:00:00'
group by Students.StudentID ,Students.First, Students.Last