連載
» 2005年03月26日 00時00分 公開

SQLクリニック(2):日付データ演算の達人技を伝授する 【第1話】 (2/3)

[山下正,株式会社インサイトテクノロジー]

特定日の取得

 では、ここから、簡単そうなのにやり方が分からない(あるいは、思い出せない)ような日付の取得について見ていきましょう。

 まずは、これです。

【特定の月の最終日を取得する(LAST_DAY)】

  • LAST_DAY(SYSDATE)
  • LAST_DAY(SYSTIMESTAMP) → DATE型になるけど、問題ないですよね。

 これは月次処理などに関連して使われることがありますが、使う機会がない開発者にとっては縁遠い関数の1つです。使い方は簡単で、取得したい月の日付を引数に渡せば、それでOKです。

 うるう年も判断してくれるので、例えば2月の最終日も問題なく取得できます。

 では、月初めはどうやって取得しましょうか? FIRST_DAYなんていう関数はあったでしょうか? いえいえ冗談です!

 では代わりに、週の初日を取得してみましょう。

【週の初日を取得する(TRUNC)】

 よくスケジュールの確認をする際に、「○○の週」という表現をします。つまり週の先頭の日を指しているのですが、これをOracleで取得する場合はどうすればよいでしょうか?

 この場合はTRUNC関数を使用します。数値を丸めたりする際に使用する関数ですが、実は日付データにも使うことができます。

 では、今週の週初めの日を取得してみます。

SQL> !cal
      2月 2005
日 月 火 水 木 金 土
       1  2  3  4  5
 6  7  8  9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28
リスト2 カレンダーの表示

 今日は2月16日です。

SQL> select SYSDATE from dual;
SYSDATE
--------
05-02-16
リスト3 今日の日付を確認

 TRUNC関数の第2引数に'day'を利用すると……。

SQL> select TRUNC(SYSDATE,'day') from dual;
TRUNC(SY
--------
05-02-13
リスト4 今週の週初めの日を取得

 という具合に、簡単に週初めの日を取得することができます。もちろんSYSDATE関数の代わりに、SYSTIMESTAMP関数を使用することもできます。ただし、DATE型に変換されることは覚えておく必要があります。

 日付データでTRUNC関数を使用する場合、第2引数には'day'のほかにもいくつか指定できます。マニュアルで確認してみてください。

 TRUNC関数を使用したサンプルSQLを1つ挙げておきます。お題は「今月の第n○曜日を取得する」です。

select decode(TRUNC(get_date,'mm'),TRUNC(SYSDATE,'mm'),get_date,null)
from (
select decode(TRUNC(TRUNC(TRUNC(SYSDATE,'mm'),'day') + :youbi -1,'mm')
             ,TRUNC(SYSDATE,'mm')
             ,TRUNC(TRUNC(SYSDATE,'mm'),'day') + :youbi -1
             ,TRUNC(TRUNC(SYSDATE,'mm'),'day') + :youbi -1 + 7)
      + (:nambanme - 1) * 7 get_date
from dual)
リスト5 今月の第n○曜日を取得する

 youbi変数に日曜日(1)から土曜日(7)までに相当する数字を設定します。また、nambanme変数に何番(週)目の曜日を取得するかを設定します。あえてSQLで記述しましたが、ここまでするなら普通はFUNCTIONにするでしょうね。

【曜日の処理はNEXT_DAY関数で】

 そういえば、Oracleの関数に曜日を扱える関数があったような……そうそう、NEXT_DAY関数です。NEXT_DAY関数は“指定した日”の次の“指定した曜日”に相当する日を取得してくれる関数です。上記のSQLも曜日を扱っているので、実はNEXT_DAY関数を使用するとシンプルになります。

select decode(TRUNC(get_date,'mm'),TRUNC(SYSDATE,'mm'),get_date,null)
from (
select NEXT_DAY(TRUNC(SYSDATE,'mm')-1,
                :youbi) + 7 *( :nambanme -1 ) get_date
from dual)
リスト6 今月の第n○曜日を取得する(NEXT_DAYバージョン)

 外側に対象月を外れた場合のハンドリング用にDECODE処理が書いてあるだけで、実際に日付を取得する部分はたった1行だけになりました。ちなみに、NEXT_DAY関数の第2引数には、曜日をそのまま指定します。例えば、'月曜日'や、'MON'といった具合です。

 関数をうまく使うと、同じ結果を返すSQLを、よりシンプルに書き直すことができます。そのためにはとにかくいろいろな関数に触れておく必要があります。知らないと使おうとは思わないですからね。引き出しは多いに越したことはありません。ではもう少し行ってみましょう。

【日付時刻の四捨五入(ROUND)】

 次のような場合はどうすればよいでしょうか。例えば、××時以降に入力された処理は、翌日扱いとしたい。

 これには四捨五入を使います。もちろん、ほかに方法がないわけではありませんが、今回は徹底的に日付データに使える関数を使い倒します。

 例えば、12時以降は翌日扱いとする場合、11時59分59秒までは2月14日ですが……。

SQL> select round(to_date('2005/02/14 11:59:59',
                         'yyyy/mm/dd hh24:mi:ss')) from dual;
ROUND(TO
--------
05-02-14
リスト7 12時以降は翌日扱いとする(11時59分59秒)

 12時ちょうどからは翌日の日付に変わります。

SQL> select round(to_date('2005/02/14 12:00:00',
                         'yyyy/mm/dd hh24:mi:ss')) from dual;
ROUND(TO
--------
05-02-15
リスト8 12時以降は翌日扱いとする(12時ちょうど)

 12時ではなく午後6時を切り替えタイミングにしたい場合は、6時間シフトさせれば大丈夫です。

SQL> select round(to_date('2005/02/14 12:00:00',
                    'yyyy/mm/dd hh24:mi:ss')-6/24) from dual;
ROUND(TO
--------
05-02-14
SQL> select round(to_date('2005/02/14 18:00:00',
                    'yyyy/mm/dd hh24:mi:ss')-6/24) from dual;
ROUND(TO
--------
05-02-15
リスト9 午後6時以降は翌日扱いとする

 実は、(というかすでに皆さん気付かれていると思いますが)どうせ時刻をシフトさせる必要があるのであれば、わざわざROUND関数を使用せず、最初からシフトさせたい時間を加減算すれば同じ結果になります。んーちょっと背景設定に無理があったでしょうか。ま、そういうこともできるということです。(次ページに続く)

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。