math error or rounding problem Money type - Mailing list pgsql-hackers
From | Justin |
---|---|
Subject | math error or rounding problem Money type |
Date | |
Msg-id | 484B06E8.1050902@emproshunts.com Whole thread Raw |
Responses |
Re: math error or rounding problem Money type
|
List | pgsql-hackers |
I believe i have found a math bug/rounding problem with Money type when its used with SUM()... Postgresql 8.3.1<br /><br/> --------------- Background on the Problem--------------------<br /><br /> We have gl_trans table with 92,000 rowswith one column containing the positive and negative entries.<br /><br /> In order to make this gl_trans table make moresense and to group the accounts in correct debits and credits along with type of accounts, A view was created that doesgrouping and sorting. To further make things easier the view casted the results into the Money Type just to make theselect statements that call the view shorter.<br /><br /> All looked great for several weeks till all of sudden the sumedvalues for all accounts goes out by 0.01. <br /><br /> I needed to confirm this was a rounding problem and not a GLentry that was bad. ( if we had a bad entry this would scream we have a far bigger problem where the application allowedan GL entry to be committed that was out of balance)<br /><br /> To confirm that all entries made have equal and oppositeentry below select statement was created. The gltrans_sequence column is integer key that groups General Ledgerentries together so all the sides of a specific entry can be found. <br /><br /> select * <br /> from <br /> (selectgltrans_sequence, sum(gltrans_amount) as Neg from gltrans where gltrans_amount < 0 group by gltrans_sequence)as neg,<br /> (select gltrans_sequence, sum(gltrans_amount) as pos from gltrans where gltrans_amount >0 group by gltrans_sequence) as pos<br /> where neg.gltrans_sequence = pos.gltrans_sequence <br /> and neg.neg <>pos.pos*-1<br /><br /> This returns no records as expected...<br /><br /> Now armed with that no entry was bad Isuspected it had to be with the money data type.<br /> So I added explicit castings <br /><font color="#3366ff"><br /> select* <br /> from <br /> (select gltrans_sequence, sum(gltrans_amount::text::money) as Neg from gltrans where gltrans_amount< 0 group by gltrans_sequence) as neg,<br /> (select gltrans_sequence, sum(gltrans_amount::text::money)as pos from gltrans where gltrans_amount > 0 group by gltrans_sequence) as pos<br />where neg.gltrans_sequence = pos.gltrans_sequence <br /> and neg.neg <> pos.pos*-1</font><br /> ----------------<br/><font color="#3366ff">select * <br /> from <br /> (select gltrans_sequence, sum(gltrans_amount::text::money)as Neg from gltrans where gltrans_amount < 0 group by gltrans_sequence) as neg,<br />(select gltrans_sequence, sum(gltrans_amount::text::money) as pos from gltrans where gltrans_amount > 0 group by gltrans_sequence)as pos<br /> where neg.gltrans_sequence = pos.gltrans_sequence <br /> and neg.neg::text::money <>pos.pos::text::money*-1</font><br /> -------------<br /><font color="#3366ff">select * <br /> from <br /> (selectgltrans_sequence, sum(gltrans_amount) as Neg from gltrans where gltrans_amount < 0 group by gltrans_sequence)as neg,<br /> (select gltrans_sequence, sum(gltrans_amount) as pos from gltrans where gltrans_amount >0 group by gltrans_sequence) as pos<br /> where neg.gltrans_sequence = pos.gltrans_sequence <br /> and neg.neg::text::money<> pos.pos::text::money*-1</font><br /><br /> -------------------<br /> Nothing resulted in showinga entry that was out of balance. <br /><br /><br /><br /><br /><br /> ----------------------Identifying the problem---------------------------<br /><br /> So i turned my attention to the view which casted numeric type to Money. View is called trailbalance <br /><br /> ------------The Bad Select Statement that creates the View --------------<br/><font color="#3366ff">SELECT p.period_id, p.period_start, p.period_end, a.accnt_id,<br /> a.accnt_number,a.accnt_descrip, p.period_yearperiod_id,<br /> a.accnt_type,<br /> SUM(CASE WHEN g.gltrans_date< p.period_start<br /> THEN g.gltrans_amount ELSE 0.0<br /> END)::text::moneyAS beginbalance,<br /> SUM(CASE WHEN g.gltrans_date <= p.period_end<br /> ANDg.gltrans_date >= p.period_start<br /> AND g.gltrans_amount <= 0::numeric<br /> THEN g.gltrans_amount ELSE 0.0<br /> END)::text::money AS negative,<br /> SUM(CASE WHENg.gltrans_date <= p.period_end<br /> AND g.gltrans_date >= p.period_start<br /> AND g.gltrans_amount >= 0::numeric<br /> THEN g.gltrans_amount ELSE 0.0<br /> END)::text::money AS positive,<br /> SUM(CASE WHEN g.gltrans_date <= p.period_end<br /> AND g.gltrans_date >= p.period_start<br /> THEN g.gltrans_amount ELSE 0.0<br /> END)::text::money AS difference,<br /> SUM(CASE WHEN g.gltrans_date <= p.period_end<br /> THEN g.gltrans_amount ELSE 0.0<br /> END)::text::money AS endbalance<br /> FROM period p<br /> CROSS JOIN accnt a<br /> LEFT JOIN gltrans g ON (g.gltrans_accnt_id = a.accnt_id<br /> ANDg.gltrans_posted = true)<br /> where p.period_id = 58<br /> group by p.period_id, p.period_start, p.period_end, a.accnt_id,<br/> a.accnt_number, a.accnt_descrip, p.period_yearperiod_id,<br /> a.accnt_type<br /> <br /> ORDER BY p.period_id, a.accnt_number;</font><br /> ---------------End Select --------------------<br /><br /><br /> Thequery that calls this View <br /><br /> ------------------<br /><font color="#3366ff">Select <br /> sum( beginBalance) as beginbalance, <br /> sum( negative ) as debit, <br /> sum( positive ) as credit, <br /> sum(difference ) as difference, <br /> sum( endbalance) as endbalance <br /> from trailbalance </font><br /> ---------------------<br/><br /> Result is <br /><br /><font color="#ff0000">-$0.01</font> -$11,250,546.74 $11,250,546.75 <fontcolor="#ff0000"> -$0.02</font> <font color="#ff0000">-$0.01</font><br /><br /> This be wrong. <br /><br/> Figuring it must be Money type dropped and recreated the view without the money casting. <br /><br /> ------------TheFixed Select Statement that creates the View --------------<br /><font color="#3366ff">SELECT p.period_id,p.period_start, p.period_end, a.accnt_id, a.accnt_number, a.accnt_descrip, p.period_yearperiod_id, a.accnt_type,sum(<br /> CASE<br /> WHEN g.gltrans_date < p.period_start THEN g.gltrans_amount<br /> ELSE 0.0<br /> END) AS beginbalance, sum(<br /> CASE<br /> WHEN g.gltrans_date<= p.period_end AND g.gltrans_date >= p.period_start AND g.gltrans_amount <= 0::numeric THEN g.gltrans_amount<br/> ELSE 0.0<br /> END) AS negative, sum(<br /> CASE<br /> WHENg.gltrans_date <= p.period_end AND g.gltrans_date >= p.period_start AND g.gltrans_amount >= 0::numeric THENg.gltrans_amount<br /> ELSE 0.0<br /> END) AS positive, sum(<br /> CASE<br /> WHEN g.gltrans_date <= p.period_end AND g.gltrans_date >= p.period_start THEN g.gltrans_amount<br /> ELSE 0.0<br /> END) AS difference, sum(<br /> CASE<br /> WHEN g.gltrans_date <=p.period_end THEN g.gltrans_amount<br /> ELSE 0.0<br /> END) AS endbalance<br /> FROM periodp<br /> CROSS JOIN accnt a<br /> LEFT JOIN gltrans g ON g.gltrans_accnt_id = a.accnt_id AND g.gltrans_posted =true<br /> GROUP BY p.period_id, a.accnt_number, p.period_start, p.period_end, a.accnt_id, a.accnt_descrip, p.period_yearperiod_id,a.accnt_type<br /> ORDER BY p.period_id, a.accnt_number;</font><br /> ---------------End Select--------------------<br /><br /><br /> The above query results in this which is what i would expect. <br /><br />0.00000000 -11250546.74375232 11250546.74375232 0.00000000 0.00000000<br /><br /><br /> Now knowing forsure its in Money type casting i do this select statement <br /> ----------------------<br /><font color="#3333ff">Select<br /> '2',<br /> sum( beginBalance )::text::money as beginbalance, <br /> sum( negative)::text::money as debit, <br /> sum( positive )::text::money as credit, <br /> sum( difference )::text::moneyas difference, <br /> sum( endbalance)::text::money as endbalance <br /> from trailbalance <br /> union<br /> Select <br /> '1',<br /> sum( beginBalance::text::money) as beginbalance, <br /> sum( negative::text::money)as debit, <br /> sum( positive::text::money) as credit, <br /> sum( difference::text::money)as difference, <br /> sum( endbalance::text::money) as endbalance <br /> from trailbalance </font><br/> -------------------------<br /><br /> The results I think very interesting <br /> "1" <font color="#ff0000">-$0.01</font> -$11,250,546.74 $11,250,546.75 <font color="#ff0000">-$0.02;</font> <font color="#ff0000">-$0.01</font><br/> "2" $0.00 -$11,250,546.74 $11,250,546.74 $0.00 $0.00<br /><br /> As youcan see casting to money before sum() is called are incorrect<br /><br /> Can anyone else confirm this odd behavior whencasting to Money type.<br /><br /> Thank you for your time and patience reading this long post....<br /><br /><br /><br/>
pgsql-hackers by date: