运行Tally的SQL Sum和Group By?

时间:2012-01-31 04:58:16

标签: group-by sum sql-server-express

我完全重写我的问题以简化它。对不起,如果您阅读以前的版本。 (此问题的先前版本包含一个非常复杂的查询示例,它会分散我真正需要的内容。)我正在使用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学生,但这是不对的,因为除非我弄错了,否则它将包括每个学生所有行的分钟总和,包括之后的行与我需要的总和无关的行。

我正在拒绝的选项:我可以扫描接收它的代码中的所有行,(虽然这会迫使我不必要地检索所有行)但这显然效率低下。我还可以在其中放置一个真实的数据字段并填充它,但是当其他记录被删除或更改时,这也会出现问题。

我不知道如何一起编写这样的查询。有什么指导吗?

2 个答案:

答案 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