Skip to content
Advertisement

Hierarchical cumulative sum in sql query (id,parentId) structure

I have two tables that I want to get all accounts and their Credit sum and Debit sum even parent records have to sum thier child values

I’m implementing Id ParentId Structure .

1- [AccountChart] Table with these fields :

Id smallint
ParentId smallint Null
AccountName nvarchar(100)

2- [Transaction] Table with these fields

AccountId smallInt
TransactionDate DateTime
Debit decimal(19,5)
Credit decimal(19,5)

here is what I tried

    Create table [AccountChart] (
    id int not null,
    parentId int null,
    AccountName nvarchar(100)

    PRIMARY KEY (ID)
      )

    Create table [TransactionData] (
    id int not null,
    TransactionDate DateTime not null,
    AccountId int not null,
    Credit decimal(19,5),
    Debit decimal(19,5)

    PRIMARY KEY (ID)
    )

    insert into [AccountChart] (id,parentId,AccountName) values (1,null,'level 0');
    insert into [AccountChart] (id,parentId,AccountName) values (2,1,'level 2');
    insert into [AccountChart] (id,parentId,AccountName) values (3,2,'level 3 - 1');
    insert into [AccountChart] (id,parentId,AccountName) values (4,2,'level 3 - 2 ');


    insert into [TransactionData] (id,TransactionDate,AccountId,Credit,Debit) values (1,'2020-03-17',3,1000.0,0.0)
    insert into [TransactionData] (id,TransactionDate,AccountId,Credit,Debit) values (2,'2020-03-17',3,0.0,1000.0)
    insert into [TransactionData] (id,TransactionDate,AccountId,Credit,Debit) values (3,'2020-03-17',4,4000.0,0.0)
    insert into [TransactionData] (id,TransactionDate,AccountId,Credit,Debit) values (4,'2020-03-17',3,6000.0,0.0)
    insert into [TransactionData] (id,TransactionDate,AccountId,Credit,Debit) values (5,'2020-03-17',4,3000.0,0.0)


    ;WITH cteBalances (AccountId, ParentId, Credit, Debit)
    AS (SELECT
      AccountChart.Id,
      AccountChart.ParentId,
      SUM([TransactionData].Credit) AS Credit,
      SUM([TransactionData].Debit) AS Debit
     FROM [TransactionData]
     INNER JOIN AccountChart ON ([TransactionData].AccountId = AccountChart.Id)
     GROUP BY AccountChart.Id,AccountChart.ParentId)

    SELECT
      AccountChart.id,
      AccountChart.ParentId,
      AccountChart.AccountName,
      cteBalances.Credit,
      cteBalances.Debit
    FROM  AccountChart 
    left JOIN cteBalances ON (AccountChart.ID = cteBalances.AccountID)

the problem that I couldn’t bring the summary values for the parent records.

the results I get is :

id  ParentId    AccountName   Credit        Debit
1   NULL        level 0       NULL          NULL
2   1           level 2       NULL          NULL
3   2           level 3 - 1   7000.00000    1000.00000
4   2           level 3 - 2   7000.00000    0.00000

but I excpect to be calculate the childs in parent as this

id  ParentId    AccountName   Credit        Debit
1   NULL        level 0       14000.00000   1000.00000
2   1           level 2       14000.00000   1000.00000
3   2           level 3 - 1   7000.00000    1000.00000
4   2           level 3 - 2   7000.00000    0.00000

Advertisement

Answer

I like the technique of using Range Keys. If you have a slow moving hierarchy, I would create a table to store these values

Just to expand, the range keys facilitate selection, and variable depth aggregation.

You may notice that the SEQ has an Order by AccountName. In my GL systems, we have a column for presentation sequence. This controls the order rather than ALPHA on name or significant digits on the account.

Example

Declare @Top  int         =  null   --<<  Sets top of Hier Try 2
Declare @Nest varchar(25) = '|---'  --<<  Optional: Added for readability

;with cteP as (
      Select Seq  = cast(10000+Row_Number() over (Order by AccountName) as varchar(500))
            ,ID
            ,ParentID 
            ,Lvl=1
            ,AccountName
      From   [AccountChart] 
      Where  IsNull(@Top,-1) = case when @Top is null then isnull(ParentID ,-1) else ID end
      Union  All
      Select Seq  = cast(concat(p.Seq,'.',10000+Row_Number() over (Order by r.AccountName)) as varchar(500))
            ,r.ID
            ,r.ParentID 
            ,p.Lvl+1
            ,r.AccountName
      From   [AccountChart] r
      Join   cteP p on r.ParentID  = p.ID)
     ,cteR1 as (Select *,R1=Row_Number() over (Order By Seq) From cteP)
     ,cteR2 as (Select A.ID,R2=Max(B.R1) From cteR1 A Join cteR1 B on (B.Seq like A.Seq+'%') Group By A.Seq,A.ID )
Select A.Lvl
      ,A.R1  
      ,B.R2
      ,A.ID
      ,A.ParentID 
      ,AccountName = max(Replicate(@Nest,A.Lvl-1) + A.AccountName)
      ,Credit      = sum(C.Credit)
      ,Debit       = sum(C.Debit)
 From cteR1 A
 Join cteR2 B on A.ID=B.ID
 Join (Select _R1=A.R1,B.* From cteR1 A Join [TransactionData] B on A.ID=B.AccountID ) C on (C._R1 between A.R1 and B.R2)
 Group By A.R1,B.R2,A.Lvl,A.ID,A.ParentID
 Order By A.R1

Returns

enter image description here

User contributions licensed under: CC BY-SA
8 People found this is helpful
Advertisement