Сгенерировать финансовый сводный отчет

Вопрос или проблема

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

Шаги по созданию сводного отчета

  1. Инициализация переменных: Вместо использования курсора, мы можем использовать CTE (Common Table Expressions) или вложенные запросы для агрегации данных по месяцам.

  2. Расчет необходимых величин: Для получения значений TY, LY, TYTD, LYTD можно использовать оконные функции, чтобы избежать множественных запросов.

  3. Формирование окончательного отчета: Объединим результаты в итоговую таблицу.

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

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

Оцените материал
Добавить комментарий

Капча загружается...