我在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月)结束。
答案 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;