I'm wondering how come SYSDATE
is different from, let's say 28-APR-18
(assuming that SYSDATE
is April, 28 of 2018).
I was debugging a little script I made, and got plenty of errors. After a while I managed to narrow it down to how the tables were filled (they were filled using the literal ' DD-MMM-YYYY
' method and I was comparing against SYSDATE
).
To understand, I wrote the following to see how each compares:
declare var1 DATE; var2 DATE;
BEGIN
var1 := SYSDATE;
var2 := '27-APR-18';
if var1 = var2 then
DBMS_OUTPUT.PUT_LINE('oh yeah');
else DBMS_OUTPUT.PUT_LINE('WTF?');
DBMS_OUTPUT.PUT_LINE(SYSDATE);DBMS_OUTPUT.PUT_LINE('27-APR-18');
end if;
END;
If the above is ran, I get the following (which is getting me puzzled):
WTF?
27-APR-18
27-APR-18
PL/SQL procedure successfully completed.
Since they are both declared as DATE type, shouldn't they both be equal?
Thanks for your time and help!
In Oracle, a DATE
value - despite the name - contains a time part as well. SYSDATE
contains the current date and the current time (up to seconds).
The Oracle tools by default (stupidly) hide the time part of a DATE
value. If you run:
select to_char(sysdate, 'yyyy-mm-dd hh24:mi:ss') as sysdate
from dual;
you can see that.
So SYSDATE
might be 2018-04-27 09:15:42
whereas the string (!) constant '27-APR-18'
is silently converted to a DATE
value at midnight: 2017-04-28 00:00:00
More details in the chapter Basic Elements of Oracle SQL in the manual
If you don't care about the time part, you can use trunc()
to set the time to midnight, trunc(sysdate)
yields 2018-04-27 00:00:00
(if today is 2018-04-27
). Note that trunc()
does not "remove" the time, it only sets it to 00:00:00
Unrelated, but:
You should never rely on implicit casting between strings and other non-character types which var2 := '27-APR-18'
does - it would eg fail on my computer as my default NLS date format is different.
If you need a DATE
value, then specify a proper date literal:
var2 := DATE '2018-04-27';
or
var2 := to_date('27-APR-18', 'dd-mon-rr');
or
var2 := to_date('27-APR-18 00:00:00', 'dd-mon-rr hh24:mi:ss');
Apart from what @horse has explained, you may use TRUNC
on sysdate
for var1 and date literal on var2
in YYYY-MM-DD
format, it would match.
declare
var1 DATE;
var2 DATE;
BEGIN
var1 := TRUNC(SYSDATE);
var2 := DATE '2018-04-27';
if var1 = var2 then
DBMS_OUTPUT.PUT_LINE('oh yeah');
else
DBMS_OUTPUT.PUT_LINE('WTF?');
DBMS_OUTPUT.PUT_LINE(SYSDATE ||','||'27-APR-18');
end if;
END;
oh yeah
Looking at your code (with line numbers)
1 DECLARE
2 var1 DATE;
3 var2 DATE;
4 BEGIN
5 var1 := SYSDATE;
6 var2 := '27-APR-18';
7
8 if var1 = var2 then
9 DBMS_OUTPUT.PUT_LINE('oh yeah');
10 END
11 DBMS_OUTPUT.PUT_LINE('WTF?');
12 DBMS_OUTPUT.PUT_LINE(SYSDATE);
13 DBMS_OUTPUT.PUT_LINE('27-APR-18');
14 END IF;
15 END;
In line 5 : '27-APR-18'
is a text literal (not a DATE
data type) and when you try to assign it to a DATE
varaible Oracle will try to be helpful and implicitly cast it to a DATE
using the TO_DATE( date_string, format_model )
function. Since this is an implicit cast then Oracle will use its default format model which is the user's NLS_DATE_FORMAT
session parameter.
You can see the format of the NLS_DATE_FORMAT
session parameter using the query:
SELECT VALUE
FROM NLS_SESSION_PARAMETERS
WHERE PARAMETER = 'NLS_DATE_FORMAT';
And line 5 is the equivalent of:
var2 := TO_DATE(
'27-APR-18',
( SELECT VALUE
FROM NLS_SESSION_PARAMETERS
WHERE PARAMETER = 'NLS_DATE_FORMAT' )
);
If the default format model matches the string then this implicit cast will work - otherwise your code will raise an exception.
The other possible problem is where the NLS_DATE_FORMAT
is DD-MON-YYYY
(or an equivalent) and then 27-APR-18
will be cast to the date 0018-04-27 00:00:00
and have a (probably) unexpected and wrong century!
(Note: NLS_DATE_FORMAT
is a session parameter so each user can set their own value so you should NEVER rely on implicit casts otherwise you will find that the same code which is working for yourself fails for other users or can even fail for the same user in different sessions - without your code ever changing.)
What you should do in line 5 is either use a DATE
literal or explicitly cast the text literal using the TO_DATE
function with a format model:
var2 := DATE '2018-04-27';
var2 := TO_DATE( '27-APR-18', 'DD-MON-RR' );
In line 8 : You are comparing two date; however, the DATE
data type always has a time component and unless you run the code at 2018-04-27T00:00:00 then SYSDATE
will have a "non-zero" time component whereas var2
did not have an explicitly specified time component so the implicit cast will have given it a time component of 00:00:00
(midnight). This means that, except for at exactly midnight, the two will never be equal and the comparison will return FALSE
.
If you want to zero the time components of a DATE
when you are comparing two dates then use the TRUNC()
function or specify a range, like these:
IF TRUNC( var1 ) = var2 THEN
IF var2 <= var1 AND var1 < var2 + INTERVAL '1' DAY THEN
In line 12 : This is the reverse of line 5 - DBMS_OUTPUT.PUT_LINE( string )
takes a string argument - not a DATE
- so Oracle will implicitly try cast the DATE
to a string (using the NLS_DATE_FORMAT
again). So line 12 is effectively;
DBMS_OUTPUT.PUT_LINE(
TO_CHAR(
SYSDATE,
( SELECT VALUE
FROM NLS_SESSION_PARAMETERS
WHERE PARAMETER = 'NLS_DATE_FORMAT' )
)
)
Since your NLS_DATE_FORMAT
is DD-MON-RR
this is not displaying the time components and is not showing you why the variables are different.
If you do:
ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS';
DECLARE
var1 DATE;
var2 DATE;
BEGIN
var1 := SYSDATE;
var2 := DATE '2018-04-27';
-- or var2 := TO_DATE( '2018-04-27 00:00:00', 'YYYY-MM-DD HH24:MI:SS' );
-- or var2 := TIMESTAMP '2018-04-27 00:00:00';
if var1 = var2 then
DBMS_OUTPUT.PUT_LINE('oh yeah');
END
DBMS_OUTPUT.PUT_LINE('WTF?');
DBMS_OUTPUT.PUT_LINE( var1 );
-- or DBMS_OUTPUT.PUT_LINE( TO_CHAR( var1, 'YYYY-MM-DD HH24:MI:SS' ) );
DBMS_OUTPUT.PUT_LINE( var2 );
-- or DBMS_OUTPUT.PUT_LINE( TO_CHAR( var2, 'YYYY-MM-DD HH24:MI:SS' ) );
END IF;
END;
/
Then the output will be:
2018-04-27 10:28:59
2018-04-27 00:00:00
and you can see the difference.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.