Oracle сравнивает день со вторым интервалом с целым числом - PullRequest
0 голосов
/ 25 октября 2018

Мне интересно, что происходит под капюшоном оракула, когда я пытаюсь сравнить дневной и второй интервал с целым числом.

Пример ниже.

SET SERVEROUTPUT ON;
DECLARE
    v_date1 TIMESTAMP := current_timestamp ;
    v_date2 TIMESTAMP := current_timestamp - 150;
BEGIN
    -- Wrong way. But what is happening here?
    IF v_date1 - v_date2 < 2 THEN
        DBMS_OUTPUT.PUT_LINE('YES - why?');
    ELSE
        DBMS_OUTPUT.PUT_LINE('NO');
    END IF;
    -- Correct way
    IF v_date1 < v_date2 + 2 THEN
        DBMS_OUTPUT.PUT_LINE('YES');
    ELSE
        DBMS_OUTPUT.PUT_LINE('NO - works as expected');
    END IF;
    -- Another correct way
    IF v_date1 - v_date2 < INTERVAL '2' DAY THEN
        DBMS_OUTPUT.PUT_LINE('YES');
    ELSE
        DBMS_OUTPUT.PUT_LINE('NO - works as expected as well');
    END IF;
END;

Может кто-нибудь объяснить мне, почему первый IF оценивается как true?

1 Ответ

0 голосов
/ 25 октября 2018

То, что вы видите, похоже на ошибку или, по крайней мере, недокументированное поведение.Поэтому, к сожалению, я думаю, что вам нужно подать запрос на обслуживание в Oracle, чтобы получить объяснение.Только они могут заглянуть под капот и посмотреть, делает ли он то, что они ожидают, или это то, о чем они не думали.


Разбивая некоторые шаги, не только для вашего конкретного вопроса:

v_date1 TIMESTAMP := current_timestamp ;

приводит current_timestamp, то есть время в вашем часовом поясе сеанса, к простой метке времени - потеря информации о часовом поясе.

v_date2 TIMESTAMP := current_timestamp - 150;

состоит из двух шагов;current_timestamp - 150 дает вам дату, теряя дробные секунды и информацию о часовом поясе, а затем возвращает ее обратно к отметке времени.Было бы лучше использовать - interval '150' day, но я понимаю, что вы просто настраиваете здесь фиктивные данные.

IF v_date1 - v_date2 < 2 THEN

также состоит из нескольких этапов: v_date1 - v_date2 дает вам интервал, например, +150 00:00:00.975444,(Обратите внимание, что это не совсем 150 дней, отчасти из-за малого времени, прошедшего между двумя вызовами current_dtimestamp, но намного больше, чем можно было ожидать от этой разницы - из-за потери точности из-за более раннего отскока черезтип данных date.)

Тогда сравнение само по себе становится интересным.Похоже, что происходит неявное преобразование данных, но вы пытаетесь сравнить интервал с числом, и не существует неявного преобразования, которое позволяет это;и в простом SQL, пытающемся сделать те же самые ошибки сравнения:

select case when current_timestamp - (current_timestamp - 150) < 2 then 'yes' else 'no' end from dual;

ORA-00932: inconsistent datatypes: expected INTERVAL DAY TO SECOND got NUMBER

Таким образом, кажется, что PL / SQL выполняет какое-то другое неявное преобразование, но это не очевидно, что, и, похоже, это не так.задокументировано или упомянуто в МОС.Похоже, это не сравнение строк, или необработанное, или преобразование этих 2 в тип интервала, или преобразование интервала в число, или интервал, или временная метка в расчете по датам ...

Кажется, что нет никакого числового значения, которое заставляет это сравнение возвращать false - независимо от того, является ли левая часть вычислением (которое может быть переписано во что-то более похожее на вашу вторую версию) или переменной с фиксированным интервалом.Это больше напоминает ошибку, чем преднамеренную.

И последнее из вашего исходного кода:

-- Correct way
IF v_date1 < v_date2 + 2 THEN

Это тоже не совсем правильно, хотя может показаться придирчивым;v_date + 2 дает вам дату, а не временную метку, поэтому, если вы еще не потеряли точность долей секунды ранее (с - 150), то вы бы сделали это в этот момент.И теперь вы сравниваете временную метку с датой, что подразумевает более неявное преобразование.Вы можете изменить числовой 2 на 2-дневный интервал, но это будет логически так же, как ваше окончательное (правильное) сравнение.

...