Why does NVL always evaluate 2nd parameter - sql

Does anyone know, why Oracle's NVL (and NVL2) function always evaluate the second parameter, even if the first parameter is not NULL?
Simple test:
CREATE FUNCTION nvl_test RETURN NUMBER AS
BEGIN
dbms_output.put_line('Called');
RETURN 1;
END nvl_test;
SELECT NVL( 0, nvl_test ) FROM dual
returns 0, but also prints Called.
nvl_test has been called, even though the result is ignored since first parameter is not NULL.

It's always been that way, so Oracle has to keep it that way to remain backwards compatible.
Use COALESCE instead to get the short-circuit behaviour.

Here is a post where Tom Kyte confirms that decode and case short circuit but not nvl but he doesn't give justification or documentation for why. Just states it to be:
http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:926029357278#14932880517348
So in your case you should use decode or case instead of nvl if an expensive function will be called in your query.

In general, it would make sense that the second parameter is evaluated before calling the function, because in general that is how functions are called: all arguments to the function are evaluated and the evaluated values are sent to the function.
However, in the case of a very common system function like NVL, I would have thought PL/SQL could optimise, treating the function call as a special case. But perhaps that is more difficult than it sounds (to me), as I'm sure this optimisation would have occurred to the developers of Oracle.

They are obviously not short-circuiting, but I can't find any references in Oracle documentation.
Check out this discussion: http://forums.oracle.com/forums/thread.jspa?messageID=3478040

Related

SQL IsNull function

I saw a sql code using the isNull function in the where clause, similar like below:date>isNull(date1,date2)
Can someone explain what this means?
Many Thanks!
Michelle
declare #date1 datetime = null
declare #date2 datetime = getdate()
select isNull(#date1,#date2)
IsNull will return the first one going left to right that is not null
If #date1 is not null, it will return #date1.
If #date1 is null, then it will return #date2.
For more details the docs are pretty good:
https://learn.microsoft.com/en-us/sql/t-sql/functions/isnull-transact-sql?view=sql-server-ver16
To explain the ISNULL function is not necessary because there are lots of documentations about it, just google and read them. One of them is linked in the other answer.
Necessary is - at least in my opinion - to point that using such a function is in many cases bad practice.
Why that? There are two big disadvantages of that function:
The function is a non standard function. This means it is provided in some DBMS like for example SQL Server DB, but it is missing in some others like for example Oracle DB. Using non standard SQL when Standard SQL is possible makes no sense to me. Often, people change the DBMS, so each usage of non standard SQL produces effort to rewrite these queries or commands.
The ISNULL function only accepts one value which should be checked and replaced if NULL. This means it must be use nested in case more arguments should be used. This can get very bad readable. What a sad implementation in my point of view.
How to get rid of these unpleasant limitations? Well, we can just use COALESCE. This is SQL-standard, will work on every DBMS and accepts a lot of arguments.
So we can just write things like
SELECT COALESCE(column1, column2, column3,...,0) FROM yourtable;
This will take the value of the first column of all columns that we write there which is NOT NULL. If all of those columns are NULL, then zero will be selected in this example. And we will very likely never have to rewrite this when we change the DBMS.
To be fair: There are some edge cases, so it can sometimes be better or even really necessary to use the ISNULL approach. I don't want to create the impression that function is not useful at all. But very often, people just use it because they saw it somewhere and didn't think out of the box.
So whenever there is no important reason why we should or must use ISNULL, I advice to use COALESCE instead.
See for example here a question about the differences if you want to know more: Question on SO

Avoid calling COUNT twice in CASE expression (PostgreSQL)

Inside a larger query, I have to COUNT a variable, then if it is larger than 1, have the count as a string otherwise an empty string:
CASE COUNT(measurement.id) > 1 THEN to_char(COUNT(measurement.id),' 999') ELSE ''
I'm afraid this is slow because I use COUNT() twice. Is there a better way?
This expression:
CASE COUNT(measurement.id) > 1 THEN to_char(COUNT(measurement.id),' 999') ELSE ''
is not slow because COUNT() is called twice. The hard work of aggregating the data is the part where the key values are brought together. The individual aggregation functions are generally not particularly expensive (there are exceptions such as COUNT(DISTINCT)). So, even if it were being called multiple times, this would not be an issue.
You could change the query to something more cryptic like:
coalesce(to_char(nullif(count(measurement.id), 0), '999')), '')
This takes a count of 0, converts it to NULL, which is then turned into an empty string (and I think it would only evaluate the argument once in Postgres, although SQL Server would evaluate it twice, in which case you use isnull() instead of coalesce()). I much prefer your version, if you feel the need to convert nice numbers into strings.
EDIT:
COUNT() appears to be defined as "immutable" which is even stronger than "stable". I'm not even sure if this is correct, but that is the case on SQL Fiddle. In any case, it probably isn't called twice, but the expensive part is the GROUP BY anyway.

