【问题标题】:Getting totals and sub-totals in Parent-Child hierarchy在父子层次结构中获取总计和小计
【发布时间】:2014-03-16 10:31:17
【问题描述】:

我有以下表格结构,我正在尝试获取总计和小计并显示值的汇总。

ChartOfAccounts(AccountNumber, AccountDescription, ParentAccountNumber, IsControlAccount)
Ledger(LedgerId, JournalId, AccountNumber, IsDebit, Amount)

我已经设法使用 CTE 来获得所需的父子关系,但我不确定如何使用它来获取汇总到父账户中的控制账户余额。

到目前为止,我已经设法将以下查询放在一起,这并不完全是我想要的 --> SQL Fiddle。当前查询似乎没有正确汇总和分组父子总数。 (我已经从小提琴中排除了年、月列)

描述问题的另一种方式是,所有控制帐户都应具有其子帐户的总数。

我需要的输出如下 (年、月、AccountNumber、AccountDescription、DebitBalance、CreditBalance、Balance)

|Account#|Acc Desc                                 | DR     | CR     | BAL    |
|1000    |Accounts Receivable                      |10000   |5000    |5000    |
|1200    |Buyer  Receivables                       |5000    |0       |5000    |
|12001   |Buyer  Receivables - Best Buy            |5000    |0       |5000    |
|1500    |Offers                                   |5000    |5000    |0       |
|4000    |Accounts Payable                         |        |4475.06 |4475.06 |  
|4100    |Supplier Invoice Payables                |        |4475.06 |4475.06 |  
|41002   |Supplier Invoice Payables - Knechtel     |        |4475.06 |4475.06 |  
|6000    |Revenue                                  |        |524.93  |524.93  |  
|6100    |Membership Fees Revenue                  |        |        |0       |  
|6200    |Processing Fees Revenue                  |        |100     |100     |  
|62002   |Processing Fees Revenue - Knechtel       |        |100     |100     |  
|6300    |Fees Revenue                             |        |424.93  |424.93  |  
|63002   |Fees Revenue  - Knechtel                 |        |424.93  |424.93  |  

【问题讨论】:

  • 您写了“我需要的输出如下”并发布了示例输出。但我在其中找不到任何“总计和小计”。那么输出表应该是什么样子呢?

标签: sql sql-server parent-child rollup


【解决方案1】:

这是我想出的,并且能够非常接近匹配您想要的输出

WITH CTEAcc 
AS
(
    SELECT
        coa.accountDescription,coa.accountnumber,coa.accountnumber as parentaccount
        FROM ChartOfAccounts coa
  where iscontrolaccount=1
  union all select c.accountdescription, coa.accountnumber, c.ParentAccount 
    from chartofaccounts coa
    inner join cteacc c on coa.ParentAccountNumber=c.accountnumber

)

