строка литерала не соответствует строке формата

#sql #oracle

#sql #Oracle

Вопрос:

Я пытаюсь запустить sql-скрипт для тестовой базы данных. Я получаю сообщение об ошибке «строка литерала не соответствует строке формата» — странно то, что мой коллега запускает тот же запрос на том же тестовом сервере и не получает никаких ошибок, только когда я его запускаю, я получаю ошибки. Я полностью запутался в этом вопросе. Куда мне отсюда идти?

Я запускаю свой запрос с помощью командной строки SQLPlus. Я получаю сообщение об ошибке в первой строке: выберите NVL…..

   select NVL(to_char(add_months(to_date(max(mem.birth_date)),   780), 'yyyymmdd'), ' ')
    into local_turn_65_on
    from member mem
   where mem.member_id in (
         select cmr.member_id
         from case_member cmr
         where cmr.case_id = due_tasks_rec.case_id
           and cmr.current_row_ind = 'Y'
           and cmr.case_member_end_date = 29991231
           and cmr.case_member_role in ('AA','AB','AD','DP'))
     and mem.update_end_date = 29991231
     and mem.birth_date between
         (to_char(add_months(sysdate,-780), 'yyyymmdd'))
     and
         (to_char(add_months(sysdate,-780), 'yyyy') || '1231');
  

Если бы кто-нибудь мог указать мне правильное направление, я был бы очень признателен!

Комментарии:

1. Какой тип данных birth_date ? Если это a date , то замените: to_date(max(mem.birth_date)) на max(mem.birth_date)

2. это не создаст никакой разницы, если это дата, заменив на_date, хотя @a_horse_with_no_name

3. @casper1111 я предполагаю, что это процедура / функция / блок, можете ли вы также опубликовать какой-нибудь код над этой ошибкой?

4. тип данных — число (8)

5. @HimanshuAhuja: если это дата, то to_date() вызов должен быть удален , а не заменен. Вызов to_date() значения, которое уже является date , создаст подобные проблемы.

Ответ №1:

тип данных — число (8)

Затем вам нужно указать модель формата для преобразования этого числа в дату; вместо

 to_date(max(mem.birth_date))
  

вам нужно сделать:

 to_date(max(mem.birth_date), 'YYYYMMDD')
  

или даже более явно:

 to_date(to_char(max(mem.birth_date)), 'YYYYMMDD')
  

На данный момент ваш код полагается на настройки NLS вашего текущего сеанса для получения используемой модели формата, и ошибка означает, что она не соответствует передаваемому вами значению. Причина, по которой ваш коллега может успешно выполнить один и тот же запрос, заключается в том, что их сеанс имеет разные настройки NLS. Вот почему вы не должны полагаться на настройки NLS (или неявные преобразования — они часто идут рука об руку), поскольку у вас нет контроля над тем, как другие люди будут запускать ваш код.

Вы можете увидеть эффект на урезанном примере:

 alter session set nls_date_format = 'DD-Mon-RR';

with mem (birth_date) as (select 20190328 from dual)
select NVL(to_char(add_months(to_date(max(mem.birth_date)),   780), 'yyyymmdd'), ' ')
from mem;

ORA-01861: literal does not match format string
  

или даже проще:

 with mem (birth_date) as (select 20190328 from dual)
select to_date(max(mem.birth_date))
from mem;

ORA-01861: literal does not match format string
  

Добавление модели формата создает дату:

 with mem (birth_date) as (select 20190328 from dual)
select to_date(max(mem.birth_date), 'YYYYMMDD')
from mem;

TO_DATE(M
---------
28-Mar-19
  

и ваша настройка и преобразование теперь тоже работают:

 with mem (birth_date) as (select 20190328 from dual)
select NVL(to_char(add_months(to_date(max(mem.birth_date), 'YYYYMMDD'),   780), 'yyyymmdd'), ' ')
from mem;

NVL(TO_C
--------
20840328
  

Вы также должны хранить даты как даты, а не как числа, но это отдельная проблема…

Комментарии:

1. Я почти уверен, что casper1111 вернется и скажет, что он / она не проектировал базу данных — это был кто-то другой, кто решил, что сохранение даты в виде числа было бы разумным поступком. Те люди, которые занимаются подобными вещами, очевидно, никогда не были вынуждены жить со своим неправильным выбором.

2. Да, вот почему это отдельная проблема, и ответ не просто «не делай этого» — как бы мне (слишком часто) ни хотелось, чтобы это было * 😎

3. Спасибо всем за вашу помощь. @Alex Poole — спасибо! — путем добавления ‘yyyymmdd’ после (mem.birth_date) исправлена моя проблема! Теперь строка кода выглядит так: выберите NVL(to_char(add_months(to_date(max(mem.birth_date), ‘ггггмдд’), 780), ‘ггггмдд’), ‘ ») который работает!

4. @Alex Poole Это должно было работать и с to_date, просто сделать его доступным для чтения в том формате, в котором он получен, например, yyyymmdd, или использовать формат даты СУБД по умолчанию, который работает так же. Что вы скажете по этому поводу?

5. @HimanshuAhuja — это то, что я сделал — предоставил модель формата для существующего to_date() вызова. Установка модели формата по умолчанию (т. Е. сеанса) все равно будет зависеть от настроек NLS — даже если они установлены в том же скрипте, в котором выполняется запрос. Этого лучше вообще избегать.

Ответ №2:

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

select NVL(to_char(add_months(to_date(to_char(max(mem.birth_date))), 780), 'yyyymmdd'), ' ')

Комментарии:

1. Нет, birth_date это число, поэтому вы преобразуете max(mem.birth_date) из числа в строку, что уже происходило неявно; а затем преобразуете эту строку в дату — по-прежнему без предоставления надлежащей модели формата. Это будет работать, только если NLS_DATE_FORMAT установлено значение YYYYMMDD. Итак, сеанс коллеги настроен таким образом, но сеанс capser1111 настроен по-другому. (Итак, не полагайтесь на NLS … * 😎