Stored procedure using NVL() on input parameter - why?

Recently, I have come to analyze a procedure in which they have used below scenario.
I want to know what is the usefulness of this ?
A procedure (cwrkid, date)
select statement
WHERE CWRK.cwrkid = NVL(in_cwrk_id,CWRK.cwrkid)
and in_cwrk_id is passed null. SO obviously, CWRK.cwrkid = CWRK.cwrkid
will always match... What the point in using variables and passing null, and ultimately satisfying a truth condition.
Am I mising something or am I thinking a lot.. :P
This is useful if you want to make the procedure reusable in future development. For now the only usecase is to select all records, but if you ever need to get only one record with a given ID you can also use this procedure.
The point is that the caller can decide whether a filter on cwrkid should be applied. One call to that function may pass NULL for that parameter, to not apply any filter. Another call to that function may pass some value for that parameter, if that caller does want to apply a filter.
I say that no filter gets applied, but I am assuming that the column is not nullable. If the column is nullable, then nulls will be filtered out, regardless of what gets passed in as the parameter value.
Normally, code like this is used to have a default behaviour in case the parameter is NULL. In this case, the WHERE-condition normally restricts to records with the given cwrkid. However, if cwrkid is null, there is no restriction.
Without the NVL, the WHERE-condition would not match at all.
Why this was done in this case is impossible to know without knowing more about the procedure and its purpose.

Possible to spy/mock Sql Server User Defined Functions?

Is it possible to mock/spy functions with T-SQL? I couldn't find anything mentioning it. I was thinking of creating my own implementation using the SpyProcedure as a guideline (if no implementation exists). Anyone had any success with this?
Thanks.
In SQL Server functions cannot have side-effects. That means, in your test you can replace the inner function with on that returns a fixed result, but there is no way to record the parameters that were past into the function.
There is one exception: If the function returns a string and the string does not have to follow a specific format, you could concatenate the passed-in parameters and then assert later on that the value coming back out contained all the correct values, but that is a very special case and not generally possible.
To fake a function, just drop or rename the original and create your own within the test. I would put this code into a helper function, as it probably will be called from more than one test.

Calling a function from within a select statement - SQL

I have the following statement:
SELECT CASE WHEN (1 = 1) THEN 10 ELSE dbo.at_Test_Function(5) END AS Result
I just want to confirm that in this case the function wont be executed?
My reason for asking is that the function is particularly slow and if the critiria is true I want to avoid calling the function...
Cheers
Anthony
Your assumpion is correct - it won't be executed. I understand your concern, but the CASE construct is "smart" in that way - it doesn't evaluate any conditions after the first valid condition. Here's an example to prove it. If both branches of this case statement were to execute, you would get a "divide by zero" error:
SELECT CASE
WHEN 1=1 THEN 1
WHEN 2=2 THEN 1/0
END AS ProofOfConcept
Does this make sense?
Do not make this assumption, it is WRONG. The Query Optimizer is completely free to choose the evaluation order it pleases and SQL as a language does NOT offer operator short-circuit. Even if you may find in testing that the function is never evaluated, in production you may hit every now and then conditions that cause the server to choose a different execution plan and first evaluate the function, then the rest of the expression. A typical example would be when the server notices that the function return is deterministic and not depending on the row data, in which case it would first evaluate the function to get the value, and after that start scanning the table and evaluate the WHERE inclusion criteria using the function value determined beforehand.
Assuming you are doing some kind of testing... If you are trying to avoid the at_Test_Function, why not just comment it out and do
SELECT 10 AS Result
Put a WaitFor Delay '00:00:05' in the function. If the statement returns immediately it didn't execute if it takes 5 seconds to return then it was executed.