Вопрос или проблема
У меня есть представление, FinancialSummaryView
, агрегирующее общие суммы сборов, общие суммы возвратов и чистую сумму (сборы – возвраты) по годам, месяцам и местоположениям. Полученные данные выглядят так:
SchoolId | LocationId | Year | Month | TotalCharges | TotalRefunds | NetTotal |
---|---|---|---|---|---|---|
3FA85F64-5717-4562-B3FC-2C963F66AFA6 | 3FA85F64-5717-4562-B3FC-2C963F66AFA6 | 2023 | 7 | 90.00 | 0.00 | 90.00 |
3FA85F64-5717-4562-B3FC-2C963F66AFA6 | 3FA85F64-5717-4562-B3FC-2C963F66AFA6 | 2023 | 8 | 180.00 | 0.00 | 180.00 |
3FA85F64-5717-4562-B3FC-2C963F66AFA6 | 3FA85F64-5717-4562-B3FC-2C963F66AFA6 | 2023 | 9 | 90.00 | 0.00 | 90.00 |
3FA85F64-5717-4562-B3FC-2C963F66AFA6 | 3FA85F64-5717-4562-B3FC-2C963F66AFA6 | 2023 | 10 | 90.00 | 0.00 | 90.00 |
3FA85F64-5717-4562-B3FC-2C963F66AFA6 | 3FA85F64-5717-4562-B3FC-2C963F66AFA6 | 2023 | 11 | 90.00 | 0.00 | 90.00 |
3FA85F64-5717-4562-B3FC-2C963F66AFA6 | 3FA85F64-5717-4562-B3FC-2C963F66AFA6 | 2023 | 12 | 210.00 | 0.00 | 210.00 |
3FA85F64-5717-4562-B3FC-2C963F66AFA6 | 3FA85F64-5717-4562-B3FC-2C963F66AFA6 | 2024 | 1 | 210.00 | 0.00 | 210.00 |
3FA85F64-5717-4562-B3FC-2C963F66AFA6 | 3FA85F64-5717-4562-B3FC-2C963F66AFA6 | 2024 | 2 | 210.00 | 0.00 | 210.00 |
3FA85F64-5717-4562-B3FC-2C963F66AFA6 | 3FA85F64-5717-4562-B3FC-2C963F66AFA6 | 2024 | 3 | 210.00 | 0.00 | 210.00 |
3FA85F64-5717-4562-B3FC-2C963F66AFA6 | 3FA85F64-5717-4562-B3FC-2C963F66AFA6 | 2024 | 4 | 210.00 | 0.00 | 210.00 |
3FA85F64-5717-4562-B3FC-2C963F66AFA6 | 3FA85F64-5717-4562-B3FC-2C963F66AFA6 | 2024 | 5 | 455.00 | 0.00 | 455.00 |
3FA85F64-5717-4562-B3FC-2C963F66AFA6 | 3FA85F64-5717-4562-B3FC-2C963F66AFA6 | 2024 | 6 | 215.00 | 0.00 | 215.00 |
3FA85F64-5717-4562-B3FC-2C963F66AFA6 | 3FA85F64-5717-4562-B3FC-2C963F66AFA6 | 2024 | 7 | 485.00 | 0.00 | 485.00 |
3FA85F64-5717-4562-B3FC-2C963F66AFA6 | 3FA85F64-5717-4562-B3FC-2C963F66AFA6 | 2024 | 8 | 520.00 | 0.00 | 520.00 |
3FA85F64-5717-4562-B3FC-2C963F66AFA6 | 3FA85F64-5717-4562-B3FC-2C963F66AFA6 | 2024 | 9 | 820.00 | 0.00 | 820.00 |
3FA85F64-5717-4562-B3FC-2C963F66AFA6 | 3FA85F64-5717-4562-B3FC-2C963F66AFA6 | 2024 | 10 | 610.00 | 0.00 | 610.00 |
Для справки, FinancialSummaryView
выглядит следующим образом:
SELECT
[SchoolId],
[LocationId],
DATEPART(YEAR, [Date]) AS [Year],
DATEPART(MONTH, [Date]) AS [Month],
COALESCE(SUM([Amount]),0) AS [TotalCharges],
COALESCE(SUM([RefundAmount]),0) AS [TotalRefunds],
COALESCE(SUM([Amount]),0) - COALESCE(SUM([RefundAmount]), 0) as [NetTotal]
FROM
[Transactions]
GROUP BY
[SchoolId],
[LocationId],
DATEPART(YEAR, [Date]),
DATEPART(MONTH, [Date]);
Я пытаюсь создать дополнительные агрегированные финансовые данные из этого представления, такие как
- Валовой за текущий месяц этого года (
TY
) - Валовой за текущий месяц прошлого года (
LY
) - Разница между двумя валовыми суммами (
TY - LY
) - Накопленные до текущего момента этого года (
TYTD
) - Накопленные до текущего момента прошлого года (
LYTD
) - Разница между двумя накопленными суммами (
TYTD - LYTD
)
Для значений накопленных до текущего момента они должны учитывать только суммы за соответствующий год для всех месяцев до и включая текущий месяц.
Результаты должны выглядеть примерно так:
TY | LY | TY v. LY | TYTD | LYTD | TYTD V. LYTD | |
---|---|---|---|---|---|---|
Январь | $210.00 | $0.00 | $210.00 | $210.00 | $0.00 | $210.00 |
Февраль | $210.00 | $0.00 | $210.00 | $420.00 | $0.00 | $420.00 |
Март | $210.00 | $0.00 | $210.00 | $630.00 | $0.00 | $630.00 |
Апрель | $210.00 | $0.00 | $210.00 | $840.00 | $0.00 | $840.00 |
Май | $455.00 | $0.00 | $455.00 | $1295.00 | $0.00 | $1295.00 |
Июнь | $215.00 | $0.00 | $215.00 | $1510.00 | $0.00 | $1510.00 |
Июль | $485.00 | $90.00 | $395.00 | $1995.00 | $90.00 | $1905.00 |
Август | $520.00 | $180.00 | $340.00 | $2515.00 | $270.00 | $2245.00 |
Сентябрь | $820.00 | $90.00 | $730.00 | $3335.00 | $360.00 | $2975.00 |
Октябрь | $610.00 | $90.00 | $520.00 | $3945.00 | $450.00 | $3495.00 |
Я уже реализовал это для одного месяца, используя следующий SQL:
declare @schoolid uniqueidentifier="3FA85F64-5717-4562-B3FC-2C963F66AFA6"
declare @currentDate datetimeoffset="2024-10-24"
declare @previousyear int = Year(DateAdd(year,-1,@currentDate))
declare @month int = Month(@currentDate)
declare @year int = Year(@currentDate)
declare @currentYearMonthGross money
declare @previousYearMonthGross money
declare @currentYearToDateGross money
declare @previousYearToDateGross money
select @currentYearMonthGross = [NetTotal] from dbo.[FinancialSummaryView] where [SchoolId] = @schoolid and [Month] = @month and [Year] = @year
select @currentYearToDateGross = SUM([NetTotal]) from dbo.[FinancialSummaryView] where [SchoolId] = @schoolid and [Month] <= @month and [Year] = @year
select @previousYearMonthGross = [NetTotal] from dbo.[FinancialSummaryView] where [SchoolId] = @schoolid and [Month] = @month and [Year] = @previousyear
select @previousYearToDateGross = SUM([NetTotal]) from dbo.[FinancialSummaryView] where [SchoolId] = @schoolid and [Month] <= @month and [Year] = @previousyear
select @currentYearMonthGross as [TY], @previousYearMonthGross as [LY], @currentYearMonthGross-@previousYearMonthGross as [TY v LY],
@currentYearToDateGross as [TYTD], @previousYearToDateGross as [LYTD], @currentYearToDateGross-@previousYearToDateGross as [TYTD v LYTD]
Я могу написать это как хранимую процедуру, которая генерирует данные для одного месяца. Я также знаю, что могу написать это, используя цикл while для итерации по месяцам, вызывать хранимую процедуру n
раз и хранить результаты во временной таблице/переменной таблицы, чтобы затем выбрать из нее.
Вопрос в следующем: есть ли другие варианты, которые позволят мне упростить задачу, чтобы я мог избежать курсора или цикла и выполнить это основанное на наборе данных?
Ответ или решение
Для создания сводного финансового отчета с использованием представления FinancialSummaryView
и с метриками, указанными в вашем запросе, можно использовать подход, основывающийся на множестве операций в рамках одного запроса без необходимости прибегать к курсорам или циклам. Это позволяет нам оставаться в контексте работы с наборами данных, что существенно повышает производительность системы.
Шаги по созданию сводного отчета
-
Инициализация переменных: Вместо использования курсора, мы можем использовать CTE (Common Table Expressions) или вложенные запросы для агрегации данных по месяцам.
-
Расчет необходимых величин: Для получения значений
TY
,LY
,TYTD
,LYTD
можно использовать оконные функции, чтобы избежать множественных запросов. -
Формирование окончательного отчета: Объединим результаты в итоговую таблицу.
Пример SQL-запроса
Запрос ниже иллюстрирует, как именно это можно сделать:
DECLARE @schoolid UNIQUEIDENTIFIER = '3FA85F64-5717-4562-B3FC-2C963F66AFA6';
DECLARE @currentDate DATETIMEOFFSET = '2024-10-24';
DECLARE @previousyear INT = YEAR(DATEADD(year, -1, @currentDate));
DECLARE @month INT = MONTH(@currentDate);
DECLARE @year INT = YEAR(@currentDate);
WITH FinancialData AS (
SELECT
YEAR(Date) AS [Year],
MONTH(Date) AS [Month],
SUM(CASE WHEN YEAR(Date) = @year THEN NetTotal ELSE 0 END) AS [TY],
SUM(CASE WHEN YEAR(Date) = @previousyear THEN NetTotal ELSE 0 END) AS [LY]
FROM
dbo.FinancialSummaryView
WHERE
SchoolId = @schoolid
GROUP BY
YEAR(Date),
MONTH(Date)
),
CumulativeData AS (
SELECT
Month,
TY,
LY,
SUM(TY) OVER (ORDER BY Month) AS TYTD,
SUM(LY) OVER (ORDER BY Month) AS LYTD
FROM
FinancialData
)
SELECT
Month,
TY,
LY,
TY - LY AS [TY_vs_LY],
TYTD,
LYTD,
TYTD - LYTD AS [TYTD_vs_LYTD]
FROM
CumulativeData
ORDER BY
Month;
Объяснение структуры запроса
-
CTE FinancialData: Этот блок собирает данные по месяцам, агрегируя суммы для текущего года (TY) и прошлого года (LY). Это позволяет нам избежать многократного обращения к представлению.
-
CTE CumulativeData: Здесь рассчитываются накопительные итоговые значения для
TYTD
иLYTD
с использованием оконных функцийSUM() OVER (ORDER BY Month)
. Это эффективный способ получения кумулятивных значений без дополнительных затруднений. -
Итоговый SELECT: Мы собираем все необходимые значения в одном запросе и упорядочиваем вывод по месяцам для удобства восприятия.
Заключение
Данный подход продемонстрировал, как можно в значительной степени упростить процесс сбора и агрегации данных без использования курсоров и циклов, что делает его более производительным и легче поддающимся поддержке. Использование CTE и оконных функций позволяет эффективно работать с наборами данных и получать необходимые финансовые метрики, что соответствует современным стандартам разработки в области информационных технологий.
Данный метод не только упрощает реализацию, но и позволяет легко адаптировать запрос к изменяющимся требованиям или при расширении структуры данных в будущем.