RM-BLOG

IT系技術職のおっさんがIT技術とかライブとか日常とか雑多に語るブログです。* 本ブログに書かれている内容は個人の意見・感想であり、特定の組織に属するものではありません。/All opinions are my own.*

【Oracle】ADD_MONTHS関数について

あんまりoracleで日付の(特に「月」の)操作を個人的にすることがなかったのでよく知らなかったが、
add_monthsでちょっと困ったのでメモ


 

 
oracleのadd_monthsの「加算月」に1を与えると「1か月後の同日」が返って来るもんだと、あまり深く考えず信じていたのだが、
第一引数が月末日の場合、気を使ってくれるのかなんなのかしらないが「翌月の月末日」が返って来る。
つまり

select add_months(to_date('20160229','yyyyMMdd'),1) as YOKUGETU from dual


とした場合=つまり「2016/2/29の1ヶ月後は?」という指定をすると、結果は「2016/3/31になる。
でも

select add_months(to_date('20160228','yyyyMMdd'),1) as YOKUGETU from dual


とした場合=つまり「2016/2/28の1ヶ月後は?」という指定をすると、結果は「2016/3/28になる。

この例でいえば、要するに2016年2月の日付をもとに、「○○月後」の指定で「2016/3/29」「2016/3/30」を導けない。
加算月に1を渡したときは、基本的には「翌月の同日」を返す関数のようだが、
元の日付が月末だったときだけ「同日」って部分を無視して「翌月の月末」にする仕様のようだ。

こういうケースにおいて、「月末」の部分を無視して「翌月の同日」を取得するには、
「その月の最初の日からその日までの日数」を「次の月の最初の日」に足すとかして
愚直に計算するしかなさそうだ。

select
    a.KIJUNDATE
  , add_months(a.KIJUNDATE , 1) as ADD_MONTHS_DATE
  , add_months(last_day(a.KIJUNDATE) + 1 ,  -1) as KIJUNDATE_FIRST_DAY
  , last_day(a.KIJUNDATE) + to_number(to_char(a.KIJUNDATE,'dd')) as YOKGETU_DOUJITSU
from (select to_date(:kijundatestr,'yyyyMMdd') as kijundate
            ,:kijundatestr as KIJUNDATESTR
      from dual
     )  a


↑のSQLのバインド変数「:kijundatestr」に「20160229」を与えると、
SQL実行結果の「YOKGETU_DOUJITSU」は「20160329」になる。

一方で、「20160531」を与えると、「YOKGETU_DOUJITSU」は「20160701」になる。
これは2016/5/31の翌月同日2016/6/31が存在しないため、
2016/6/1+31日にして2016/7/1を導出しているためである。
このケースでは「翌月同日」という表現がふさわしいとは到底思えず、
そうなるとadd_monthsのほうがまだ使いやすいと感じる。
要は使い方次第ということだろうか。

いずれにせよ12ヶ月はそれぞれ日数が異なるため、
これら「月」の考え方に無理やり「同日」という概念を当てはめようとするとおかしなことになる。
単純に日単位で物差しを考えたほうがシンプルに片付くのは事実だろう。
全部の月が同じ日数ならよかったのにね。
面倒くさいね。