select parentaccount as [Account#], accountdescription as [Acc Desc], 
sum(case when isdebit=1 then amount else 0 end) as DR,
sum(case when isdebit=0 then amount else 0 end) as CR,
sum(case when isdebit=1 then amount else 0 end)-sum(case when isdebit=0 then amount else 0 end) as BAL
from (select c.accountdescription, c.accountnumber, 
      c.parentaccount, l.isdebit, l.amount 
      from cteacc c
left join ledger l
  on c.accountnumber=l.accountnumber
union all select c.accountdescription, 
      c.accountnumber, c.accountnumber as parentaccount, 
      l.isdebit, l.amount 
      from ChartOfAccounts c
      inner join ledger l
  on c.accountnumber=l.accountnumber where amount<>0) f
group by parentaccount, accountdescription
order by parentaccount

这里是 sql 小提琴:http://www.sqlfiddle.com/#!3/d94bc/106

【讨论】:

    【解决方案2】:

    又一个变种。保留层次结构和 iscontrol 字段仅供参考。首先,它将帐户层次结构与每个帐户相关联(递归 cte)。然后,对于每个帐户,根据层次结构位置(以及它是否是控制帐户)计算该帐户的分类帐项目总和。最后,包装在另一个查询中以计算余额并从输出中剔除未使用的帐户。

    WITH AccountHierarchy AS (
    
        SELECT AccountNumber
              ,AccountDescription
              ,CAST(AccountNumber AS VARCHAR(MAX))
                 + '/' AS AccountHierarchy
              ,IsControlAccount
          FROM ChartOfAccounts
          WHERE ParentAccountNumber IS NULL
    
        UNION ALL
    
        SELECT c.AccountNumber
              ,c.AccountDescription
              ,CAST(h.AccountHierarchy  AS VARCHAR(MAX))
                 + CAST(c.AccountNumber AS VARCHAR(MAX))
                 + '/' AS AccountHierarchy
              ,c.IsControlAccount
          FROM ChartOfAccounts c
          INNER JOIN AccountHierarchy h ON (c.ParentAccountNumber = h.AccountNumber)
          WHERE ParentAccountNumber IS NOT NULL
    )
    
    SELECT AccountNumber
          ,AccountDescription
          ,AccountHierarchy
          ,IsControlAccount
          ,DR
          ,CR
          ,CASE WHEN (DR IS NULL AND CR IS NULL) THEN NULL
                ELSE COALESCE(DR, 0) - COALESCE(CR, 0)
                END AS BAL
    
      FROM (SELECT h.AccountNumber
                  ,h.AccountDescription
                  ,h.AccountHierarchy
                  ,h.IsControlAccount
    
                  ,(SELECT SUM(l.Amount)
                      FROM Ledger l
                      INNER JOIN AccountHierarchy hd ON (l.AccountNumber = hd.AccountNumber)
                      WHERE l.IsDebit = 1
                        AND (    (h.IsControlAccount = 1 AND hd.AccountHierarchy LIKE h.AccountHierarchy + '%')
                              OR hd.AccountHierarchy = h.AccountHierarchy)
                   ) AS DR
    
                  ,(SELECT SUM(l.Amount)
                      FROM Ledger l
                      INNER JOIN AccountHierarchy hd ON (l.AccountNumber = hd.AccountNumber)
                      WHERE l.IsDebit = 0
                        AND (    (h.IsControlAccount = 1 AND hd.AccountHierarchy LIKE h.AccountHierarchy + '%')
                              OR hd.AccountHierarchy = h.AccountHierarchy)
                   ) AS CR
    
              FROM AccountHierarchy h
            ) x
    
      WHERE NOT(CR IS NULL AND DR IS NULL)
      ORDER BY AccountHierarchy
    

    我将这个question 用作层次结构示例。

    输出:

    |        ACCOUNTNUMBER |                 ACCOUNTDESCRIPTION |                                                ACCOUNTHIERARCHY | ISCONTROLACCOUNT |     DR |        CR |        BAL |
    |----------------------|------------------------------------|-----------------------------------------------------------------|------------------|--------|-----------|------------|
    | 1000                 |                Accounts Receivable |                                           1000                / |                1 |  10000 |      5000 |       5000 |
    | 1200                 |                 Buyer  Receivables |                      1000                /1200                / |                1 |   5000 |    (null) |       5000 |
    | 12001                |      Buyer  Receivables - Best Buy | 1000                /1200                /12001               / |                0 |   5000 |    (null) |       5000 |
    | 1500                 |                             Offers |                      1000                /1500                / |                0 |   5000 |      5000 |          0 |
    | 4000                 |                   Accounts Payable |                                           4000                / |                1 | (null) | 4475.0685 | -4475.0685 |
    | 4100                 |                 Supplier  Payables |                      4000                /4100                / |                1 | (null) | 4475.0685 | -4475.0685 |
    | 41002                |      Supplier  Payables - Knechtel | 4000                /4100                /41002               / |                0 | (null) | 4475.0685 | -4475.0685 |
    | 6000                 |                            Revenue |                                           6000                / |                1 | (null) |  524.9315 |  -524.9315 |
    | 6200                 |            Processing Fees Revenue |                      6000                /6200                / |                1 | (null) |       100 |       -100 |
    | 62002                | Processing Fees Revenue - Knechtel | 6000                /6200                /62002               / |                0 | (null) |       100 |       -100 |
    | 6300                 |                       Fees Revenue |                      6000                /6300                / |                1 | (null) |  424.9315 |  -424.9315 |
    | 63002                |            Fees Revenue - Knechtel | 6000                /6300                /63002               / |                0 | (null) |  424.9315 |  -424.9315 |
    

    【讨论】:

      【解决方案3】:

      从您想要的输出开始,我提出了以下查询,该查询根据 ParentAccountNumber 对子帐户进行分组。仅需要子查询,因为我假设您想在汇总之前将任何 NULL 值转换为 0(在 SQL 中,NULL + 42 = NULL)。

      with preresult as
      (
          select acc.ParentAccountNumber as AccountNumber,
                acc.AccountDescription as "Acc Desc",
                ISNULL(ld.Amount, 0) as DR,
                ISNULL(lc.Amount, 0) as CR
      
          from ChartOfAccounts acc
      
          left outer join Ledger ld
          on (ld.AccountNumber = acc.AccountNumber AND ld.IsDebit = 1)
      
          left outer join Ledger lc
          on (lc.AccountNumber = acc.AccountNumber AND lc.IsDebit = 0)
      
          where acc.ParentAccountNumber is not null
      )
      
      select c.AccountNumber as "ACC",
            c.AccountDescription as "ACC DESC",
            sum(DR) as DR,
            sum(CR) as CR,
            sum(DR) - sum(CR) AS BL
      
      from preresult p
      join ChartOfAccounts c on (c.AccountNumber = p.AccountNumber)
      
      group by c.AccountNumber, c.AccountDescription;
      

      sqlfiddle 可以在这里找到:http://www.sqlfiddle.com/#!3/d94bc/81/0

      【讨论】:

        【解决方案4】:

        这似乎给了你想要的:

        ;WITH recurs
        AS
        (
            SELECT C.AccountNumber, C.IsControlAccount, C.ParentAccountNumber, C.AccountDescription, 
                    COALESCE((SELECT SUM(Amount) FROM Ledger WHERE AccountNumber = C.AccountNumber and IsDebit = 1), 0) AS DR,
                    COALESCE((SELECT SUM(Amount) FROM Ledger WHERE AccountNumber = C.AccountNumber and IsDebit = 0), 0) AS CR,
                    COALESCE((SELECT SUM(CASE WHEN IsDebit = 0 THEN Amount * -1 ELSE Amount END) FROM Ledger WHERE AccountNumber = C.AccountNumber), 0) AS BAL
            FROM ChartOfAccounts C
            WHERE IsControlAccount = 0
            UNION ALL
            SELECT C.AccountNumber, C.IsControlAccount, C.ParentAccountNumber, C.AccountDescription,
                    r.DR, r.CR, R.BAL
            FROM ChartOfAccounts C
            INNER JOIN recurs r
                ON r.ParentAccountNumber = c.AccountNumber
        )
        SELECT R.AccountNumber, R.AccountDescription, SUM(R.DR) AS DR, SUM(R.CR) AS CR, SUM(R.BAL) AS BAL
        FROM recurs R
        WHERE NOT (R.DR = 0 AND R.CR = 0 AND R.BAL = 0)
        GROUP BY R.AccountNumber, R.AccountDescription
        ORDER BY AccountNumber
        

        SQL 小提琴here

        结果:

        |        ACCOUNTNUMBER |                 ACCOUNTDESCRIPTION |    DR |        CR |        BAL |
        |----------------------|------------------------------------|-------|-----------|------------|
        | 1000                 |                Accounts Receivable | 10000 |      5000 |       5000 |
        | 1200                 |                 Buyer  Receivables |  5000 |         0 |       5000 |
        | 12001                |      Buyer  Receivables - Best Buy |  5000 |         0 |       5000 |
        | 1500                 |                             Offers |  5000 |      5000 |          0 |
        | 4000                 |                   Accounts Payable |     0 | 4475.0685 | -4475.0685 |
        | 4100                 |                 Supplier  Payables |     0 | 4475.0685 | -4475.0685 |
        | 41002                |      Supplier  Payables - Knechtel |     0 | 4475.0685 | -4475.0685 |
        | 6000                 |                            Revenue |     0 |  524.9315 |  -524.9315 |
        | 6200                 |            Processing Fees Revenue |     0 |       100 |       -100 |
        | 62002                | Processing Fees Revenue - Knechtel |     0 |       100 |       -100 |
        | 6300                 |                       Fees Revenue |     0 |  424.9315 |  -424.9315 |
        | 63002                |            Fees Revenue - Knechtel |     0 |  424.9315 |  -424.9315 |
        

        【讨论】:

        • 如果您需要 Bal 中的非否定答案,您可以简单地将 ABS() 添加到 BAL
        猜你喜欢
        • 2020-05-29
        • 1970-01-01
        • 2023-03-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-02-09
        相关资源
        最近更新 更多