図1●今回取り上げるSQL文の注意点<BR>(1)日付型,(2)SUMやAVGなどの集約関数,(3)GROUP BY句とHAVING句――今回はこの3点にまつわるSQL文の注意点を説明する
図1●今回取り上げるSQL文の注意点<BR>(1)日付型,(2)SUMやAVGなどの集約関数,(3)GROUP BY句とHAVING句――今回はこの3点にまつわるSQL文の注意点を説明する
[画像のクリックで拡大表示]
図2●日付型の時間データに注意&lt;BR&gt;Oracleの日付型(DATE)には時間のデータが含まれるため,(1)日付型カラムに対して日付だけの検索条件を指定しても検索できない。日付を検索条件にしたい場合,日付型に時間データが含まれることを意識し,「(2)範囲指定する」,「(3)時間を切り捨てる」,「(4)日付を取り出す」などの方法を採る必要がある
図2●日付型の時間データに注意<BR>Oracleの日付型(DATE)には時間のデータが含まれるため,(1)日付型カラムに対して日付だけの検索条件を指定しても検索できない。日付を検索条件にしたい場合,日付型に時間データが含まれることを意識し,「(2)範囲指定する」,「(3)時間を切り捨てる」,「(4)日付を取り出す」などの方法を採る必要がある
[画像のクリックで拡大表示]

今回取り上げる日付型,集約関数,GROUP BY句,HAVING句は,便利なのでよく使われる。しかしこれらを使い始めのころに一度はやってしまう失敗例がある。一度経験すると大丈夫なのだが,分からないうちは戸惑うことになる。Oracleの日付型には時間データが含まれるため検索時に時間を忘れてはいけない。集約関数ではNULL値を除いて計算するので,平均値計算など意図した結果と異なることがある。HAVING句を利用する際は,実行性能を意識して使いたい。

 SQL文を利用する際に注意すべき基礎的なポイントを解説するセミナーの第3回である*1。前回は,文字列型,NULL*値,複合インデックス*にまつわる注意点を説明した。文字列型には2種類あること,NULLは0(ゼロ)でも空白文字でもない特殊な値であること,複合インデックスの利用は検索条件のカラムに左右されることを説明した。

 今回は,(1)日付型,(2)合計値や平均値などを計算する集約関数*,(3)GROUP BY*句やHAVING*句にまつわる注意点を説明する(図1[拡大表示])。なおRDBMSは,筆者の利用経験の多いOracleをベースとするが,基本的にはほかのRDBMSでも同様である。

●Oracleの日付型
時間データの存在を忘れやすい

 RDBMSには数値や文字のデータ型以外に,日付データを格納するデータ型が用意されている。Oracleでは「DATE」である。DATE型を使えば日付に日数を加算したり減算したりすることが可能で,日時としてあり得ないデータはエラーになる。日付情報を扱う場合,日付型を使うと便利である。ただ,OracleのDATE型には「年」「月」「日」の日付データ以外に,「時」「分」「秒」の時間データが含まれている*2。この時間データの存在が落とし穴になることがある。

一度は失敗する日付型データの検索

 DATE型は日付と時間を格納するが,デフォルトの出力形式は「YY-MM-DD(年-月-日)」で時間を表示しない。例えば「2002年10月2日10時10分10秒」のDATE型データは,デフォルトでは「02-10-02」と出力する。

 DATE型に関して一度は経験する失敗例がある。「2002年10月2日」のデータを検索する場合,DATE型のWHERE*句におけるデフォルトの指定方法は「YY-MM-DD(年-月-日)」であるため,

WHERE YMD = '02-10-02'

と指定してしまいがちだ。だが,これでは期待した検索結果が返ってこない(図2[拡大表示](1))。WHERE句に'02-10-02'と記述すると「2002年10月2日0時0分0秒」を指定したことになるからである。DATE型に時間データがあることを見落としやすい。

 「2002年10月2日」のデータを検索するには,(1)範囲指定する,(2)時間部分を切り捨てて日付と比較する,(3)日付部分を取り出して比較する——などの方法を採らなければならない。

 (1)2002年10月2日のデータを時間まで意識すれば,2002年10月2日0時0分0秒から2002年10月2日23時59分59秒までとなる。WHERE句のデフォルトの指定形式で指定すると,

WHERE YMD >= '02-10-02'
  AND YMD < '02-10-03'
となる(図2(2))。このように検索条件を指定すれば,2002年10月2日のデータが検索できる。

 (2)DATE型データの時間部分を切り捨てるには,TRUNC関数*を使えばよい。TRUNC関数は,DATE型データの時間部分を0時0分0秒にする。この関数を使って

WHERE TRUNC(YMD) = '02-10-02'
と指定すれば,2002年10月2日のデータが検索できる(同(3))。

 最後の方法は,(3)DATE型データから日付部分を取り出す方法である。DATE型データから日付部分を取り出すには,指定書式のデータを文字データとして取り出すことができるTO_CHAR関数*を使えばよい。

WHERE TO_CHAR(YMD, 'YY-MM-DD') = '02-10-02'

と指定すれば,2002年10月2日のデータが検索できる(同(4))。

 3つの方法のいずれもDATE型データを検索できるが,(2)TRUNC関数や(3)TO_CHAR

関数を利用する際は注意が必要である。TRUNC()やTO_CHAR()などの関数を利用すると,DATE型カラムに付いているインデックスが利用できなくなるからである。日付型カラムにインデックスを付けるケースは多く,インデックスの利用の有無で検索の実行性能は大きく変化することがある。日付の検索には,(1)範囲指定が無難な方法と言える。ただRDBMS製品によっては,関数利用時にも有効なインデックス機能を提供する製品もあるので,そのような機能の利用を合わせて検討しよう*3


玉川 敏一
シーズ・ラボ ITソリューション部 セクションマネージャー