Oracle DB Ora-01839: дата недействительна для указанного месяца. 29-02-2016 високосный год

Все мы знаем, что сегодня особенный день. Это 29th of February 2016 високосного года.

Мы получаем сообщение об ошибке из некоторых таблиц в нашей БД Oracle. Ошибка: Oracle ORA-01839: date not valid for month specified.

Например, простой выбор, где возникает ошибка: select * from table where table_date > sysdate -0.1;

Для других таблиц этот выбор не представляет проблемы, только для некоторых таблиц.

Есть ли способ решить эту проблему? Потому что сегодня мы не можем использовать много столов.

Мы используем Oracle 12c.


person Patrick    schedule 29.02.2016    source источник
comment
Каков тип данных столбца table_date?   -  person Wernfried Domscheit    schedule 29.02.2016
comment
@WernfriedDomscheit это date   -  person Patrick    schedule 29.02.2016
comment
select table_date from... также получает эту ошибку - может быть, это другой столбец, который не работает при неявном преобразовании? Это таблица или представление (которое что-то вычисляет); или у него есть виртуальная колонка? Что такое NLS_DATE_FORMAT в сеансе, который получает ошибку? Видите ли вы это в конкретном клиенте и можете ли вы воспроизвести его в SQL*Plus?   -  person Alex Poole    schedule 29.02.2016
comment
Ну, вы потратили впустую ответ, который я в основном написал для объяснения обработки интервалов, но рад, что вы поняли это * 8-) Это задокументированное поведение.   -  person Alex Poole    schedule 29.02.2016


Ответы (1)


После интенсивных исследований стало ясно, почему некоторые из наших избранных сегодня не работают. Ошибка вызвана ключевым словом interval и это известная проблема. (Или так ANSI/ Спецификация ISO говорит, что это должно работать, внизу страницы 205/вверху страницы 206)

Вот цитата из блога сообщества Oracle:

Вопрос:

select to_date('2012-feb-29','yyyy-mon-dd') + interval '1' year as dt from dual;

ORA-01839: date not valid for month specified
01839. 00000 -  "date not valid for month specified"
*Cause:    
*Action:

select to_date('2012-feb-29','yyyy-mon-dd') + interval '2' year as dt from dual;

ORA-01839: date not valid for month specified
01839. 00000 -  "date not valid for month specified"
*Cause:    
*Action:

select to_date('2012-feb-29','yyyy-mon-dd') + interval '3' year as dt from dual;

ORA-01839: date not valid for month specified
01839. 00000 -  "date not valid for month specified"
*Cause:    
*Action:

select to_date('2012-feb-29','yyyy-mon-dd') + interval '4' year as dt from dual;

29-FEB-16 00:00:00


select to_date('2012-feb-29','yyyy-mon-dd') + interval '1' day as dt from dual;

01-MAR-12 00:00:00

select to_date('2012-feb-29','yyyy-mon-dd') + interval '1' month as dt from dual;

29-MAR-12 00:00:00

Ответ:

Именно так работают ИНТЕРВАЛЫ. Високосные годы - наименьшая из проблем; добавление 1 месяца к 31 марта приводит к той же ошибке. Если вы хотите убедиться, что результатом является допустимая ДАТА, используйте ADD_MONTHS. (Отдельной функции для добавления лет нет; используйте ADD_MONTH (SYSDATE, 12*n), чтобы получить ДАТУ через n лет.)


Почему это происходит в нашем случае:

В нашем случае мы использовали виртуальную частную базу данных для некоторых наших таблиц из соображений безопасности. И там мы применили ключевое слово interval в большинстве выборок.

Что делать вместо этого:

Вместо этого используйте ADD_MONTHS.

select add_months(to_date('2012-feb-29','yyyy-mon-dd'), 12) as dt from dual;
person Patrick    schedule 29.02.2016
comment
Просто примечание: для перехода на летнее время поведение также отличается. Сравните TIMESTAMP '2015-10-25 01:30:00 Europe/Berlin' + INTERVAL '1' MONTH и ADD_MONTHS(TIMESTAMP '2015-10-25 01:30:00 Europe/Berlin', 1) - person Wernfried Domscheit; 29.02.2016
comment
У нас по ошибке был код, похожий на следующий (что сегодня вызвало это исключение Oracle из-за того, что ODP.NET использовал INTERVAL с DbFunctions.AddYears): DataSet.Where(e => e.Date >= DbFunctions.AddYears(DateTime.Now, -1)) - person Trent; 01.03.2016
comment
Спасибо тебе за это. Интересно, какой смысл даже поддерживать дату + интервал, если вы не собираетесь поддерживать пересечение границ месяца и високосных лет... - person James Daily; 18.10.2017