Skip to Main Content

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question, Mladen.

Asked: March 05, 2004 - 10:10 am UTC

Last updated: March 30, 2007 - 1:28 pm UTC

Version: 8.1.7

Viewed 10K+ times! This question is

You Asked

Hi Tom,

My problem is handling of zero_divide exception.
I'm using INSERT INTO T1 (SELECT... FROM T2) syntax. Tables T1 and T2 are known only at the runtime.
SELECT contains many calculations as per example:
SELECT C1/C2,C3+(C4/C5)... FROM T2

I would like to identify rowid of the row raising zero_devide exception.

Thank you very much for everything that you done on this site. It is very helpful for me.

Best regards.
Mladen



and Tom said...

select decode( c2, 0, to_number(null), c1/c2 )


will accomplish your goal safely. Use decode to avoid evaluating a zero divide condition.

Note: the to_number is not "extra", it is necessary to avoid implicit conversions to and from strings.

Note2: I'm assumging when c2 is zero, the returned value should be "unknown", if it should be zero or some other number, it is OK to return that as well:

decode( c2, 0, 0, c1/c2 )



Rating

  (14 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

More or less the same problem

Gabriel, February 17, 2005 - 9:29 am UTC

My problem is that we have an application where the end user can introduce a formula, wich will eventually turned into a dynamic sql. The idea is to return a null if the expression encounters a zero-divide expression.

Our options are
- Use a function to wrap around the expression that would catch the zero-divide exception. I don't know how to solve this.

- Parse the expressions the user can introduce. This is difficult to do.


Do you know a way to implement the first solution ?
Something I could use as follows :
SELECT myfunc(C1/C2),myfunc(C3+(C4/C5))... FROM T2


Thank you so much for this and for your books, they have been very helpful.

Cheers
Gabriel


Tom Kyte
February 17, 2005 - 10:37 am UTC

parsing the expression would not help unless the function returns a constant value, in which case -- it would not be much of a function!



I'm not liking the idea, before I answer how to do in a function, tell me -- how many of these functions are there? why are we not using a view? how often are they executed?



Sounds Dangerous ?

Paul, February 17, 2005 - 11:10 am UTC

"My problem is that we have an application where the end user can introduce a formula, wich will eventually turned into a dynamic sql"

If I understand that correctly you are taking the user input (a formula) and including it directly in your SQL query. Sounds like a huge SQL injection security hole to me.

If you aren't parsing the input how do you know the user will always input valid column names?

Tom Kyte
February 17, 2005 - 1:43 pm UTC

the entire thing sounds scary to a degree...

Let me explain

A reader, February 17, 2005 - 12:02 pm UTC

What we have here is a denormalized table of objects and counters. The table T have the following columns:
OBJ_ID
C01
C02
C03

In the user interface (web based), the user can create, at any time, a derived counter by defining a formula that can be C01/C02 or (C01+C03)/C02/C01 for example. Those formulas will be stored in some table and resulting of that, an engine will create some dynamic sql which will look like this :

SELECT C01/C02, (C01+C03)/C02/C01 FROM T

as we can see, if C02 or C01 for example are equal to zero the sql statement will fail with zero_divide exception

I cannot predict the formula the user will enter in the system that's why I am not able to create a view with the decodes and so. Another option would be to "force" the user to enter the formula "C01/C02" in the following format "decode( c2, 0, to_number(null), c1/c2 )" which is not user friendly and forces the user to know SQL language.
Specially if the formula is more complex like (C01+C03)/C02/C01 for example.

The botom line is that maybe the engine has to parse the string the user enters and replace it with the protected version of it.

This will be executed periodically since this is a near real time system used for supervision.

Is there another way to solve this ? By catching the zero_devide exception for example ... that's why I came up with the function example...

Tom Kyte
February 17, 2005 - 1:56 pm UTC

so, user could also give you:

my_pkg.fire_emp( 55 )

or:

(select data_i_should_not_see from table_I_shouldnt_access where ... )

because you can select a select

select (select user from dual) from dual;


you are giving them an interface to run many queries as you, to run functions as you.


is it me, or does this sound potentially dangerous to you too? still sure you want to give your end users SQLPlus access using your web applications credentials?


Forgot something

Gabriel, February 17, 2005 - 12:06 pm UTC

Sorry I forgot to sign the previous Review

Some more details

Mladen, February 18, 2005 - 4:32 am UTC

Hi,

Let me explain my case in details:
- I had a similar problem with the user who can input the formula using specific user interface that parse the user input in order to let enter only valid column names, operators etc.
- Formulas entered by the user are then executed by the application engine using dynamic SQL. Our parser allows C1/C2 formulas and we had zero_divide exception at the execution time.

There is a two ways to solve this problem:
1. Your users must use decode function in such cases (that was our choice, decode is finally not too dificult to learn)
2. You must integrate parsing engine in order to automatically add decode in your formulas.

I regret that Oracle cannot return ROWID of the record raising an exception in order to easy identify the record with the problem.

Tom Kyte
February 18, 2005 - 8:44 am UTC

you do understand the SECURITY implications of such a thing? yes?  you really do right?  You never seem to mention it -- it is pretty big, very popular, and way easy to exploit.  


you can easily write a plsql function

ops$tkyte@ORA9IR2> create table t ( x int, y int, z int );
 
Table created.
 
ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2> insert into t
  2  select rownum-1, rownum-2, rownum-3
  3  from all_users where rownum <= 5;
 
5 rows created.
 
ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2> create or replace function eval( p_rowid in rowid, p_tname in varchar2, p_function in varchar2 ) return number
  2  as
  3          l_return number;
  4          zero_divide exception;
  5          pragma exception_init( zero_divide, -1476 );
  6  begin
  7          execute immediate
  8          'select ' || p_function || ' from ' || p_tname || ' where rowid = :x'
  9          into l_return using p_rowid;
 10
 11          return l_return;
 12  exception
 13          when zero_divide
 14          then
 15                  return null;
 16  end;
 17  /
 
Function created.
 
ops$tkyte@ORA9IR2> select t.*, eval(t.rowid, 't', 'x/y+z' ) from t;
 
         X          Y          Z EVAL(T.ROWID,'T','X/Y+Z')
---------- ---------- ---------- -------------------------
         0         -1         -2                        -2
         1          0         -1
         2          1          0                         2
         3          2          1                       2.5
         4          3          2                3.33333333
 
ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2> grant execute on eval to scott;
 
Grant succeeded.
 
<b>caveat emptor</b>


ops$tkyte@ORA9IR2> @connect scott/tiger
ops$tkyte@ORA9IR2> set termout off
scott@ORA9IR2> set termout on
scott@ORA9IR2>
scott@ORA9IR2> select count(*) from dba_users;
select count(*) from dba_users
                     *
ERROR at line 1:
ORA-00942: table or view does not exist
 
 
scott@ORA9IR2> select ops$tkyte.eval( rowid, 'dual', '(select count(*) from dba_users)' ) from dual;
 
OPS$TKYTE.EVAL(ROWID,'DUAL','(SELECTCOUNT(*)FROMDBA_USERS)')
------------------------------------------------------------
                                                          24
 
 

NVL-like functionality

Gabriel, February 18, 2005 - 6:13 am UTC

The objective of our application looks like the same as yours Mladen.

The only "feature" I was looking for here was a function like "NVL" that, instead of testing "NULL" values would test the zero_divide exception.

Something like
FUNC ( expr1 , expr2 )

Where the function FUNC would return 'expr2' in case 'expr1' raised a zero_divide exception.





Tom Kyte
February 18, 2005 - 8:58 am UTC

you got it above, just add a parameter.

This might be af job for regexps

Anders, February 18, 2005 - 9:16 am UTC

if REGEXP_LIKE('2+df',
'^[-+]?([a-z][a-z0-9_]*|[0-9]+)' ||
'([-+*/]([a-z][a-z0-9_]*|[0-9]+))*$', 'i') then
... run_query ...


Tom Kyte
February 18, 2005 - 9:29 am UTC

how so? you have columns referenced in the function -- not values of columns.

And performance

A reader, February 18, 2005 - 9:36 am UTC

Security issue aside (as if), you'll also be opening up the potential for horrendously badly written subqueries to be entered and potentially kill performance.

e.g. rather than "c1/c2" user types "(select count(*) from reallybigunindexedtable bt where c1 > bt.col1)"



Re: This might be af job for regexps

Anders, February 18, 2005 - 9:43 am UTC

My the point was that the regexp would only match safe expressions and therefor could be used to make your "eval" (evil?) function safe.

(Would be nice if Oracles regexp-implementation was Perl compatible)

Tom Kyte
February 18, 2005 - 10:02 am UTC

how does that make it safe?

XX1, November 02, 2005 - 10:39 am UTC

I am receiving an ora-1476 error, but it only seems to affect the WHERE clause of the statement.

SELECT (hrs.value/div.value)
,....
FROM ...
WHERE ...
AND (hrs.value/div.value) <> paf.normal_hours

fails with ora-1476: divisor is equal to zero

Using DECODE in the WHERE clause:

SELECT (hrs.value/div.value)
,....
FROM ...
WHERE ...
AND (hrs.value/DECODE(div.value,0,1,div.value))
<> paf.normal_hours

works OK.

I also checked the query runs OK, after changing the <> to =, just to make sure that the row causing the problem wasn't being excluded.

So (hrs.value/div.value) seems to work OK in the select but not in the where clause. Is there an explaination for this?

div is a table used as a lookup and only contains a few rows and value is never set to zero. The value column is a varchar2.


Tom Kyte
November 03, 2005 - 5:16 am UTC

You have no control over the order of evaluation of a predicate.

they are free to do the

AND (hrs.value/div.value) <> paf.normal_hours

whenever they want - before you apply a filter that removes the div.value = 0 rows...


I would suggest doing the decode in the SELECT as well - there is no assurance it won't zero divide in general.




Finding number of DML errors generated after DML statements in SQLPLUS

Rohit, February 16, 2006 - 6:51 am UTC

Hello Tom,

I am trying to excute a set of INSERT/UPDATE statements (more than 50k) through SQLPLUS. Before commiting the operation, I want to know how many statements have failed. Is there a way to do this in sqlplus. If this number is > zero, I will ROLLBACK the operation.

Thanks




Tom Kyte
February 16, 2006 - 12:00 pm UTC

wrap your sql in plsql so you can have some control over it

OR

whenever sqlerror exit rollback;

and sqlplus will exit - rolling back all work - upon the first error (makes sense, why bother continuing)


mahbod, February 06, 2007 - 8:45 am UTC

hi
i want divide A/B into result and i want manage that if B=0 then result=0

note: i want use only DECODE function

for example
decode(A/B , ? , 0 , A/B)


Tom Kyte
February 06, 2007 - 11:23 am UTC

select decode( b, 0, 0, a/b)

Thats really not helpful

David, March 16, 2007 - 11:42 am UTC

Yes decode(b,0,null,a/b) will avoid the divide by zero. But what should you do when b is actually a calculated field potentially with its own zero-divide bugs as in:

(1+a+b)/(1+c+d/e)

Solution 1: Encapsulate each division operation in its own decode as in: decode(e,0,null,decode(1+c+d/e,0,null,(1+a+b)/(1+c+d/e)))

Why is this not really a solution? Suppose your boss decides the new equation should be (1+a+b+f/g)/(1+c+d/(e+f/g)) and that this needs to occur in multiple queries in the database, not to mention that it is now extremely difficult to read and understand the code.

Solution 2: Use inner select queries to name the numerators and denominators. This makes the code more readable.

Why this isn't really a solution: A 100 line query (80 lines of which are column declarations) becomes a 500 line query 400 lines of which are duplicated query names from the previous selects.

Solution 3: Write a function to calculate the value.

This is the best solution so far -- but why does everything in Oracle have to be so difficult? A function is also completely unsuitable for dynamic code and ad-hoc queries. I'm seriously considering writing a function d(x number, y number) which returns x/y just to avoid this inane limitation in Oracle.

What the solution should be: Introduce an option/hint to the parser
Select /*divide_by_zero_is_null*/

which tells the Oracle to convert all divide by zeros into null for this query.

In some cases its better to get some data instead of getting none at all.
Tom Kyte
March 17, 2007 - 3:56 pm UTC

umm, it is very useful for it shows you how to avoid a zero divide

and hey, if you can avoid a zero divide once, you can avoid it twice and so on.


If you want to avoid zero divides, you will do the needful, period. Not really caring that you think this is "inappropriate", it is sort of the way it is done.


...
In some cases its better to get some data instead of getting none at all. ....


that is a really bad idea.


This is not oracle being difficult, tell me of a language - say java, or C - whereby a zero divide is just something to ignore?

An example

David, March 29, 2007 - 9:35 pm UTC

An example of a language where divide by zero is not a problem:
Oracle SQL
In particular: ratio_to_report()

I'm not saying that all divisions by 0's should be converted to nulls but that the developer should be given the choice and ability to easily avoid them. I would be pissed if my paycheck was 0 because rogue divide by zero nulled it out, but there are instances where division by zero is not a problem -- or better yet has no solution [How does one calculate percent growth of something that started at 0?]. To have to manually wrap every single division statement that might lead to an error is a waste or time. One cannot always validate that the data will not lead to such a division especially in group aggregations. Ratio_to_report recognizes this and wraps the division so that it doesn't cause ORA-01476. For everything else we have to explicitely wrap it ourselves. This leads to additional sub-selects and difficult to maintain code. This is furhter complicated by the "deprecation" of decode -- Which given how useful it is probably won't occur before the "Big Crunch" or "Heat Death" of the Universe -- "case d when 0 the null else n/d end" in every query in the system is going to be fun, thank god I will be dead before that happens (I only expect to be alive for another 80 years).

We have exceptions. So lets use them.
Set a flag which tells the engine to ignore XXX exception and apply the natural corrective action (with the understanding that developer is giving up something in the process).

I understand why Oracle can't take a key violation and drop the conflicting row when doing block level inserts. Fair enough. Its the price we pay for using a robust transactional database.

So if your answer were: "The results of Select are calculated at the block level and there is no way to determine the problem row so all rows would have to be returned with null which is unacceptable." That would at least be a start. But I doubt that is the case and ratio_to_report suggests that it is possible to trap the division at some point. It seems that somewhere in the Oracle kernel either
a) a thread is trapping the CPU NaN exception, clearing it and passing back the Oracle divide by 0 exception.
or
b) a thread is validating the data before passing the division to the CPU.
or
c) the ratio_to_report aggregate is checking the division before evaluating

Applying the (c) type solution across the board could have performance implications so I can understand why it might not be implemented.

That still leaves (a) or (b): why can't a flag be set on that thread telling it to correct the divide by zero, and continue?

My only guess is that they just didn't think of this when coding the engine and the required changes would have to be made across multiple architectures and in very critical code segments and as a result simply cannot be fixed without major rewritting of code which could potentially seriously affect system stability and as a result wouldn't see production for years.

And if thats the case I can accept that, every program has bugs and design oversights, mine more than most. But admitting to the error is much better than saying "Fix it yourself" "Oracle is doing the right thing" "What did you expect to happen" which is what I see a lot of on AskTom.
Tom Kyte
March 30, 2007 - 1:28 pm UTC

ratio_to_report is a function, not a "language feature"


... given the choice and ability to easily avoid them. ...

and I would argue THEY ARE

decode(x,0,null,x)

that would make division by zero (x) NULL.

we'll have to agree to disagree - I don't see the decode() to be onerous at all.