I'm not sure this is quite a duplicate; although the linked question covers similar ground and the advice to compare strings to strings and dates to dates is certainly valid, you're already doing that, and I'm not sure it will necessarily help you understand what you're seeing here.
The key is to look at what the actual string values are when you do the string comparisons. I've assumed your NLS_DATE_FORMAT is DD/Mon/YYYY
as that's the way you're writing fixed dates, but it would work with the default DD-MON-RR
as well. I'm also ignoring potential complications from NLS_SORT and other settings.
select to_char(sysdate), to_char('01/Jan/2013'), to_char('01/Jan/2020') from dual;
TO_CHAR(SYSDATE) TO_CHAR('01/JAN/2013') TO_CHAR('01/JAN/2020')
-------------------- ---------------------- ----------------------
11/Dec/2013 01/Jan/2013 01/Jan/2020
Firstly, the to_char()
of the fixed strings doesn't do anything, you're converting a string to a string. But think about what happens when you plug those values into your where
clause;
select decode(count(*), 1, 'Yes', 'No') from dual
where to_char(sysdate) between to_char('01/Jan/2013') and to_char('01/Jan/2020');
becomes
select decode(count(*), 1, 'Yes', 'No') from dual
where '11/Dec/2013' between '01/Jan/2013' and '01/Jan/2020';
This is purely a string comparison; although they still represent dates to you, to the SQL engine they are just strings. The comparison fails on the first character - the first 1
in 11/Dec/2013
is compared with the 0
in the two other strings, and '1'
is not between '0'
and '0'
. (Simplifying a bit).
When you compare against the 10th,
select decode(count(*), 1, 'Yes', 'No') from dual
where to_char(sysdate) between to_char('01/Jan/2013') and to_char('10/Jan/2020');
becomes:
select decode(count(*), 1, 'Yes', 'No') from dual
where '11/Dec/2013' between '01/Jan/2013' and '10/Jan/2020';
which fails because '11'
is not between '01'
and '10'
.
In your last query,
select decode(count(*), 1, 'Yes', 'No') from dual
where to_char(sysdate) between to_char('01/Jan/2013') and to_char('11/Jan/2020');
becomes:
select decode(count(*), 1, 'Yes', 'No') from dual
where '11/Dec/2013' between '01/Jan/2013' and '11/Jan/2020';
and (simplifying a bit again) the comparison succeeds because '11/D'
is, as a string, between '01/J'
and '11/J'
.
Using a different format mask would allow this to work consistently, but the only safe and reliable way to to this is to compare everything as dates, and to explicitly state the date format model in the to_date()
calls for your fixed values:
select decode(count(*), 1, 'Yes', 'No') from dual
where sysdate between to_date('01/Jan/2013', 'DD/Mon/YYYY')
and to_date('01/Jan/2020', 'DD/Mon/YYYY');
And don't do to_date(sysdate)
; as the linked question mentioned that does an implicit conversion to date to string, which is pointless at best, but can introduce problems if a format model is specified. If you're trying to remove the time portion of the current date you can use trunc(sysdate)
rather than converting to a string and back.