Expanding on @PonderStibbon's explanation a bit, the documentation for the NVL()
function says:
The arguments expr1 and expr2 can have any data type. If their data types are different, then Oracle Database implicitly converts one to the other.
In your first query the null
isn't an identified data type, so the second argument can't be implicitly converted. In the second query the ''
is equivalent to null but is still a string expression, so the date is implicitly converted to a string too, as dump shows.
When you compare your DateField
column with the string value returned by nvl()
the string is implicitly converted back to a date.
All those implicit conversions mean that your NLS settings come into play. With a common NLS_DATE_FORMAT
setting like DD-MON-RR
no date ever matches your condition, because of how RR
is handled.
Consider:
select nvl('', to_date('01/01/1900', 'dd/mm/yyyy')),
nvl('', to_date('31/12/2999', 'dd/mm/yyyy'))
from dual;
NVL('',TO NVL('',TO
--------- ---------
01-JAN-00 31-DEC-99
Remember these are strings, not dates. What happens if you use them in a comparison against a real date?
select * from dual
where date '2017-01-01' between nvl('', to_date('01/01/1900', 'dd/mm/yyyy'))
and nvl('', to_date('31/12/2999', 'dd/mm/yyyy'));
which is really, because they evaluate to strings:
select * from dual
where date '2017-01-01' between '01-JAN-00' and '31-DEC-99';
but they those strings are implicitly converted back to dates:
select * from dual
where date '2017-01-01' between to_date('01-JAN-00') and to_date('31-DEC-99');
which uses the same NLS setting:
select * from dual
where date '2017-01-01' between to_date('01-JAN-00', 'DD-MON-RR')
and to_date('31-DEC-99', 'DD-MON-RR');
which still looks like it should work, on the surface. But what are those dates now, really? Using your initial date and allowing implicit conversion until the last moment, when I want to see what's really in there:
select to_char(to_date(to_char(to_date('01/01/1900', 'dd/mm/yyyy'))), 'SYYYY-MM-DD'),
to_char(to_date(to_char(to_date('31/12/2999', 'dd/mm/yyyy'))), 'SYYYY-MM-DD') from dual;
TO_CHAR(TO_ TO_CHAR(TO_
----------- -----------
2000-01-01 1999-12-31
The RR
rules mean that the two-digit year 00 is converted to 2000, while the two-digit year 99 is converted to 1999.
So your second query is actually doing:
select 1 from ordine where DateField between date '2000-01-01' and date '1999-12-31';
or
select 1 from ordine where date '2000-01-01' <= DateField
and DateField <= date '1999-12-31';
and those conditions cannot both be true at the same time, so no date ever matches. Or to put it another way:
If expr3 < expr2, then the interval is empty
With a different NLS_DATE_FORMAT
that preserved the full year your second query would also find the data you expect:
alter session set nls_date_format = 'DD-MON-RR';
select 1 from dual where sysdate between nvl('', to_date('01/01/1900', 'dd/mm/yyyy')) and nvl('', to_date('31/12/2999', 'dd/mm/yyyy'));
no rows selected
alter session set nls_date_format = 'SYYYY-MM-DD';
select 1 from dual where sysdate between nvl('', to_date('01/01/1900', 'dd/mm/yyyy')) and nvl('', to_date('31/12/2999', 'dd/mm/yyyy'));
1
----------
1
But don't rely on NLS settings or implicit conversions. Use null when you mean null, not an empty string, even if they are usually treated the same. (Oracle recommends that you do not treat empty strings the same as nulls.)