1

我試圖從一個包含所有歷史記錄的交易表中創建一個月度銀行賬戶報表。 我希望有第一行的期初餘額,然後使用遞歸cte在當時更新的餘額。 我知道這也可以通過表更新來完成,但遞歸是我正在尋找的。使用遞歸cte的SQL月度報表

表結構會是這樣:

declare @temp table (date datetime,tran_id int,cust_id int,tran_type char,amount int) 
insert into @temp values('2017-06-06 22:05:10.703',1,1,'c',700), 
('2017-06-12 22:05:10.703',2,1,'d',100),('2017-06-20 22:05:10.703',3,1,'c',200), 
('2017-06-26 22:05:10.703',4,1,'d',450),(getdate()+1,5,1,'d',200), 
(getdate()+2,6,1,'d',200),(getdate()+3,7,1,'c',500), 
(getdate()+4,8,1,'d',300),(getdate()+5,9,1,'d',200), 
('2017-06-18 22:05:10.703',12,1,'d',100) 

所以在這裏有交易的6個月和7 當月的期初餘額將在6月的所有交易將被用作總和錨點以獲得遞歸平衡。 現在我想選擇查詢將date,tran_id,cust_id,credit,debit,balance作爲結果集。

所以,如果表中有類似的數據:

date     tran_id cust_id tran_type amount 
2017-06-06 22:05:10.703 1 1 c 700 
2017-06-12 22:05:10.703 2 1 d 100 
2017-06-20 22:05:10.703 3 1 c 200 
2017-06-26 22:05:10.703 4 1 d 450 
2017-07-08 16:34:24.817 5 1 d 200 
2017-07-09 16:34:24.817 6 1 d 200 
2017-07-10 16:34:24.817 7 1 c 500 
2017-07-11 16:34:24.817 8 1 d 300 
2017-07-12 16:34:24.817 9 1 d 200 
2017-06-18 22:05:10.703 12 1 d 100 

The monthly statement for month 7 should be like: 
opening balance of 250 

date      tran_id cust_id credit debit balance 
2017-07-08 16:40:56.810  5  1  NULL  200 50 
2017-07-09 16:40:56.810  6  1  NULL  200 -150 
2017-07-10 16:40:56.810  7  1  500  NULL 350 
2017-07-11 16:40:56.810  8  1  NULL  300 -50 
2017-07-12 16:40:56.810  9  1  NULL  200 -250 

我一直在使用遞歸CTE和總結窗口函數嘗試,但它並沒有被排平衡給予持續的平衡只是一個排。

同樣在cte中使用聚合函數也是如此。

;with cte as 
(
select cust_id,sum(case when tran_type='c' then amount*1 else amount*-1 end) 
as 'opening balance' from @temp 
where MONTH(date)=6 group by cust_id 
), 

cte2 as 
(
select * from cte 
union all 
select t.cust_id,amount+[opening balance] as 'balance1' from @temp t join 
cte2 c on c.cust_id=t.cust_id 
where MONTH(date)=7 
) 
select * from cte2 

OR

;with cte as 
(
select cust_id,sum(case when tran_type='c' then amount*1 else amount*-1 end) 
as 'opening balance' from @temp 
where MONTH(date)=6 group by cust_id 
union all 
select t.cust_id,SUM(amount+[opening balance]) as 'balance1' from @temp t 
join cte c on c.cust_id=t.cust_id 
where MONTH(date)=7 
) 
select * from cte 
option (MAXRECURSION 1000) 

我在想什麼?

+0

你能與預期的結果 –

+0

添加表 –

+0

的SQL Server版本使用的是 –

回答

0

對於舊版本,你可以用CROSS APPLY來計算運行平衡

;WITH opening_balance_cte 
    AS (SELECT cust_id, 
       Sum(CASE WHEN tran_type = 'c' THEN amount ELSE -amount END) AS opening_balance 
     FROM Yourtable 
     WHERE [date] < '2017-07-01' 
     GROUP BY cust_id) 
SELECT *, 
     balance 
FROM Yourtable a 
     JOIN opening_balance_cte ob 
     ON ob.cust_id = a.cust_id 
     CROSS apply (SELECT ob.opening_balance + Sum(CASE WHEN tran_type = 'c' THEN amount ELSE -amount END) AS balance 
        FROM Yourtable b 
        WHERE a.cust_id = b.cust_id 
          AND a.tran_id >= b.tran_id 
          AND b.[date] >= '2017-07-01' 
          AND b.[date] < '2017-08-01') cs 
WHERE a.[date] >= '2017-07-01' 
     AND a.[date] < '2017-08-01' 

如果你真的想知道如何使用Recursive CTE然後

;WITH opening_balance_cte 
    AS (SELECT cust_id, 
       Sum(CASE WHEN tran_type = 'c' THEN amount ELSE -amount END) AS opening_balance 
     FROM @temp 
     WHERE [date] < '2017-07-01' 
     GROUP BY cust_id), 
    rec_cte 
    AS (SELECT TOP 1 a.*, 
         opening_balance + CASE WHEN tran_type = 'c' THEN amount ELSE -amount END AS balance 
     FROM @temp a 
       JOIN opening_balance_cte o 
        ON a.cust_id = o.cust_id 
     WHERE a.[date] >= '2017-07-01' 
       AND a.[date] < '2017-08-01' 
     ORDER BY a.[date] ASC 
     UNION ALL 
     SELECT t.*, 
       rc.balance + CASE WHEN t.tran_type = 'c' THEN t.amount ELSE -t.amount END 
     FROM rec_cte rc 
       JOIN @temp t 
        ON t.cust_id = rc.cust_id 
        AND t.tran_id = rc.tran_id + 1) 
SELECT * 
FROM rec_cte 
+0

yes..this works..correct me if I am wrong,but there is no way this can be done using recursive cte right?because the group by clause .. –

+0

@AnisShaikh - 我們可以使用'遞歸CTE',但這是一個更好的方法 –

+0

@AnisShaikh - 用'遞歸CTE'更新回答 –

0

只需使用累計總和:

select t.*, 
     sum(case when tran_type = 'c' then amount else - amount end) over 
      (partition by cust_id order by date) as balance 
from @temp t; 

然後,您可以使用子查詢或選擇範圍CTE:

select t.* 
from (select t.*, 
      sum(case when tran_type = 'c' then amount else - amount end) over 
       (partition by cust_id order by date) as balance 
     from @temp t 
    ) t 
where . . . 

您需要爲where子查詢,所以它不會影響累計金額。

編輯:

在SQL Server 2008中,你可以使用cross apply

select t.*, 
     t2.balance 
from @temp t cross apply 
    (select sum(case when t2.tran_type = 'c' then t2.amount else - t2.amount end) as balance 
     from @temp t2 
     where t2.cust_id = t.cust_id and t2.date <= t.date 
    ) t2; 
+0

這是有益的,但我是在SQL Server 2008中的情況下,尋找解決方案增加了預期的結果以表格的形式 –