Недопустимый месяц в оракуле, когда используется add_months

Я дал приведенный ниже код в запросе.

    where to_date(cal_wid,'yyyymmdd') 
    between  add_months(to_date(sysdate, 'MM-YYYY'),-12) 
    and      to_date(sysdate, 'MM-YYYY')

Я получаю следующую ошибку. (я делаю на сервере Xampp)

   Warning: oci_execute(): ORA-01843: not a valid month in C:\xampp\htdocs\xxx\index.php on line 149

   Warning: oci_fetch_array(): ORA-24374: define not done before fetch or execute and fetch in C:\xampp\htdocs\xxx\index.php on line 160

Может ли кто-нибудь сказать, почему я получаю эту ошибку?


person rji rji    schedule 19.03.2015    source источник


Ответы (2)


Никогда, никогда не используйте TO_DATE() на чем-то, что уже является DATE. Причина этого в том, что Oracle придется выполнять некоторые неявные преобразования, чтобы следовать вашим пожеланиям:

TO_DATE(sysdate, 'mm-yyyy')

действительно работает как

TO_DATE(TO_CHAR(sysdate, '<default nls_date_format parameter>'), 'mm-yyyy')

поэтому, если для вашего nls_date_format установлено значение, отличное от «мм-гггг», у вас возникнут проблемы. Параметр nls_date_format по умолчанию — «DD-MON-YY», что, скорее всего, соответствует вашему значению.

Если все, что вы хотели сделать, это добавить_months к 1-му числу текущего месяца, тогда вам следует использовать TRUNC(), например:

add_months(trunc(sysdate, 'MM'),-12)


Вот доказательство неявного to_char, если вы to_date что-то, что уже является датой, по запросу Лалита - план выполнения базового запроса, включающего to_date(sysdate):

SQL_ID  3vs3gzyx2gtcn, child number 0
-------------------------------------
select *  from   dual where  to_date(sysdate) < sysdate

Plan hash value: 3752461848

----------------------------------------------------------------------------
| Id  | Operation          | Name | E-Rows |E-Bytes| Cost (%CPU)| E-Time   |
----------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |        |       |     2 (100)|          |
|*  1 |  FILTER            |      |        |       |            |          |
|   2 |   TABLE ACCESS FULL| DUAL |      1 |     2 |     2   (0)| 00:00:01 |
----------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter(TO_DATE(TO_CHAR(SYSDATE@!))<SYSDATE@!)

Вы можете ясно видеть TO_CHAR() в условии фильтра.

person Boneist    schedule 19.03.2015
comment
Эй, я помню, как однажды я проследил сеанс и обнаружил, что в коде есть to{date на sysdate, однако я не нашел неявного преобразования в to_char перед применением to_date. Я хотел бы предоставить тестовый пример сейчас, но не могли бы вы попытаться добавить некоторые доказательства. Я бы тоже попробовал, когда получу доступ к базе данных. - person Lalit Kumar B; 19.03.2015
comment
Доказательство добавлено по вашему запросу. - person Boneist; 19.03.2015
comment
Хорошо, спасибо. Хотел бы я снова проголосовать (я уже сделал это однажды) ;-) - person Lalit Kumar B; 19.03.2015
comment
Я добавил ПРИМЕЧАНИЕ в свой ответ в соответствии с вашим ответом. - person Lalit Kumar B; 19.03.2015

Ошибка здесь:

to_date(sysdate, 'ММ-ГГГГ')

Давайте посмотрим, почему:

SQL> select to_date(sysdate, 'MM-YYYY') from dual;
select to_date(sysdate, 'MM-YYYY') from dual
               *
ERROR at line 1:
ORA-01843: not a valid month


SQL>

Вам не нужно снова преобразовывать sysdate в дату. Удалите to_date и просто используйте SYSDATE.

ПРИМЕЧАНИЕ В качестве дополнительной информации см. ответ @Boneist, чтобы получить представление о неявном преобразовании что Oracle вынужден делать при применении to_date на SYSDATE.

Вы используете to_date для преобразования строки в дату. Для расчета даты вам просто нужно использовать значение даты.

Например,

SQL> WITH DATA AS(
  2  SELECT '20150101' cal_wid FROM DUAL
  3  )
  4  SELECT *
  5  FROM DATA
  6  WHERE   to_date(cal_wid,'yyyymmdd')
  7  BETWEEN add_months(SYSDATE,-12)
  8  AND     SYSDATE
  9  /

CAL_WID
--------
20150101

SQL>

Измените свой запрос как:

WHERE to_date(cal_wid,'yyyymmdd') BETWEEN add_months(SYSDATE, -12) AND SYSDATE
person Lalit Kumar B    schedule 19.03.2015