Do not use on any
shared computer
August 30, 2008 12:12am pdt
 
[x]
Attachment Details

SQL Server 2000 ; have 10 rows of DEBIT + CREDIT columns, can SQL tally up the balances for each account?

Tags: SQL
hi, this is my table. is it possible do work out the balance via SQL, im currently doing it in .net.
Account   Date                         Debit        Credit      Balance
10139      2007-08-31       2025.91      0.0      0.0
10139      2007-08-31       0.0      3620.11      0.0
10139      2007-09-30       4631.52      0.0      0.0
10139      2007-09-30       0.0      11336.71      0.0
10139      2007-09-30       0.0      14.8801      0.0
12211      2007-08-31       1352.76      0.0      0.0
12211      2007-08-31       0.0      3872.5      0.0

e.g. will be
Account   Date                         Debit        Credit      Balance
10139      2007-08-31       205      0.0      -205
10139      2007-08-31       0.0      305      +100
10139      2007-09-30       0.0      15      +115
12211      2007-08-31       135      0.0      -135
12211      2007-08-31       0.0      325      +190
Start your free trial to view this solution
[x]
The Solution Rating System

With so many solutions, how can you tell which solutions are most likely to help you and which ones are not? To provide you with a tool to use, we rate our solutions based on various elements that most accurately determine if a solution is a quality solution. To explain what factors affect the solution rating, here are the elements we take into consideration when formulating our solution rating.

  • The Grade of the Solution
  • The Zone Rank of the Expert Providing the Solution
  • The Number of Author and Expert Comments
  • The Number of Experts Contributing
  • The Feedback of the Community

Your Input Matters
Because of the way the system is set up, the most important variable in this equation is you. As a member of Experts Exchange, you are able to cast your vote on the quality of the solutions in regard to how complete, accurate, helpful and easy to understand each solution is. When you provide your feedback, each rating is adjusted accordingly. So, if you see a solution that has a poor rating that you think is a good solution, let us know by rating it. As you do, the rating will be adjusted and will become more accurate for other members of our site.

If you have any suggestions that you would like to make for our rating system, please ask a question in the Suggestions Zone of Community Support.

Thank you!

Question Stats
Zone: Microsoft
Question Asked By: jxharding
Solution Provided By: auke_t
Participating Experts: 2
Solution Grade: A
Views: 0
Translate:
Loading Advertisement...
 
[+][-]Expert Comment by PaultheBroker
Expert Comment by PaultheBroker:

All comments and solutions are available to Premium Service Members only.

Start your 7-day free trial and see for yourself why Experts Exchange is the easiest and most proven technology resource in the world. Get Started

Already a member? Login to view this solution.

 
 
[+][-]Accepted Solution by auke_t

Rank: Master

Accepted Solution by auke_t:

All comments and solutions are available to Premium Service Members only.

Start your 7-day free trial and see for yourself why Experts Exchange is the easiest and most proven technology resource in the world. Get Started

Already a member? Login to view this solution.

 
 
[+][-]Administrative Comment by angelIII

Rank: Savant

Administrative Comment by angelIII:

All comments and solutions are available to Premium Service Members only.

Start your 7-day free trial and see for yourself why Experts Exchange is the easiest and most proven technology resource in the world. Get Started

Already a member? Login to view this solution.

 
 
[+][-]Author Comment by jxharding
Author Comment by jxharding:

All comments and solutions are available to Premium Service Members only.

Start your 7-day free trial and see for yourself why Experts Exchange is the easiest and most proven technology resource in the world. Get Started

Already a member? Login to view this solution.

 
 
[+][-]Expert Comment by auke_t

Rank: Master

Expert Comment by auke_t:

All comments and solutions are available to Premium Service Members only.

Start your 7-day free trial and see for yourself why Experts Exchange is the easiest and most proven technology resource in the world. Get Started

Already a member? Login to view this solution.

 
 
[+][-]Assisted Solution by PaultheBroker
Assisted Solution by PaultheBroker:

