Sql 看看我在上面链接的其他答案,以获得一个例子或谷歌它 ;with m as (select * from @Table), e as (select * from m where AMT>0), r as (select * from m where AMT<0), ex as ( select *, ROW_NUMBER() over (partition by Client order by [date] ) rn, 1 q from e join FN_NUMBERS(1000) on N<= e.AMT ), rx as ( select *, ROW_NUMBER() over (partition by Client order by [date] ) rn, 1 q from r join FN_NUMBERS(1000) on N<= -r.AMT ), j as ( select isnull(ex.Client, rx.Client) Client, (datediff(DAY, ISNULL(ex.[Date],rx.[Date]), GETDATE()) / 30) dd, (isnull(ex.q,0) - isnull(rx.q,0)) q from ex full join rx on ex.Client = rx.Client and ex.rn = rx.rn where ex.Client is null or rx.Client is null ), mm as ( select j.Client, j.q, isnull(x.n,99) n from j left join (values (0),(1),(2)) x (n) on dd=n ), b as ( select Client, SUM(AMT) balance from m group by Client ), p as ( select b.*, p.[0] as [0-12days], p.[1] as [30-59days], p.[2] as [60-89days], p.[99] as [90days+] from mm pivot (sum(q) for n in ([0],[1],[2],[99])) p left join b on p.Client = b.Client ) select * from p order by 1
再见如果源数据不进行数据透视,这会容易得多。我假设您在某个表中有一个客户、日期和金额,并使用日期将数据透视到日期范围。如果在数据透视之前这样做,会更容易。我怀疑显示的数据是查询的结果。也许你可以提供交易的样本data@xQbert没错。如果数据未透视,您将如何处理它?您可以使用类似于Sql 看看我在上面链接的其他答案,以获得一个例子或谷歌它 ;with m as (select * from @Table), e as (select * from m where AMT>0), r as (select * from m where AMT<0), ex as ( select *, ROW_NUMBER() over (partition by Client order by [date] ) rn, 1 q from e join FN_NUMBERS(1000) on N<= e.AMT ), rx as ( select *, ROW_NUMBER() over (partition by Client order by [date] ) rn, 1 q from r join FN_NUMBERS(1000) on N<= -r.AMT ), j as ( select isnull(ex.Client, rx.Client) Client, (datediff(DAY, ISNULL(ex.[Date],rx.[Date]), GETDATE()) / 30) dd, (isnull(ex.q,0) - isnull(rx.q,0)) q from ex full join rx on ex.Client = rx.Client and ex.rn = rx.rn where ex.Client is null or rx.Client is null ), mm as ( select j.Client, j.q, isnull(x.n,99) n from j left join (values (0),(1),(2)) x (n) on dd=n ), b as ( select Client, SUM(AMT) balance from m group by Client ), p as ( select b.*, p.[0] as [0-12days], p.[1] as [30-59days], p.[2] as [60-89days], p.[99] as [90days+] from mm pivot (sum(q) for n in ([0],[1],[2],[99])) p left join b on p.Client = b.Client ) select * from p order by 1,sql,sql-server,pivot,aggregate-functions,Sql,Sql Server,Pivot,Aggregate Functions,再见如果源数据不进行数据透视,这会容易得多。我假设您在某个表中有一个客户、日期和金额,并使用日期将数据透视到日期范围。如果在数据透视之前这样做,会更容易。我怀疑显示的数据是查询的结果。也许你可以提供交易的样本data@xQbert没错。如果数据未透视,您将如何处理它?您可以使用类似于sum(amount)over(partition by client order by date ASC)的窗口函数来生成客户的运行总计,然后仅透视最后一天的最后总计。源表示例(unpivoted)和该格式的一些示
sum(amount)over(partition by client order by date ASC)
的窗口函数来生成客户的运行总计,然后仅透视最后一天的最后总计。源表示例(unpivoted)和该格式的一些示例数据将是一个良好的开端。但即使这样,你也表现出了不错的整数-500-100+600。。。。我怀疑他们是否都表现得那么好,那么你想如何处理部分呢?从未插入的数据开始,您有更好的机会获得所需的结果。模拟一些样本数据和预期结果。@Leettickett根据我对您的解释的理解,对于ABC公司来说,0-29天应该有500个吗?(我指的是你想要的结果)要么我错过了什么,要么你错过了什么。您的查询看起来像我当前用于透视数据的查询。结果就像我的第一个例子。。。但我需要它看起来像我的第二个例子?@Leettickett你能提供你的源数据和第一个枢轴背后的逻辑吗?你的问题缺乏基本信息。每列是什么意思?我上面的代码目前为您提供了每个日期窗口的运行余额以及当前余额。你到底想要什么?目前我的查询只给出了每个老化桶的余额(我相信和你的一样)。但是,如果你看我的例子,你会发现有信用和借记的记录实际上需要进一步的工作;例如,CDE Co在60-89天内有一个贷方余额,在30-59天内有一个借方余额,因此这些余额可以相互应用,在0-29天内只留下余额。我将在原始问题中添加一个源数据示例。@Leettickett然而,说明一个日期窗口应应用于另一个日期窗口的逻辑是什么?当然是在未来?考虑到截至该时间点的所有交易,您是否仅在每个窗口结束时计算当前余额?其想法是先平衡较旧的借方/贷方。例如,如果您有100英镑的贷记(任何年龄),100英镑的贷记60天,另外100英镑的贷记30天,您会将100英镑的贷记应用于100英镑的60天。希望这有意义?太棒了,谢谢你,我需要花一点时间在它上面,但看起来很彻底!
Client Balance 0-29days 30-59days 60-89days 90days+
ABC Co £500 £0 £250 £250 £0
CDE Co £200 £200 £200 £-200 £0
FGH Co £100 £100 £-50 £-500 £600
IJK Co £-100 £100 £0 £0 £-200
Client Balance 0-29days 30-59days 60-89days 90days+
ABC Co £500 £0 £250 £250 £0
CDE Co £200 £200 £0 £0 £0
FGH Co £100 £100 £0 £0 £50
IJK Co £-100 £0 £0 £0 £-100
declare @t table(PaymentDate date
,Client nvarchar(50)
,Amount decimal(10,2)
);
insert into @t values
('20160920','ABC Co',250),('20161020','ABC Co',250 ),('20161020','CDE Co',200 ),('20161020','CDE Co',200 ),('20160920','CDE Co',-200 ),('20160101','FGH Co',600 ),('20160920','FGH Co',-500 ),('20161020','FGH Co',-100 ),('20161120','FGH Co',100 );
declare @ReportDate date = getdate();
select Client
-- Data aggregated by each period
,sum(Amount) as ClientBalance
,sum(case when PaymentDate between dateadd(d,-29,@ReportDate) and @ReportDate then Amount else 0 end) as [0-29 Days]
,sum(case when PaymentDate between dateadd(d,-59,@ReportDate) and dateadd(d,-30,@ReportDate) then Amount else 0 end) as [30-59 Days]
,sum(case when PaymentDate between dateadd(d,-89,@ReportDate) and dateadd(d,-60,@ReportDate) then Amount else 0 end) as [60-89 Days]
,sum(case when PaymentDate <= dateadd(d,-90,@ReportDate) then Amount else 0 end) as [90+ Days]
,'' as [ ]
-- Data aggregated as a rolling periodic balance
,sum(Amount) as ClientBalance
,sum(case when PaymentDate <= @ReportDate then Amount else 0 end) as [0-29 Days]
,sum(case when PaymentDate <= dateadd(d,-30,@ReportDate) then Amount else 0 end) as [30-59 Days]
,sum(case when PaymentDate <= dateadd(d,-60,@ReportDate) then Amount else 0 end) as [60-89 Days]
,sum(case when PaymentDate <= dateadd(d,-90,@ReportDate) then Amount else 0 end) as [90+ Days]
from @t
group by Client
order by Client;
DECLARE @Table AS TABLE (Client CHAR(6), AMT INT, Date DATE)
INSERT INTO @Table VALUES
('ABC Co',250 ,'2016/09/20')
,('ABC Co',250 ,'2016/10/20')
,('CDE Co',200 ,'2016/11/20')
,('CDE Co',200 ,'2016/10/20')
,('CDE Co',-200,'2016/09/20')
,('FGH Co',600 ,'2016/01/01')
,('FGH Co',-500,'2016/09/20')
,('FGH Co',-50 ,'2016/10/20')
,('FGH Co',100 ,'2016/11/20')
,('IJK Co',-100 ,'2016/01/01')
,('IJK Co',-100 ,'2016/09/20')
;WITH cte AS (
SELECT
Client
,Date
,AMT
,CurrentBalance = SUM(AMT) OVER (PARTITION BY Client)
,BackwardsRunningTotal = SUM(AMT) OVER (PARTITION BY Client ORDER BY Date DESC)
,CurrentBalanceMinusBackwardsRunningTotal = SUM(AMT) OVER (PARTITION BY Client) - SUM(AMT) OVER (PARTITION BY Client ORDER BY Date DESC)
,DateGroup = CASE
WHEN DATEDIFF(day,Date,GETDATE()) BETWEEN 0 AND 29 THEN '0-29days'
WHEN DATEDIFF(day,Date,GETDATE()) BETWEEN 30 AND 59 THEN '30-59days'
WHEN DATEDIFF(day,Date,GETDATE()) BETWEEN 60 AND 89 THEN '60-89days'
WHEN DATEDIFF(day,Date,GETDATE()) >= 90 THEN '90days+'
ELSE 'Unknown Error'
END
,BalanceAtTime = SUM(AMT) OVER (PARTITION BY Client ORDER BY Date)
FROM
@Table
)
, cteWhenCurrentBalanceIsMet AS (
SELECT
Client
,MaxDate = MAX(DATE)
FROM
cte
WHERE
CurrentBalanceMinusBackwardsRunningTotal = 0
GROUP BY
Client
)
, cteAgedDebtPrepared AS (
SELECT
c.Client
,Balance = c.CurrentBalance
,c.DateGroup
,Amt = CASE
WHEN CurrentBalanceMinusBackwardsRunningTotal = 0
THEN ISNULL(LAG(CurrentBalanceMinusBackwardsRunningTotal) OVER (PARTITION BY c.Client ORDER BY Date DESC),AMT)
ELSE AMT
END
FROM
cteWhenCurrentBalanceIsMet m
INNER JOIN cte c
ON m.Client = c.Client
AND m.MaxDate <= c.Date
AND SIGN(c.AMT) = SIGN(c.CurrentBalance)
)
SELECT *
FROM
cteAgedDebtPrepared
PIVOT (
SUM(Amt)
FOR DateGroup IN ([0-29days],[30-59days],[60-89days],[90days+])
) pvt
ORDER BY
Client
Client Balance 0-29days 30-59days 60-89days 90days+
ABC Co 500 NULL 250 250 NULL
CDE Co 200 200 NULL NULL NULL
FGH Co 150 100 NULL NULL 50
IJK Co -200 NULL NULL -100 -100
Create Table Debt (
Client char(6),
Amount money,
[Date] date);
Insert Into Debt
Values
('ABC Co', 250, Convert(date, '20/09/2016', 103)),
('ABC Co', 250, Convert(date, '20/10/2016', 103)),
('CDE Co', 200, Convert(date, '20/11/2016', 103)),
('CDE Co', 200, Convert(date, '20/10/2016', 103)),
('CDE Co', -200, Convert(date, '20/09/2016', 103)),
('FGH Co', 600, Convert(date, '01/01/2016', 103)),
('FGH Co', -500, Convert(date, '20/09/2016', 103)),
('FGH Co', -50, Convert(date, '20/10/2016', 103)),
('FGH Co', 100, Convert(date, '20/11/2016', 103));
With Grouping_cte As (
Select Client, Sum(ABS(Amount)) As Amount,
Case When DateDiff(Day, GetDate(), [Date]) > -30 Then '0-29 days'
When DateDiff(Day, GetDate(), [Date]) > -60 Then '30-59 days'
When DateDiff(Day, GetDate(), [Date]) > -90 Then '60-89 days'
Else '90+ days' End As [Date],
Case When Amount < 0 Then 'In' Else 'Out' End As [Type]
From Debt
Group By Client,
Case When DateDiff(Day, GetDate(), [Date]) > -30 Then '0-29 days'
When DateDiff(Day, GetDate(), [Date]) > -60 Then '30-59 days'
When DateDiff(Day, GetDate(), [Date]) > -90 Then '60-89 days'
Else '90+ days' End,
Case When Amount < 0 Then 'In' Else 'Out' End),
RunningTotals_cte As (
Select Client, Amount, [Date], [Type],
Sum(Amount) Over (Partition By Client, [Type] Order By [Date] Desc) - Amount As RunningTotalFrom,
Sum(Amount) Over (Partition By Client, [Type] Order By [Date] Desc) As RunningTotalTo
From Grouping_cte),
Allocated_cte As (
Select Outs.Client, Outs.Date, Outs.Amount + IsNull(Sum(x.borrowed_qty),0) As AdjustedAmount
From (Select * From RunningTotals_cte Where [Type] = 'Out') As Outs
Left Join (Select * From RunningTotals_cte Where [Type] = 'In') As Ins
On Ins.RunningTotalFrom < Outs.RunningTotalTo
And Outs.RunningTotalFrom < Ins.RunningTotalTo
And Ins.Client = Outs.Client
Cross Apply (
Select Case When ins.RunningTotalTo < Outs.RunningTotalTo Then Case When ins.RunningTotalFrom > Outs.RunningTotalFrom Then -1 * Ins.Amount
Else -1 * (Ins.RunningTotalTo - Outs.RunningTotalFrom) End
Else Case When Outs.RunningTotalFrom > Ins.RunningTotalFrom Then Outs.Amount
Else -1 * (Outs.RunningTotalTo - Ins.RunningTotalFrom) End End) As x (borrowed_qty)
Group By Outs.Client, Outs.Date, Outs.Amount)
--Select * From Allocated_cte;
Select Client,
Sum(AdjustedAmount) As Balance,
Sum(iif([Date] = '0-29 days', AdjustedAmount, Null)) As [0-29 days],
Sum(iif([Date] = '30-59 days', AdjustedAmount, Null)) As [30-59 days],
Sum(iif([Date] = '60-89 days', AdjustedAmount, Null)) As [60-89 days],
Sum(iif([Date] = '90+ days', AdjustedAmount, Null)) As [90+ days]
From Allocated_cte
Group By Client;
DECLARE @Table AS TABLE (Client CHAR(6), AMT INT, Date DATE)
INSERT INTO @Table VALUES
('ABC Co',250 ,'2016/09/20')
,('ABC Co',250 ,'2016/10/20')
,('CDE Co',200 ,'2016/11/20')
,('CDE Co',200 ,'2016/10/20')
,('CDE Co',-200,'2016/09/20')
,('FGH Co',600 ,'2016/01/01')
,('FGH Co',-500,'2016/09/20')
,('FGH Co',-50 ,'2016/10/20')
,('FGH Co',100 ,'2016/11/20')
,('IJK Co',-100 ,'2016/01/01')
,('IJK Co',-100 ,'2016/09/20')
,('LMN Co',-200 ,'2016/01/01')
,('LMN Co', 50 ,'2016/06/10')
,('LMN Co',-100 ,'2016/09/20')
;WITH cteRowNumbers AS (
SELECT *, RowNumber = ROW_NUMBER() OVER (PARTITION BY Client ORDER BY Date DESC)
FROM
@Table
)
, cteRecursive AS (
SELECT
Client
,CurrentBalance = SUM(AMT)
,Date = CAST(GETDATE() AS DATE)
,Amt = CAST(0 AS INT)
,RemainingBalance = SUM(Amt)
,AttributedAmt = 0
,RowNumber = CAST(0 AS BIGINT)
FROM
@Table
GROUP BY
Client
UNION ALL
SELECT
r.Client
,r.CurrentBalance
,c.Date
,c.AMT
,CASE WHEN SIGN(r.CurrentBalance) = SIGN(c.AMT) THEN r.CurrentBalance - c.AMT ELSE r.RemainingBalance END
,CASE
WHEN SIGN(r.CurrentBalance) <> SIGN(c.AMT) THEN 0
WHEN ABS(r.RemainingBalance) < ABS(c.AMT) THEN r.RemainingBalance
ELSE c.AMT END
,c.RowNumber
FROM
cteRecursive r
INNER JOIN cteRowNumbers c
ON r.Client = c.Client
AND r.RowNumber + 1 = c.RowNumber
WHERE
SIGN(r.RemainingBalance) = SIGN(r.CurrentBalance)
)
, ctePrepared AS (
SELECT
Client
,CurrentBalance
,DateGroup = CASE
WHEN DATEDIFF(day,Date,GETDATE()) BETWEEN 0 AND 29 THEN '0-29days'
WHEN DATEDIFF(day,Date,GETDATE()) BETWEEN 30 AND 59 THEN '30-59days'
WHEN DATEDIFF(day,Date,GETDATE()) BETWEEN 60 AND 89 THEN '60-89days'
WHEN DATEDIFF(day,Date,GETDATE()) >= 90 THEN '90days+'
ELSE 'Unknown Error'
END
,AttributedAmt
FROM
cteRecursive
WHERE
RowNumber > 0
AND AttributedAmt <> 0
)
SELECT *
FROM
ctePrepared c
PIVOT (
SUM(AttributedAmt)
FOR DateGroup IN ([0-29days],[30-59days],[60-89days],[90days+])
) pvt
ORDER BY
Client
Client CurrentBalance 0-29days 30-59days 60-89days 90days+
ABC Co 500 NULL 250 250 NULL
CDE Co 200 200 NULL NULL NULL
FGH Co 150 100 NULL NULL 50
IJK Co -200 NULL NULL -100 -100
LMN Co -250 NULL NULL -100 -150
DECLARE @Table AS TABLE (Client CHAR(6), AMT INT, Date DATE)
INSERT INTO @Table VALUES
('ABC Co',250 ,'2016/09/20')
,('ABC Co',250 ,'2016/10/20')
,('CDE Co',200 ,'2016/11/20')
,('CDE Co',200 ,'2016/10/20')
,('CDE Co',-200,'2016/09/20')
,('FGH Co',600 ,'2016/01/01')
,('FGH Co',-500,'2016/09/20')
,('FGH Co',-50 ,'2016/10/20')
,('FGH Co',100 ,'2016/11/20')
,('IJK Co',-200 ,'2016/01/01')
,('IJK Co',100 ,'2016/09/20')
;with
m as (select * from @Table),
e as (select * from m where AMT>0),
r as (select * from m where AMT<0),
ex as (
select *, ROW_NUMBER() over (partition by Client order by [date] ) rn, 1 q
from e
join FN_NUMBERS(1000) on N<= e.AMT
),
rx as (
select *, ROW_NUMBER() over (partition by Client order by [date] ) rn, 1 q
from r
join FN_NUMBERS(1000) on N<= -r.AMT
),
j as (
select
isnull(ex.Client, rx.Client) Client,
(datediff(DAY, ISNULL(ex.[Date],rx.[Date]), GETDATE()) / 30) dd,
(isnull(ex.q,0) - isnull(rx.q,0)) q
from ex
full join rx on ex.Client = rx.Client and ex.rn = rx.rn
where ex.Client is null or rx.Client is null
),
mm as (
select j.Client, j.q, isnull(x.n,99) n
from j
left join (values (0),(1),(2)) x (n) on dd=n
),
b as (
select Client, SUM(AMT) balance
from m
group by Client
),
p as (
select b.*, p.[0] as [0-12days], p.[1] as [30-59days], p.[2] as [60-89days], p.[99] as [90days+]
from mm
pivot (sum(q) for n in ([0],[1],[2],[99])) p
left join b on p.Client = b.Client
)
select *
from p
order by 1
Client balance 0-12days 30-59days 60-89days 90days+
ABC Co 500 NULL 250 250 NULL
CDE Co 200 200 NULL NULL NULL
FGH Co 150 100 NULL NULL 50
IJK Co -100 NULL NULL NULL -100