11

Hi there I am looking for common mistakes pl/sql programmers make when they program applications which deals with a lot of monetary computations. (Discounts, tax, rates, etc.) I had been using java and hibernate for monetary computations for as far as I can remember java has a set of rules and best practices like BigDecimal to preserve precision and etc. Right now Im trying to improve my pl/sql skills in dealing with financial modules thats why I want to know this gotchas and avoid them. Also are there any existing mantra or pl/sql best practice for this? Thanks in advance guys.

royjavelosa
  • 2,048
  • 6
  • 30
  • 46
  • 2
    Why do you think that its a mistake? I mean related to my question here [Money computations, comparison between java and Oracle/PL SQL which to use](http://stackoverflow.com/questions/7171556/money-computations-comparison-between-java-and-oracle-pl-sql-which-to-use) some even suggested to use pl/sql 100% – royjavelosa Aug 26 '11 at 02:56
  • 3
    Oracle uses decimal numbers pretty much everywhere (you have to go out of your way to use binary numbers). However inserts into a table will silently truncate the value to the number format, so there's some scope for rounding errors (which I'd expand on if others vote to reopen the question) – Gary Myers Aug 26 '11 at 04:20
  • 1
    I really don't know why they closed this question when all I'm asking are common mistakes new pl/sql programmers make? The intention of the question is to avoid those mistakes and learn best practices from pl/sql experts here in SO. The question did not even compare pl/sql with anything to spark any debate or arguments. I dont find anything about gotchas and bestpractices debatable unless pl/sql does not have any established best practice when handling monetary computations, that which I strongly doubt because pl/sql has been around for a long time. – royjavelosa Aug 26 '11 at 05:19
  • 1
    I grant that it's a somewhat subjective question, but it's a topic for which a developer with experience using PL/SQL for financial apps might have specific, supportable advice to give. (Sadly, I am not that person.) Reopened. – Dave Costa Aug 26 '11 at 13:16
  • It might help to make the question more specific. For example, state some particular best-practices you use in Java and ask what if any equivalents exist in PL/SQL. – Dave Costa Aug 26 '11 at 14:07
  • @Dave Costa Comparing them with other programming languages will only cause confusion, debates and arguments. Id rather focus on PL/SQL alone Im sure it has its own set of best practices. Also the question is very clear as long as you focus on this two words 1)PL/SQL 2)money and nothing else. Thanks – royjavelosa Aug 28 '11 at 06:57
  • Not siphoning off interest calculation round-off errors into their secret account in the Bahamas :-) – paxdiablo Aug 30 '11 at 03:07

3 Answers3

6

Using this example:

create table t_val
  (id number(10,0),
  value number(5,2));

declare
  v_dummy number;
begin
  delete from t_val;
  for i in 9 .. 20 loop
    insert into t_val values (i, 1/i);
    select count(*)
    into v_dummy 
    from t_val 
    where value = 1/i;
    dbms_output.put_line(to_char(i,'00')||':'||v_dummy||':'||
              to_char(1/i,'000.999999'));  
  end loop;
  --
end;
/

select id, value from t_val order by 1;

You can see that the code inserts, for example, 0.11111 which is implicitly rounded to 0.11 When the code immediately tries to count the values for 0.11111 it fails to find any matches.

Similarly the values for (1/14) and (1/15) both get rounded to 0.07.

This isn't a problem specific to PL/SQL, and I've seen similar issues in Java and PHP code when the value in the 'client' is rounded when it makes it into the database. PL/SQL does offer a better solution though, as you can declare a variable of the type of a specific table/column, and this binding is maintained even if the column is changed.

declare
  v_value  t_val.value%type;
  v_dummy number;
begin
  delete from t_val;
  for i in 9 .. 20 loop
    v_value := 1/i;
    insert into t_val values (i, v_value);
    select count(*)
    into v_dummy 
    from t_val 
    where value = v_value;
    dbms_output.put_line(to_char(i,'00')||':'||v_dummy||':'||
            to_char(1/i,'000.999999')||':'||to_char(v_value,'000.999999'));  
  end loop;
  --
end;
/

So best practice is, when dealing with an SQL statement, use bind variables that are anchored to the type (including length/scale/precision) of the underlying table.

Gary Myers
  • 34,963
  • 3
  • 49
  • 74
6

Here are 2 quick hints:

The practical Oracle tip: use NUMBER (without scale/prec) both as table column type and in PL/SQL .. saves you alot headaches. NUMBER(x,y) does not save you any storage or cpu cycles ..

The general hint (you want to have the big picture first):

  • First, research what your specific app requirements regarding decimal arithmetic really are: do you do tax? if yes, US or EU? rounding rules are different depending on jurisdiction and/or application. Does Oracle SQL support the desired stuff? Does PL/SQL? If not, the mistake is to use PL/SQL to do that stuff anyway.

  • The upcoming gold standard for decimal arithmetic is IEEE decimal128. You may want to read on Wikipedia and probably http://www.carus-hannover.de/doc/DFP_PW6_in_SAP_NetWeaver_0907.pdf as an example. It supports all flavor of rounding etc. Don't know when it comes to Oracle QL / PL/SQL

oberstet
  • 21,353
  • 10
  • 64
  • 97
5

The problem with dealing with MONEY types in any language is:

  • Truncation - When you should have rounded but instead the result got truncated.
  • Rounding - When you should have truncated, but it got rounded.

If you are careful about these at every step, handling MONEY is not such a tough job.

Lazylabs
  • 1,414
  • 16
  • 23