All comments and solutions are available to Premium Service Members only.

Start your 7-day free trial and see for yourself why Experts Exchange is the easiest and most proven technology resource in the world. Get Started

Already a member? Login to view this solution.

 
 
[+][-]Administrative Comment by angelIII

Rank: Savant

Administrative Comment by angelIII:

All comments and solutions are available to Premium Service Members only.

Start your 7-day free trial and see for yourself why Experts Exchange is the easiest and most proven technology resource in the world. Get Started

Already a member? Login to view this solution.

 
 
Loading Advertisement...
Open Discussion
Open Discussion
 
Comment by PaultheBroker
10139      2007-08-31 00:00:00.000      2025.91      .00      2025.91
10139      2007-08-31 00:00:00.000      .00      -3620.11      -1594.20
10139      2007-09-30 00:00:00.000      4631.52      .00      6657.43
10139      2007-09-30 00:00:00.000      .00      -11351.59      -8314.27
12211      2007-08-31 00:00:00.000      1352.76      .00      1352.76
12211      2007-08-31 00:00:00.000      .00      -3872.50      -2519.74

You accepted that???
 
 
Comment by PaultheBroker
This however, DOES work - however, as I said before you NEED the time on the datetime (or unique days) else you willl get the daily balance, not the strict running sum.

create table mytable (account int, [date] datetime, Debit money, Credit  money, balance money)
insert into mytable (account, [date],debit,credit)
select 10139,'20070830',205,0.0
union select 10139,'20070831',0.0,305
union select 10139,'20070930',0.0, 15
union select 12211,'20070830',135,0.0
union select 12211,'20070831',0.0,325
-------------------------------------

select
      account = a.account,
      date = a.date,
      Debit = min(a.Debit),
      Credit = min(a.Credit),
      Balance = sum(b.credit - b.debit)
from       MyTable a
LEFT JOIN myTable b
      on a.account = b.account
      and b.date <= a.date
GROUP BY a.account, a.date
ORDER BY a.account, a.date
 
 
Comment by PaultheBroker
10139      2007-08-30 00:00:00.000      205.0000      .0000      -205.0000
10139      2007-08-31 00:00:00.000      .0000      305.0000      100.0000
10139      2007-09-30 00:00:00.000      .0000      15.0000      115.0000
12211      2007-08-30 00:00:00.000      135.0000      .0000      -135.0000
12211      2007-08-31 00:00:00.000      .0000      325.0000      190.0000
 
 
Comment by auke_t
It seemed to me he wanted the daily balance with the credit and the debit in two different lines.

I work at the financial department of our compagny so I tend to understand the weird ways financial oriented people want their information ;-)
 
 
Comment by auke_t
Actually it's probably not even daily totals, but the month totals as the days mentioned are the final days of their respective months
 
 
Comment by PaultheBroker
hi auke - maybe I'm not understnading something here, but I don't understand your solution at all...the first two rows seem to work fine - I have a put in $2025, and my balance is then $2025.  I then withdraw $3620, so now have -$1594 - ok so far.  However, the next month I put in $4,631 - but my balance is now $6657 - seems to me the balance should be about $3,000 in your example (-$1595 + $4631) but you have ($2025 + $4631)....anyway, I don't want to spend any more time on this - I'm interested in exercising my SQL, not my math !!!!!   Take care :) ... Paul
 
 
Comment by auke_t
Ah, true! Thanks Paul!
--------------------
So it should be:

SELECT
      account,
      date,
      SUM(Debit) AS Debit,
      - SUM(Credit) AS Credit,
      (SELECT ISNULL(SUM(m2.Debit),0) - ISNULL(SUM(m2.Credit),0) FROM #mutations m2 WHERE m1.account = m2.account AND
(
(
      m2.date = m1.date  
AND
      CASE WHEN m2.debit <> 0 THEN 0 ELSE 1 END <= CASE WHEN m1.debit <> 0 THEN 0 ELSE 1 END
)
OR m2.date < m1.date
)
) AS Balance
FROM
      #mutations m1
