Просмотр вашего кода (с номерами строк)
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;
В строке 5 : '27-APR-18'
- это текстовый литерал (не тип данных DATE
), и при попытке присвоить его переменному DATE
Oracle попытается быть полезным и неявным приведите его к DATE
, используя функцию TO_DATE( date_string, format_model )
. Поскольку это неявное приведение, Oracle будет использовать свою модель формата по умолчанию, которая является параметром сеанса пользователя NLS_DATE_FORMAT
.
Вы можете увидеть формат параметра сеанса NLS_DATE_FORMAT
, используя запрос:
SELECT VALUE
FROM NLS_SESSION_PARAMETERS
WHERE PARAMETER = 'NLS_DATE_FORMAT';
И строка 5 эквивалентна:
var2 := TO_DATE(
'27-APR-18',
( SELECT VALUE
FROM NLS_SESSION_PARAMETERS
WHERE PARAMETER = 'NLS_DATE_FORMAT' )
);
Если модель формата по умолчанию соответствует строке, то это неявное приведение будет работать - в противном случае ваш код вызовет исключение.
Другая возможная проблема заключается в том, что NLS_DATE_FORMAT
равен DD-MON-YYYY
(или эквивалентному), а затем 27-APR-18
будет приведен к дате 0018-04-27 00:00:00
и будет иметь (вероятно) неожиданный и неправильный век!
(Примечание: NLS_DATE_FORMAT
- это параметр сеанса, поэтому каждый пользователь может установить свое собственное значение, поэтому НИКОГДА не следует полагаться на неявное приведение, в противном случае вы обнаружите, что тот же код, который работает для самостоятельно происходит сбой других пользователей или даже может произойти сбой одного и того же пользователя в разных сеансах - без изменения кода.)
В строке 5 вы должны либо использовать литерал DATE
, либо явно преобразовать текстовый литерал, используя функцию TO_DATE
с моделью формата:
var2 := DATE '2018-04-27';
var2 := TO_DATE( '27-APR-18', 'DD-MON-RR' );
В строке 8 : Вы сравниваете две даты; однако тип данных DATE
всегда имеет компонент времени, и если вы не запустите код в 2018-04-27T00: 00: 00, тогда SYSDATE
будет иметь «ненулевой» компонент времени, тогда как var2
не имел явно указанный компонент времени, поэтому неявное приведение даст ему компонент времени 00:00:00
(полночь). Это означает, что, за исключением ровно полуночи, эти два никогда не будут равны, и сравнение вернет FALSE
.
Если вы хотите обнулить временные компоненты DATE
при сравнении двух дат, используйте функцию TRUNC()
или укажите диапазон, например:
IF TRUNC( var1 ) = var2 THEN
IF var2 <= var1 AND var1 < var2 + INTERVAL '1' DAY THEN
В строке 12 : Это обратная строка 5 - DBMS_OUTPUT.PUT_LINE( string )
принимает строковый аргумент - не DATE
- поэтому Oracle неявно попытается привести DATE
к строке (используя NLS_DATE_FORMAT
снова). Таким образом, строка 12 является эффективной;
DBMS_OUTPUT.PUT_LINE(
TO_CHAR(
SYSDATE,
( SELECT VALUE
FROM NLS_SESSION_PARAMETERS
WHERE PARAMETER = 'NLS_DATE_FORMAT' )
)
)
Поскольку ваш NLS_DATE_FORMAT
равен DD-MON-RR
, он не отображает компоненты времени и не показывает, почему переменные отличаются.
Если вы делаете:
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;
/
Тогда вывод будет:
2018-04-27 10:28:59
2018-04-27 00:00:00
и вы можете увидеть разницу.