SQL Sum и Group By для работающего Tally? - PullRequest
2 голосов
/ 31 января 2012

Я полностью переписываю свой вопрос, чтобы упростить его.Извините, если вы прочитали предыдущую версию.(Предыдущая версия этого вопроса включала очень сложный пример запроса, который отвлекал от того, что мне действительно нужно.) Я использую 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.Таким образом, сумма всех минут всех уроков для одного и того же ученика на уроках только до даты.

Таким образом, запрос будет возвращен:

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 Student, но это не так.Правильно, потому что, если я не ошибаюсь, это будет включать в себя сумму минут для всех строк для каждого студента, включая строки, которые следуют за строкой, которые не имеют отношения к нужной мне сумме.

ВАРИАНТЫ I 'M ОТКАЗЫВАЯ: я мог сканировать все строки в моем коде, который получает его (хотя это заставило бы меня без необходимости извлекать все строки), но это, очевидно, неэффективно.Я также мог бы поместить туда поле реальных данных и заполнить его, но это также создает проблемы, когда другие записи удаляются или изменяются.

Я понятия не имею, как написать такой запрос вместе.Любое руководство?

Ответы [ 2 ]

1 голос
/ 01 февраля 2012

Это прекрасная возможность использовать оконные агрегаты. Хитрость в том, что вам нужен 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

Таким образом, фильтр применяется после завершения агрегирования.

Теперь, если вы хотите применить фильтр, который не влияет на агрегацию (например, только запрос данных для определенного учащегося), вы должны применить фильтр к внутреннему запросу, так как обрезка строк, которые не влияют на ранние вычисления (как и данные для других студентов) улучшат производительность.

0 голосов
/ 31 января 2012

Мне кажется, что следующий код послужит вашей цели. Проверьте его: -

    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
...