GROUP BY
      account,
      date,
      CASE WHEN debit <> 0 THEN 0 ELSE 1 END
ORDER BY
      account,
      date,
      CASE WHEN debit <> 0 THEN 0 ELSE 1 END
 
 
Comment by PaultheBroker
ok - I see you've used:

      m2.date = m1.date  
AND
      CASE WHEN m2.debit <> 0 THEN 0 ELSE 1 END <= CASE WHEN m1.debit <> 0 THEN 0 ELSE 1 END

which I think will allow there to be one debit and one credit on any particular day, and still give you a runnings sum across those two entries.  Presumably you could extend this to account for unique doollar amounts or something....still its (necessarily) all a bit nasty with the the time isn't it !!! :)
 
 
Comment by auke_t
As date includes time different times will result in different rows and as such everything will be fine.
Actually I think it would be better to aggregate to months, but that might be a bit too much reading between the lines as I suspect it beeing the "end of month account balances" or something.

For something totally different: It took me some time to realize why financial people have a Credit and a Debet column instead of just a positive and a negative value in the same column. Then I realized that negative values are used, but only to correct faulty entries. Just deleting the faulty record seems not to be allowed. But that's probably cause financial people can't be trusted!

(Just hope my colleagues never get to read this comment) ;-)
 
 
Comment by PaultheBroker
hahahaha - both the answer and assist to auke! - well I guess you lose some and win some !! :)
 
 
Comment by jxharding
man i really screwed up on this one... i'm asking community again.
i apologise. i'm sitting on a 33.6K dial-up line and it takes ages to load.
 
 
Comment by PaultheBroker
Don't worry about it - it was nice of you to even try - if the points actually meant something it would be one thing :) , but just the 'thank you' is nice to have...
 
 
Comment by jxharding
i cannot tell you how important this thread has been in the current situation that i'm trying to sort out.
thank you very very much for your time and effort.
it has been very complete and definitely a source of regular reference.
 
 
Comment by jxharding
hi, i know this thread is finished, i have just 1 seemingly very simple amendment that i kindly ask help on. I reckoned this is the most effective way of asking it to the main thread experts, else obviously i'll create a new thread and everyone is happy.


..."As date includes time different times will result in different rows and as such everything will be fine"
that is true but i have a problem now, i have more than 1 transaction happening on 1 day and i only receive a DATE from the user .e.g

union all select 10139,'MyNote','20070831',0.0,305
union all select 10139,'MyNote2','20070831',0.0,305

now the boss isn't happy with me, he says i must display both items:
10139      2007-08-30       205.0000      .0000      MyNote      205.0000
10139      2007-08-31      .0000      -305.0000      MyNote      -405.0000
10139      2007-08-31       .0000      -305.0000      MyNote2      -405.0000

but the running total must be:
10139      2007-08-30       205.0000      .0000      MyNote      205.0000
10139      2007-08-31      .0000      -305.0000      MyNote      -100.0000
10139      2007-08-31       .0000      -305.0000      MyNote2      -405.0000

i have tried and spent an hour on this but i cant get it. if possible?

here's the template im practising on:

create table mytable (account int,note nvarchar(50), [date] datetime, Debit money, Credit  money, balance money)
insert into mytable (account,note, [date], debit,credit)
select 10139,'MyNote','20070830',205,0.0
union all select 10139,'MyNote','20070831',0.0,305
union all select 10139,'MyNote2','20070831',0.0,305
union all select 10139,'MyNote','20070930',0.0, 15
union all select 12211,'MyNote','20070830',135,0.0
union all select 12211,'MyNote1','20070830',134,0.0
union all  select 12211,'MyNote','20070831',0.0,325
-------------------------------------
SELECT
      account,
      date,
      SUM(Debit) AS Debit,
      - SUM(Credit) AS Credit,Note,
      (SELECT ISNULL(SUM(m2.Debit),0) - ISNULL(SUM(m2.Credit),0) FROM mytable m2 WHERE m1.account = m2.account AND
(
(
      m2.date = m1.date  
AND
      CASE WHEN m2.debit <> 0 THEN 0 ELSE 1 END <= CASE WHEN m1.debit <> 0 THEN 0 ELSE 1 END
)
OR m2.date < m1.date
)
) AS Balance
FROM
      mytable m1
