Oracle DB to_date с годом - to_date (2017, 'ГГГГ') неожиданный возврат

При написании нескольких запросов мне нужно было вернуть только те строки, в которых столбец даты установлен в этом году (2017), это не моя проблема. Я знаю, как написать этот запрос несколькими разными способами, но я наткнулся на что-то странное и неожиданное для меня . Кто-нибудь может объяснить, почему Oracle db 11.2 ведет себя таким образом?

select sysdate from dual

returns: 
2017/12/05 09:22:27

select to_date(2017,'YYYY'),trunc(sysdate,'YYYY') from dual
returns :
2017/12/01 00:00:00 2017/01/01 00:00:00

select to_date(2017,'YYYY'),trunc(sysdate,'YYYY') from dual
where trunc(sysdate,'YYYY') = to_date(2017,'YYYY')

no rows returned

Почему to_date(2017,'YYYY') возвращает 2017/12/01, вернет ли он 2017/01/01 в следующем месяце? Почему это работает именно так? Я бы ожидал, что он всегда будет возвращать 2017/01/01 независимо от текущего месяца (если часть месяца действительно меняется в зависимости от sysdate).


person Jakub Dobruchowski    schedule 05.12.2017    source источник
comment
Почему такое поведение очевидно? Я бы ожидал, что функция будет вести себя одинаково, независимо от того, какие параметры я ее поставил, и здесь, согласно ответу MT0, для года и месяца функция возвращает текущий, а для остальных возвращает 1-й возможный   -  person Jakub Dobruchowski    schedule 05.12.2017
comment
@JakubDobruchowski Я предполагаю, что причина, по которой он не использует текущий день, заключается в том, что функция всегда возвращает действительную дату. Представьте, что если бы сегодня было 31 декабря, и вы сделали TO_DATE( '02', 'MM' ), и он использовал текущий день месяца, то оператор выдал бы ошибку, поскольку в феврале нет 31 дня. Я никогда не видел обоснования того, почему используется текущий месяц (а не первый месяц), но я предполагаю, что существует значительный вариант использования для людей, которые хотят, чтобы даты были в текущем месяце определенного года, и это поведение по умолчанию допускает это .   -  person MT0    schedule 05.12.2017
comment
Хорошая точка. Я, конечно, вижу сильный вариант использования в том, чтобы запускать что-то одно в месяц, просто написав to_date(5,'DD')   -  person Jakub Dobruchowski    schedule 05.12.2017
comment
Почему бы просто не сравнить год, а не полную дату: where extract(year from some_date) = extract(year from sysdate)   -  person a_horse_with_no_name    schedule 05.12.2017


Ответы (1)


В Oracle TO_DATE предполагает что:

  • Если вы не укажете год, то это текущий год;
  • Если вы не укажете месяц, то это текущий месяц;
  • Если вы не укажете день, то это первый день месяца;
  • Если вы не укажете часы, то это полночный час (0);
  • Если вы не укажете минуты, то это будет 0 минут после часа; и
  • Если вы не укажете секунды, то это 0 секунд в минуте.

Вы указываете только год (2017), так что это будет:

  • Ноль минут и секунд после полуночи первого дня текущего месяца указанного вами года (2017).

Если вам нужен первый день года, укажите месяц (и желательно остальную часть даты):

select to_date( '201701','YYYYMM'),
       trunc(sysdate,'YYYY')
from   dual
where  trunc(sysdate,'YYYY') = to_date( '201701','YYYYMM' )

Или используйте литерал даты:

select DATE '2017-01-01',
       trunc(sysdate,'YYYY')
from   dual
where  trunc(sysdate,'YYYY') = DATE '2017-01-01'
person MT0    schedule 05.12.2017
comment
Хм, странно, я прочитал пару сайтов, посвященных функции to_date Oracle, и ни один из них не предоставил информацию о том, что будет предполагать to_date. Даже не документы. docs.oracle.com/cd/B19306_01/server.102/ b14200/functions183.htm - person Jakub Dobruchowski; 05.12.2017
comment
@JakubDobruchowski Это недокументированный особенность TO_DATE(). Я тоже просмотрел официальную документацию в поисках ссылки (и не нашел ее), а затем мне пришлось довольно усердно искать вторичную ссылку, чтобы подтвердить мой опыт того, как это всегда работало в нескольких версиях Oracle. - person MT0; 05.12.2017