每个月的余额(按类型)

时间:2018-07-03 10:56:36

标签: sql sql-server tsql

我在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

我需要帮助进行查询以返回每种类型的输入和输出(作为余额)的总和,但它应该在每个月末返回该总和,即:< / p>

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年7月)结束。

3 个答案:

答案 0 :(得分:1)

已获得结果,您可以去

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)

答案 1 :(得分:0)

您可以使用滞后功能,以下代码可能会有所帮助:

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

答案 2 :(得分:0)

假设您的源数据位于您最初提供的结构中(即,这不是另一个查询的结果),这是一个相当简单的转换,它使用日期表和通过有序{{1} }。

如果您已有日期表,则可以删除此脚本中的前2个sum

cte

输出:

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;