HashCoder
HashCoder

Reputation: 946

Grouping columns in SQL Server

I am trying to group the credit and debit account numbers for money transaction.

http://sqlfiddle.com/#!18/67ecd/4/0

I am able to group if there are multiple transactions on the same credit and debit account.

CREATE Table TestTable 
(
    DebitAccountNumber NVARCHAR(10),
    CredtiAccountNumber NVARCHAR(10),
    Amount MONEY
)

INSERT INTO TestTable 
VALUES ('A', 'C', 10), ('C', 'A', 20), ('A', 'C', 40);

SELECT  
    DebitAccountNumber, 
    CredtiAccountNumber, 
    SUM(amount) 
FROM 
    TestTable
GROUP BY
    DebitAccountNumber, CredtiAccountNumber

Since credit and debit account is happening on the same account, the expected output is A -> C 30.

Could anyone please help me with the query?

Upvotes: 2

Views: 70

Answers (3)

Hilarion
Hilarion

Reputation: 870

Based on @GordonLinoff's approach, having in mind the result, you accepted from @dnoeth, my suggested answer would be:

SELECT v.CreditAccountNumber, v.DebitAccountNumber, SUM(v.Amount) AS SumAmount
FROM TestTable AS tt
CROSS APPLY
     (VALUES
        (DebitAccountNumber, CreditAccountNumber, Amount),
        (CreditAccountNumber, DebitAccountNumber, -Amount)
     ) AS v(DebitAccountNumber, CreditAccountNumber, Amount)
GROUP BY v.CreditAccountNumber, v.DebitAccountNumber
HAVING SUM(v.Amount) > 0
ORDER BY 1, 2;

Here's a fiddle for it.

Upvotes: 0

dnoeth
dnoeth

Reputation: 60462

WITH cte AS
 ( 
   SELECT
      -- "normalize" debitor and creditor to: account#1 < account#2
      CASE WHEN DebitAccountNumber < CreditAccountNumber THEN DebitAccountNumber  ELSE CreditAccountNumber END AS Account1,
      CASE WHEN DebitAccountNumber < CreditAccountNumber THEN CreditAccountNumber ELSE DebitAccountNumber  END AS Account2,
      -- adjust sign when accounts switched   
      CASE WHEN DebitAccountNumber < CreditAccountNumber THEN Amount              ELSE -Amount             END AS amount
   FROM TestTable
 )
SELECT
   Account1,
   Account2,
   Sum(amount) -- might be negative 
FROM cte
GROUP BY
   Account1,
   Account2
;

See fiddle

Edit:

If you want to avoid negative amounts you can "normalize" it again:

WITH normalized AS
 ( 
   SELECT
      -- "normalize" debitor and creditor to: account#1 < account#2
      CASE WHEN DebitAccountNumber < CreditAccountNumber THEN DebitAccountNumber  ELSE CreditAccountNumber END AS Account1,
      CASE WHEN DebitAccountNumber < CreditAccountNumber THEN CreditAccountNumber ELSE DebitAccountNumber  END AS Account2,
      -- adjust sign when accounts switched   
      CASE WHEN DebitAccountNumber < CreditAccountNumber THEN Amount              ELSE -Amount             END AS amount
   FROM TestTable
 ),
summed as
 (
   SELECT
      Account1,
      Account2,
      Sum(amount) as Amount -- might be negative 
   FROM normalized
   GROUP BY
      Account1,
      Account2
 )
select
   case when Amount > 0 then Account1 else Account2 end as Debitor,
   case when Amount > 0 then Account2 else Account1 end as Creditor,
   abs(Amount) as Amount
from summed
;

See fiddle

Upvotes: 1

Gordon Linoff
Gordon Linoff

Reputation: 1269803

I suspect you want:

select accountnumber, sum(amount)
from ((select DebitAccountNumber as accountnumber, - amount as amount
       from testtable
      ) union all
      (select CreditAccountNumber as accountnumber, amount
       from testtable
      )
     ) cd
group by accountnumber;

In SQL Server, you can use apply:

select v.accountnumber, sum(v.amount)
from testtable tt cross apply
     (values (debitaccountnumber, - amount), (creditaccountnumber, amount)
     ) v(accountnumber, amount)
group by v.accountnumber;

Upvotes: 3

Related Questions