GROUP BY
      account,note,
      date,
      CASE WHEN debit <> 0 THEN 0 ELSE 1 END
ORDER BY
      account,
      date,
      CASE WHEN debit <> 0 THEN 0 ELSE 1 END

drop table mytable
 
 
Comment by jxharding
this is a big feat i know, i looked at
http://www.sqlservercentral.com/articles/T-SQL/61539/
because i picked up on the term "triangular join" here.
it seems easier if there were ID's involved.
 
 
Comment by auke_t
Indeed, so you could just temporary add ID's!

create table #mytable (account int,note nvarchar(50), [date] datetime, Debit money, Credit  money, balance money)
insert into #mytable (account,note, [date], debit,credit)
select 10139,'MyNote','20070830',205,0.0
union all select 10139,'MyNote','20070831',0.0,305
union all select 10139,'MyNote2','20070831',0.0,305
union all select 10139,'MyNote','20070930',0.0, 15
union all select 12211,'MyNote','20070830',135,0.0
union all select 12211,'MyNote1','20070830',134,0.0
union all  select 12211,'MyNote','20070831',0.0,325
-------------------------------------

SELECT
      *
INTO
      #temptable
FROM
      #mytable

ALTER TABLE #temptable ADD MutationId INT Identity


SELECT
      account,
      date,
      SUM(Debit) AS Debit,
      - SUM(Credit) AS Credit,
      Note,
      (SELECT ISNULL(SUM(m2.Debit),0) - ISNULL(SUM(m2.Credit),0) FROM #temptable m2 WHERE m1.account = m2.account AND

      m2.MutationId <= m1.MutationId)  AS Balance
FROM
      #temptable m1
GROUP BY
      account,
      note,
      MutationId,
      date
ORDER BY
      account,
      date

drop table #temptable
drop table #mytable
 
 
Comment by jxharding
thank you, i have learned you and add a column of type identity and it'll insert ID's . great stuff.

if i do this: i can see the Id's
--------------------------------------
create table #mytable (account int,note nvarchar(50), [date] datetime, Debit money, Credit  money, balance money)
insert into #mytable (account,note, [date], debit,credit)
select 10139,'MyNote','20070830',205,0.0
union all select 10139,'MyNote','20070831',0.0,305
union all select 10139,'MyNote2','20070831',0.0,305
union all select 10139,'MyNote','20070930',0.0, 15
union all select 12211,'MyNote','20070830',135,0.0
union all select 12211,'MyNote1','20070830',134,0.0
union all  select 12211,'MyNote','20070831',0.0,325
-------------------------------------
create table #temptable (account int,note nvarchar(50), [date] datetime, Debit money, Credit  money, balance money)
INSERT INTO   #temptable
SELECT      *FROM      #mytable

ALTER TABLE #temptable ADD MutationID INT Identity

select * from #TempTable

drop table #temptable
drop table #mytable
-------------------------------------
but if i replace  select * from #TempTable  WITH

SELECT
      account,
      date,
      SUM(Debit) AS Debit,
      - SUM(Credit) AS Credit,
      Note,
      (SELECT ISNULL(SUM(m2.Debit),0) - ISNULL(SUM(m2.Credit),0) FROM #temptable m2 WHERE m1.account = m2.account AND

      m2.MutationID <= m1.MutationID)  AS Balance
FROM
      #temptable m1
GROUP BY
      account,
      note,
      MutationID,
      date
ORDER BY
      account,
      date

