Баланс за каждый месяц по типу - PullRequest
0 голосов
/ 03 июля 2018

У меня есть таблица в SQL-Server с несколькими записями входных и выходных значений со столбцами для типа и даты.

Примерно так:

DATE       |INPUT  |OUTPUT |TYPE
2018-01-10 | 256.35|       |A
2018-02-05 |       |  35.00|B
2018-02-15 |  65.30|       |A
2018-03-20 | 158.00|       |B
2018-04-02 |       |  63.32|B
2018-05-12 |       | 128.12|A
2018-06-20 |       |   7.35|B

Мне нужна помощь для создания запроса, который возвращает сумму входов и выходов (в виде баланса) для каждого типа, но он должен возвращать эту сумму в конце каждого месяца , то есть:

YEAR|MONTH|TYPE|BALANCE
2018|    1|A   | 256.35
2018|    1|B   |   0.00
2018|    2|A   | 321.65
2018|    2|B   | -35.00
2018|    3|A   | 321.65
2018|    3|B   | 123.00
2018|    4|A   | 321.65
2018|    4|B   |  59.68
2018|    5|A   | 193.53
2018|    5|B   |  59.68
2018|    6|A   | 193.53
2018|    6|B   |  52.33
2018|    7|A   | 193.53
2018|    7|B   |  52.33

Не забывайте, что баланс каждого месяца зависит от баланса предыдущего месяца, или, другими словами, баланс каждого месяца - это не только движения этого месяца, но и всех предыдущих месяцев.

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

Ответы [ 3 ]

0 голосов
/ 03 июля 2018

Вы можете использовать функцию задержки, ниже может помочь код:

select year(date), month(date), type
, sum(input-output) + isnull(lag(sum(input-output),1,0) over(order by year(date), month(date), type), 0)
from test group by year(date), month(date), type
0 голосов
/ 03 июля 2018

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

Если у вас уже есть таблица дат, вы можете удалить первые 2 cte s в этом сценарии:

declare @t table(DateValue date,InputAmount decimal(8,2),OutputAmount decimal(8,2),ProdType nvarchar(1));
insert into @t values
 ('2018-01-10',256.35,null,'A')
,('2018-02-05',null, 35.00,'B')
,('2018-02-15', 65.30,null,'A')
,('2018-03-20',158.00,null,'B')
,('2018-04-02',null, 63.32,'B')
,('2018-05-12',null,128.12,'A')
,('2018-06-20',null,  7.35,'B')
;

-- Min date can just be min date in the source table, but the max date should be the month end of the max date in the source table0
declare @MinDate date = (select min(DateValue) from @t);
declare @MaxDate date = (select max(dateadd(day,-1,dateadd(month,datediff(month,0,DateValue)+1,0))) from @t);

with n(n) as (select * from (values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) t(t))   -- Using a tally table, built a table of dates
    ,d(d) as (select top(select datediff(day,@MinDate,@MaxDate)+1) dateadd(day,row_number() over (order by (select null))-1,@MinDate) from n n1,n n2,n n3, n n4)
    ,m as (select p.ProdType                                                        -- Then join to the source data to create a date value for each posible day for each product type
                    ,d.d
                    ,dateadd(day,-1,dateadd(month,datediff(month,0,d)+1,0)) as m    -- And calculate a running total using a windowed aggregate
                    ,sum(isnull(t.InputAmount,0) - isnull(t.OutputAmount,0)) over (partition by p.ProdType order by d.d) as RunningTotal
            from d
                cross join (select distinct ProdType
                            from @t
                            ) as p
                left join @t as t
                    on d.d = t.DateValue
                        and p.ProdType = t.ProdType
            )
select m
        ,ProdType
        ,RunningTotal as Balance
from m
where m = d
order by m.d
        ,m.ProdType;

Выход:

+-------------------------+----------+---------+
|            m            | ProdType | Balance |
+-------------------------+----------+---------+
| 2018-01-31 00:00:00.000 | A        |  256.35 |
| 2018-01-31 00:00:00.000 | B        |    0.00 |
| 2018-02-28 00:00:00.000 | A        |  321.65 |
| 2018-02-28 00:00:00.000 | B        |  -35.00 |
| 2018-03-31 00:00:00.000 | A        |  321.65 |
| 2018-03-31 00:00:00.000 | B        |  123.00 |
| 2018-04-30 00:00:00.000 | A        |  321.65 |
| 2018-04-30 00:00:00.000 | B        |   59.68 |
| 2018-05-31 00:00:00.000 | A        |  193.53 |
| 2018-05-31 00:00:00.000 | B        |   59.68 |
| 2018-06-30 00:00:00.000 | A        |  193.53 |
| 2018-06-30 00:00:00.000 | B        |   52.33 |
+-------------------------+----------+---------+
0 голосов
/ 03 июля 2018

Результат достигнут, вот и все:

declare @min_month datetime=(select dateadd(month,datediff(month,0,min([DATE])),0) from _yourtable)
declare @max_month datetime=(select dateadd(month,datediff(month,0,max([DATE])),0) from _yourtable)

;WITH months(d) AS (
  select @min_month
  UNION ALL
  SELECT dateadd(month,1,d) -- Recursion
  FROM months
  where dateadd(month,1,d)<=getdate()
)
select distinct
    year(m.d) as YEAR,
    month(m.d) as MONTH,
    types.v as [TYPE]
    ,sum(isnull(t.[INPUT],0)-isnull(t.[OUTPUT],0)) over (partition by types.v order by m.d)
from months m
cross join (select distinct type from _yourtable)types(v)
left join _yourtable t on dateadd(month,datediff(month,0,t.[DATE]),0)=m.d and types.v=t.TYPE
order by m.d,type
option(maxrecursion 0)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...