No short-circuit OR with an Oracle function?
To allow an Super User/Admin to log in to my system, I am running (a larger version of) this query:
Select *
From mytable
Where (:id = 'Admin' Or :id = mytable.id);
If I pass a user id I get all the data for that user; if I pass the string 'Admin' I get all the data. This works because Oracle's OR is a short-circuit operator.
However, if I make 'Admin' a package constant and get it with a function, like this
Select *
From mytable
Where (:id = mypackage.GetAdminConstant Or :id = myt开发者_开发百科able.id);
I get ORA-01722: invalid number
when I pass 'Admin'.
Why does OR lose its short-circuit aspect when I introduce a function?
It doesn't lose the short-circuit aspect. But SQL is not a procedural language, and there is no guarantee of the order of evaluation of multiple predicates.
In C, if you write a || b
, you know that a
will be evaluated first, then b
will be evaluated only if necessary.
In SQL, if you write a OR b
, you know only that either a
or b
will be evaluated first, and that the other expression (at least in Oracle) will be evaluated only if necessary.
Looking at the execution plan for the two queries may give some indication of the order of evaluation, or it may not.
I would guess that, in your first case, Oracle can see that the first expression will have the same value for every row, so evaluates it first. When you change to the second case, Oracle now sees a function that could have different results each time it is evaluated, so it will have to check each row, so it tries to do the simple equality check on a column before doing the function call.
I wonder if you would get different results if you marked the function DETERMINISTIC so Oracle would know that it is essentially a constant.
Better use 2 bind variables.
Select *
From mytable
Where (:admin = 'Admin' Or (:admin is null and :id = mytable.id));
精彩评论