----------------
Server: Msg 207, Level 16, State 3, Line 18
Invalid column name 'MutationID'.
this is weird stuff, i can see the mutationid if i do a normal select

 
 
Comment by auke_t
--Ah, sql2000 does parse the temptable while parsing the script.
--Add a GO after the ALTER TABLE statement so:

ALTER TABLE #temptable ADD MutationID INT Identity
GO

--That would do the trick!
 
 
Comment by jxharding
genius!!
 
 
Comment by PaultheBroker
Please remember that you NEED to specify an order when creating the IDENTITY column - so you should ORDER BY when you create the temporary table.  I also suggest that you order not only by date, but also across the debit and credit columns, so that the results have a chance of being the same each time you run the report...

INSERT INTO   #temptable
SELECT      *FROM      #mytable
--> ORDER BY account,date ASC,credit+debit DESC

I presume you are using MSSQL 2000, as you didn't use the SELECT * INTO that auke suggested.  2005 has this cool syntax which would obviate the need to create an IDENTITY column:

SELECT
-->      id = ROW_NUMBER() OVER (order by account,date ASC,credit+debit DESC)
      ,*
INTO #sortedtable
FROM #mytable


 
 
Comment by PaultheBroker
and auke's final query is overly complex - two methods are being used together where one would do.  EITHER you put the subquery in the SELECT statement like this:

SELECT
      account,
      date,
      Debit,
      Credit,
      Note,
      Balance = (
            SELECT SUM(isnull(m2.Credit,0) - ISNULL(m2.Debit,0))
            FROM      #sortedtable m2
            WHERE      m1.account = m2.account
            AND            m2.id <= m1.id)
FROM
      #sortedtable m1
ORDER BY
      account, date, (Credit + Debit) DESC
 
 
Comment by PaultheBroker
or you put it in the FROM statement like this....

SELECT
      account      = m1.account,
      date            = MAX(m1.date),
      Debit            = SUM(m1.Debit),
      Credit      = SUM(m1.Credit),
      Note            = MAX(m1.Note),
      Balance      = SUM(isnull(m2.Credit,0) - ISNULL(m2.Debit,0))
FROM    #sortedtable m1, #sortedtable m2
WHERE      m1.account = m2.account
AND            m2.id <= m1.id
GROUP BY m1.account, m1.id
ORDER BY
      m1.account, MAX(m1.date), MAX(m1.Credit + m1.Debit) DESC
--------------------
The two statements are equivalent - the choice would be dependent on if you wanted more than one column aggregated - in your case, as you only want the balance, then the first mehtod (in the SELECT column) would be most appropriate.
 
 
Comment by PaultheBroker
ORDER BY  m1.id  

would have been better than repeating the ORDER BY used to create the id...both are equivalent.
 
 
Comment by PaultheBroker
my last SQL statement (with the SUM(Credit), SUM(DEBIT) is not right....will repost soon
 
 
Comment by PaultheBroker
sorry - stupid error - in the second version, you need to select a single row from table m1 (i.e with MAX, MIN etc), as this is the table which will be duplicated up...so this (like my very first answer) will work....I've included two more fields, RunningCredit and RunningDebit which gives you the more realistic scenario where this solution would be preferable to doing the triangular join in the SELECT  statement.  Hope this explains things a little better....

SELECT
      m1.id,
      account      = m1.account,
      date      = MAX(m1.date),
      Debit      = MAX(m1.Debit),
      Credit      = MAX(m1.Credit),
      Note      = MAX(m1.Note),
     RunningCredit = SUM(isnull(m2.Credit,0)),
     RunningDebit= SUM(isnull(m2.Debit,0)),
      Balance      = SUM(isnull(m2.Credit,0) - ISNULL(m2.Debit,0))
FROM    #sortedtable m1, #sortedtable m2
WHERE      m1.account = m2.account
AND      m2.id <= m1.id
GROUP BY     m1.id, m1.account
ORDER BY      m1.id
 
 
20080723-EE-VQP-34 / EE_QW_2_20070628