【问题标题】:Hierarchical cumulative sum in sql query (id,parentId) structuresql查询(id,parentId)结构中的分层累积和
【发布时间】:2020-03-17 15:23:57
【问题描述】:

我有两张表,我想获取所有帐户及其贷方金额和借方金额 即使是父记录也必须将它们的子值相加

我正在实现 Id ParentId 结构。

1- [AccountChart] 包含这些字段的表格:

Id smallint
ParentId smallint Null
AccountName nvarchar(100)

2- [Transaction] 包含这些字段的表

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

这是我尝试过的

    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)

我无法为父记录带来汇总值的问题。

我得到的结果是:

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

但我希望像这样计算父母中的孩子

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

【问题讨论】:

  • 能否提供一些示例数据和预期结果?

标签: sql sql-server database reporting database-administration


【解决方案1】:

我喜欢使用范围键的技术。 如果您的层次结构移动缓慢,我会创建一个表来存储这些值

只是为了扩展,范围键便于选择和可变深度聚合。

您可能会注意到SEQ 有一个Order by AccountName。在我的 GL 系统中,我们有一个列用于表示序列。这控制了帐户上名称或有效数字的顺序而不是 ALPHA。

示例

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

退货

【讨论】:

  • 感谢您的回答。但是这个解决方案没有提供预期的结果并且不容易维护。你能不能让它更易读、更清晰。
  • @Shehab 已更正,在最终查询/聚合中,我加入了 ID 而不是 AccountID
  • 它有效。但是当我用我的实际数据尝试它时,它非常慢。我们可以让它更快或更清洁吗?
  • @Shehab 什么是慢...层次结构构建或聚合?你的会计科目表有多深?索引计数;事务表是否被索引?几十年来,我一直在一级银行使用范围密钥技术,在真正的海量数据(数十亿行)上表现出色。同样,我建议如果您的层次结构移动/变化缓慢,请将其与范围键一起存储。然后就是 R1 和 R2 之间的一个小聚合问题
  • 聚合我认为它很慢。得到结果花了 1 分 26 秒。我的事务表有超过100万条半记录。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-07-24
  • 2015-03-07
  • 2023-03-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多