I've got a quick question about default values in PL/SQL functions in Oracle. Take this program as an example;
create or replace
FUNCTION testFunction
(
varNumber IN NUMBER DEFAULT 0
)
RETURN NUMBER
AS
BEGIN
dbms_output.put_line(varNumber);
RETURN varNumber;
END;
The idea here being that if no value is specified for varNumber when this function is called, then it will take the value of 0.
Now, my problem is that my functions are getting called from a web services layer that will always pass in NULL as the value for parameters which it doesn't have a value for. Oracle interprets NULL as a value, and so does not initialise varNumber to its default of 0.
I can see why this approach makes sense, but I was wondering if there was a way to override this behaviour, and make it so that if a NULL value is passed, that it causes Oracle to assign the explicit DEFAULT value that is specified in the function header?
I have considered the option of doing a manual check...
IF(varNumber IS NULL) THEN
varNumber := 0;
END IF;
However, there are hundreds of functions where this may be an issue, never mind the large number of parameters per function, and so I'd prefer it if I could find a more general solution to the problem.
Cheers for any insight you can give.
Use NVL to define the value.
NVL( value_in, replace_with )
You can't assign values to an IN parameter, but you could make them IN/OUT and then set them. That raises a big potential for misuse and confusion, though.
So I think you'd do better with a local variable. But you can do it in the declaration. That is,
create or replace
FUNCTION testFunction
(
varNumber IN NUMBER DEFAULT 0
)
RETURN NUMBER
AS
vFix number := nvl(varNumber,0);
BEGIN
dbms_output.put_line(vFix);
RETURN vFix;
END;
Your manual check is the only way to safely do what you want.
You can write that in one line like this though:
varNumber = NVL(varNumber,0);
Good luck!
Related
Consider the following example:
create or replace function f(n integer) return integer as
begin
return n;
end;
/
begin
dbms_output.put_line(f(3.8));
end;
/
3.8
PL/SQL procedure successfully completed.
This makes no sense to me. Obviously, PL/SQL simply ignores the integer specification, both on entering the function and on exiting it. Is this simply a bug? Is it a design choice, made deliberately by the language developers?
Here is why I find this confusing. Compare to the following example:
declare
x integer;
begin
x := 3.8;
dbms_output.put_line(x);
end;
/
4
PL/SQL procedure successfully completed.
In this example, the data type specification is complied with. PL/SQL doesn't throw an error, but at least it performs an implicit coercion and it does not violate the data type declared for x - the variable stores the value 4, an integer, not 3.8.
So, how does PL/SQL do the function call thing in the first example? As far as I understand (never having been trained formally in computing), whenever the compiler or interpreter finds a function call it creates a stack frame, with variables for the arguments passed to the function and for the return value to come back from the function. Aren't these variables, when the stack frame is created, supposed to be the same data type as specified in the function declaration? If the stack frame has a field of integer data type for the argument 3.8, how come that is not coerced to 4 before it is even stored in the corresponding variable? And the same thing for the return value: if the function returns 3.8 but the caller expects an integer (and therefore the corresponding variable in the stack frame should be integer), how is it able to accept the return value 3.8?
And, most disturbing - why is this behavior different from the behavior when explicitly declared variables are involved (as in my second example)?
Thank you for sharing your thoughts!
The answer is found in the documentation for Oracle Database (to which your question has absolutely no relation whatsoever).
Firstly, INTEGER in the said database is a synonym for NUMBER(38). Upon assignment to a NUMBER(38) variable x, as in your second example, according to the assignment rules the NUMBER (with arbitrary precision) literal 3.8 is rounded.
In your first example though no assignment happens because IN parameters to PL/SQL subprograms are passed by reference and the same reference (to the NUMBER value 3.8) is returned.
I have two procedures in oracle which define a parameter differently, even though the underlying data is the same. In code we represent it as an integer.
procedure GetByNum(..., vRegionID in number, ...)
procedure GetByInt(...., vRegionID in integer, ...)
In java we always define the field as an Integer (and in db too)
public Integer getRegionID() {
return 100;
}
Is there a way to pass this (in java) integer to both procedures using the same OracleTypes.*? We use a custom base wrapper around org.springframework.jdbc.object.StoredProcedure to call both of them. Modifying the stored procedures is not allowed for right now, so all that is left is fixing up the java.
Can you pass getRegionID as OracleTypes.NUMBER to the getByInt proc?
Can you pass it as OracleTypes.INTEGER to getByNum, which expects in number?
Another, magical way?
This might seem simple, but I assume i'm not allowed to mangle parameters like this. I hope thats not true!
Edit:
Using OracleTypes.INTEGER to pass to procedure ... (param in number) worked
Don't know if this is just environment specific though
Hoping for a definitive answer, so I can have them start using the new wrapper without worrying about an unforeseen issue.
INTEGER is a ANSI standard type, which Oracle describes as a subtype of NUMBER as NUMBER(38). Therefore, anywhere that takes an INTEGER as a parameter should also accept a NUMBER so long as there are no fractional parts.
I am getting a rather strange behaviour when invoking oracle instr function, or probably I'm blind enough not to see my stupid mistake.
Actually I written a procedure to split a string. For example if we have string
a,e,i,o,u
then my split method will look like
string_split('a,e,i,o,u',',',5);
where first parameter is the string to split while second one is the separator and third one is the number of element I know is there after splitting.
Now, of number of things , one thing my procedure do is invoke
start_index := instr(temp_string_to_split,',',1,(total_element-i));
But the moment it is invoked I get a
ORA-06512 ,numeric or value error
But if I invoke
start_index := instr(temp_string_to_split,1,(total_element-i));
the procedure runs,though not in a desirable manner. Note that in second invocation separator parameter is missing, and directly number is passed as the second parameter, which I guess should have cause big time exception. But surprisingly it goes and run fine.
Can somebody explain this anomaly...or help me see if I'm missing something.
Thanks,
Mawia
I'm assuming that in your call to instr, temp_string_to_split is the string that was passed to string_split, and (total_element-i) is meant to be an iterator over the number of splits to make. (As an aside, it seems odd that you have ',' hardcoded in this call, when you appear to be passing it as a parameter to string_split.
I tried emulating this with the following SQL, which worked fine:
SELECT LEVEL,instr('a,e,i,o,u',',',1,LEVEL)
from dual connect by level < 5;
Do you know the exact values of temp_string_to_split, total_element, and i on the call to instr that caused the error?
Thanks a lot all for responding.
Actually as I told earlier, I was calling
start_index := instr(temp_string_to_split,',',1,(total_element-i));
in a loop. Now as a final value of the loop
(total_element-i)
was getting negative. And this was the root of malady.
One thing, I'm still puzzled though is as it was a run time generated condition, that is to say everything before the final invocation was legal. Then why I dont see on console the result of few of DBMS_OUTPUT.PUT_LINE statement which I had put into to trace the execution.
Thanks,
Mawia
I have a VB6 function, which executes an SQL delete. The function returns a boolean depending on whether or not the deletion was successful:
Public Function Delete(ByVal RecordID As Integer) As Boolean
On Error GoTo ErrorGenerated //Execute SQL delete
Delete = True
Exit Function
ErrorGenerated: Delete = False
End Function
I read somewhere that it is better to return an integer, which dictates whether or not the deletion was successful. However, there can only be two outcomes from running the function from what I can see i.e. deleted or not deleted (not deleted if an error is thrown). Is it better to return an integer?
I'd suggest your best bet is to return an enumerated type; each value for the enumeration can then explain to the caller what the problem is in a clear and unambiguous way, and new error reasons can be added later as required without breaking anything. Something like...
Public Enum DB_ERRS
Success
NoConnection
FailedForThisReason
FailedForThatReason
FailedForOtherReason
Failed
End Enum
Then all your database access functions could return a value of this type...
Public Function Delete(ByVal RecordID As Integer) As DB_ERRS
On Error GoTo ErrorGenerated
Execute SQL delete
Delete = Success
Exit Function
ErrorGenerated:
If Err.Number = this Then
Delete = FailedForThisReason
Else
Delete = Failed
End If
End Function
Intellisense will even help you fill them in.
This is rather subjective.
One would say, return a boolean because it's as simple as it gets.
Another one would say, return an integer, because later you might want to add a third status, such as "archived," and it would break existing code.
And someone else would say, Ditch that C-style return codes. Create a sub that doesn't return anything, and raise an exception in case you need to indicate failure.
I personally prefer exceptions. But it's up to you to decide.
In terms of size, an integer is a 32-bit signed integer, while the boolean data type doesn't really have a defined size. However, it also depends on the context from where you've read about using integers over booleans.
For SOME, the difference is irrelevant when using it as a return value from functions.
However, it can be something of a preference in stored procedures if you're also considering the return value from the stored procedure. The evaluation of booleans (when converted to numbers) may lead to it being treated like a bit (0 and 1).. In any case, it's more of a subjective approach. Integers allow more flexibility, while booleans offer limitation and simplicity. Which is better? I think it's almost entirely up to you, your preference, your coding standards, your company's coding standards, or whatnot..
Just to share a link on data types :
http://msdn.microsoft.com/en-us/library/aa383751(v=vs.85).aspx
I'll throw my opinion in. I personally think that returning a boolean value is the right thing to do. Do you really care why it failed to delete? Not normally, there are only a few reasons why a delete could fail in the first place (file locked or lack of permissions). If you need to return the reason for failure so it can be handled differently in some way, then yes, return an integer. Now personally, I don't like magic numbers, so I would never return an integer and would return an enum value instead.
At work we often use functions returning a BOOLEAN where the BOOLEAN represents a logical statement and not whether the operation of the function was successfully or not
e.g. BOOLEAN HaseThisValueBeCountedAlready (Value)
When validating the input in this function what would be the best way proceed if invalid input was detected. Some people think to just return FALSE but in my opinion that would just hide the fact that something is wrong and the Caller might proceed doing something with the value not knowing that the answer doesn't make sense.
The function might be globally accessible so it feels a bit weird assuming the caller will validate the input.
Any ideas?
In general, for invalid input that doesn't enable the functions to provide the service/answer, you need to raise an exception.
This way, the guy asking the "question" to the function knows he's not "formulating" it the right way.
if its a value that need to be read periodically , you can assign the output to a global variable ,if it valid or dont update global variable if the input is invalid , so the global variable stays with the previous valid value.
this way , each function need this value , use the global variable with 100% that is valid value.