"недействительный месяц", хотя другие операции типа даты работают - PullRequest
1 голос
/ 10 июля 2020

Итак, я видел здесь несколько похожих вопросов, но ни одно из решений у меня не помогло. В представлении я генерирую поле типа даты, выполнив:

TO_DATE(DataOraModifica, 'YYYYMMDDhh24miss') 

Я запрашиваю это представление и хочу найти все записи, сделанные за последние 15 дней. но когда я пробую это:

select  dataoramodifica, sysdate-dataoramodifica as dife from myview 
where sysdate-dataoramodifica<15

, но я получаю эту ошибку: ORA-01843: недействительный месяц 01843. 00000 - «недействительный месяц»

Я читал другие сообщения, что это может быть связано с преобразованиями to_char и to_date, а также с форматом времени сеанса. Но я не использую никаких to_char, а только один to_date непосредственно в моей исходной строке

На самом деле, если я попробую другие операции, такие как:

select  dataoramodifica, sysdate-dataoramodifica as dife from myview
where rownum<10

, я получу результат, который делает смысл:

13-AUG-16 1426.998530092592592592592592592592592593

единственная проблема возникает, когда я пытаюсь использовать эти операции для условия WHERE. Я пробовал делать to_char и to_date, чтобы преобразовать все в тот же формат и обратно, но безрезультатно. Я хотел бы избежать изменения формата даты сеанса, поскольку для моего представления я использую некоторые хранимые процедуры, связанные с датой (и которые мне не разрешено изменять), и это рабочая система, поэтому я не хочу создавать проблемы.

Спасибо

---- Изменить ----

Кажется, я не обрабатываю все случаи, когда TO_DATE может завершиться неудачно при использовании:

length(F_Convert2NormalDate(dataoramodifica))=14 then 
TO_DATE(F_Convert2NormalDate(DataOraModifica), 'YYYYMMDDhh24miss') 
else TO_DATE('19000101000000', 'YYYYMMDDhh24miss') end
AS DATAORAMODIFICA

Чтобы увидеть, какие возможные значения я сделал:

  select distinct 
substr(f_convert2normaldate(dataoramodifica), 5,2) as mms, from mytable

и полученные мной значения были:

(null) 00 01 02 03 04 05 06 07 08 09 10 11 12

Думаю, мне нужно найти способ справиться со всеми возможными ошибками преобразования, потому что явно некоторые из них мне не хватает. Параметр default не работает, потому что я использую версию 11 ...

---- Изменить ---

Как большинство из вас предложили, был случай, когда преобразование не удавалось даже после моей проверки из 14 символов ('19000000000000'). После добавления этого в мой «случай ... тогда» он работает. Хотя это все еще кажется жестко запрограммированным решением, но я думаю, что на данный момент это придется сделать, поскольку версия, которую я использую, не позволяет обрабатывать общие c исключения (например, «по умолчанию ... при ошибке преобразования» ) и я думаю, что проблема должна быть решена на этапе генерации данных теми, кто за нее отвечает.

Спасибо всем за вашу помощь!

Ответы [ 2 ]

3 голосов
/ 10 июля 2020

хранимая процедура ... принимает строку, созданную системой, которая каким-то образом закодирована, и возвращает другую строку в формате ГГГГММДДччммсс, если ввод не дает действительной даты, он возвращает строку «00».

Если он действительно возвращает «00000000000000» (или , как выясняется, , «19000000000000»), то это вызовет эту ошибку:

select to_date('00000000000000', 'YYYYMMDDhh24miss') from dual;

ORA-01843: not a valid month

select to_date('19000000000000', 'YYYYMMDDhh24miss') from dual;

ORA-01843: not a valid month

Когда вы запрашиваете представление без условия, вы либо ограничиваете до 10 неопределенных строк с помощью where rownum<10, как вы показали, либо ваш клиент только извлекает и отображает «первые» несколько строк или блоков строк (например, 50 строк по умолчанию в SQL Разработчик). Процедура (которая на самом деле является функцией, предположительно ...) вызывается только для тех нескольких строк, которые не содержат проблемных c данных.

Когда у вас есть условие, каждая строка должна быть оценивается, поэтому вы сталкиваетесь с проблемой c one.

Вы можете избежать проблемы, изменив представление, чтобы сделать что-то вроде:

TO_DATE(
  case
    when DataOraModifica = '00' then null
    when DataOraModifica = '00000000000000' then null
    when DataOraModifica = '19000000000000' then null
    else DataOraModifica
  end, 
  'YYYYMMDDhh24miss'
)

db <> fiddle

Между прочим, я обычно записываю условие как

where dataoramodifica >= trunc(sysdate) - 15;

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

Глядя на проверку длины, которую вы уже выполняли, вы можете включить это как:

case when length(F_Convert2NormalDate(DataOraModifica)) != 14
       or F_Convert2NormalDate(DataOraModifica) = '00'
       or F_Convert2NormalDate(DataOraModifica) = '00000000000000'
       or F_Convert2NormalDate(DataOraModifica) = '19000000000000'
     then date '1900-01-01'
     else TO_DATE(DataOraModifica, 'YYYYMMDDhh24miss')
end

Или, если вы используете последнюю версию Oracle (12.2+), вы можете позволить to_date() обрабатывать любое состояние ошибки:

TO_DATE(DataOraModifica default '19000101000000' on conversion error,
  'YYYYMMDDhh24miss')

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

db <> fiddle

Чтобы определить значения, вызывающие проблему, вы можете сделать что-то вроде этого:

set serveroutput on
declare
  str varchar2(30);
  dt date;
begin
  for r in (select dataoramodifica from mytable) loop
    begin
      str := F_Convert2NormalDate(r.DataOraModifica);
      dt := case when length(str) = 14
                 then TO_DATE (str, 'YYYYMMDDhh24miss')
                 else TO_DATE('19000101000000', 'YYYYMMDDhh24miss')
            end;
    exception
      when others then
        dbms_output.put_line(r.DataOraModifica || ' -> ' || str || ' => ' || sqlerrm);
    end;
  end loop;
end;
/

Это попытается преобразовать каждое значение в таблице одно за другим; при обнаружении проблемы он сообщит об этом, но продолжит работу. Вы, конечно, можете добавить в отладку другие полезные данные, например значение первичного ключа строки.

2 голосов
/ 10 июля 2020

Я считаю, что проблема в ваших данных из вашей таблицы. Вы говорите, что есть столбец со строковыми значениями, и конвертируете эту строку в дату, а затем используете этот преобразованный столбец в предложении select и wheer.

Вот пример вашей возможной ситуации:

Если в вашей таблице есть хорошая строка, такая как '20200405222222', тогда ваш запрос будет работать:

select  to_date('20200405222222', 'YYYYMMDDhh24miss')
        , sysdate-to_date('20200405222222', 'YYYYMMDDhh24miss') as dife 
from dual;

Если строка похожа на '20201305222222', то ваш запрос не будет работать:

select  to_date('20201305222222', 'YYYYMMDDhh24miss')
        , sysdate-to_date('20201305222222', 'YYYYMMDDhh24miss') as dife 
from dual;

Вот небольшая демонстрация

Из моего примера видно, что я использовал 13 как числовую c строку для представления месяца, и это недействительный месяц ...

Вот еще лучший пример, где я создал представление и использовал два разных запроса:

https://dbfiddle.uk/?rdbms=oracle_18&fiddle=1f70f43aef1297044261ca813b8022bc

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...