ホーム DoRuby Postgresqlでカレンダーを使わずに日付を列挙する方法(generate_series)

Postgresqlでカレンダーを使わずに日付を列挙する方法(generate_series)

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

今回、カレンダーテーブルを使わずに日付を列挙する方法を書きます。

 運用中のシステムでカレンダーと結合したい要求が発生!

カレンダーテーブルを作りたいが、データベースにテーブル追加ができない場合、どうしよう?

 generate_series

Postgresql8.xから、generate_series という集合関数が出来た。

version 8と9で拡張されているので注意

 9.xの場合

関数引数型戻り型説明
generate_series(start, stop)intまたはbigintsetof intまたはsetof bigint(引数の型と同一)startからstopまで、刻み1で連続する値を生成します。
generate_series(start, stop, step)intまたはbigintsetof intまたはsetof bigint(引数の型と同一)startからstopまで、刻みstepで連続する値を生成します。
generate_series(start, stop, step interval)timestamp または timestamp with time zonesetof timestamp または setof timestamp with time zone(引数型と同じ)startからstopまで、刻みstepで連続する値を生成します。

SELECT * FROM generate_series(‘2008-03-01 00:00’::timestamp,

‘2008-03-04 12:00′, ’10 hours’);

generate_series

———————

2008-03-01 00:00:00

2008-03-01 10:00:00

2008-03-01 20:00:00

2008-03-02 06:00:00

2008-03-02 16:00:00

2008-03-03 02:00:00

2008-03-03 12:00:00

2008-03-03 22:00:00

2008-03-04 08:00:00

(9 rows)http://www.postgresql.jp/document/9.1/html/functions-srf.html

これを使って日付の列挙をしてみる。

6月一か月分の日付を以下のSQLで取得してみる。

SELECT

*

FROM

generate_series( cast(‘2012/06/01’ as timestamp) ,

DATE_TRUNC(‘month’, cast(‘2012/06/01’ as timestamp) + ‘1 months’) + ‘-1 days’,

‘1 days’)

以下のように出力出来ました。

generate_series

———————

2012-06-01 00:00:00

2012-06-02 00:00:00

2012-06-03 00:00:00

2012-06-04 00:00:00

2012-06-05 00:00:00

2012-06-06 00:00:00

2012-06-07 00:00:00

2012-06-08 00:00:00

2012-06-09 00:00:00

2012-06-10 00:00:00

2012-06-11 00:00:00

2012-06-12 00:00:00

2012-06-13 00:00:00

2012-06-14 00:00:00

2012-06-15 00:00:00

2012-06-16 00:00:00

2012-06-17 00:00:00

2012-06-18 00:00:00

2012-06-19 00:00:00

2012-06-20 00:00:00

2012-06-21 00:00:00

2012-06-22 00:00:00

2012-06-23 00:00:00

2012-06-24 00:00:00

2012-06-25 00:00:00

2012-06-26 00:00:00

2012-06-27 00:00:00

2012-06-28 00:00:00

2012-06-29 00:00:00

2012-06-30 00:00:00

(30 rows)

 8.xの場合

関数引数型戻り値の型説明
generate_series(start, stop)int または bigintsetof int またはsetof bigint (引数の型と同一)startからstopまでの刻み1で連続する値を生成します。
generate_series(start, stop, step)int または bigintsetof int または setof bigint (引数の型と同一)startからstopまでの刻みstepで連続する値を生成します。

select current_date + s.a as dates from generate_series(0,14,7) as s(a);

dates

————

2004-02-05

2004-02-12

2004-02-19

(3 rows)http://www.postgresql.jp/document/pg800doc/html/functions-srf.html

以下のSQLで先ほどと同様に6月の一か月分の日付を出力してみます。

SELECT

to_char( ‘2012-06-01’::Date + arr.i, ‘YYYY/MM/DD’ ) as date1

FROM

generate_series( 0,

(select EXTRACT(DAY FROM DATE_TRUNC(‘month’, cast(‘2012/06/01’ as timestamp) + ‘1 months’) + ‘-1 days’) – 1)::int

) as arr(i)

date1

————

2012/06/01

2012/06/02

2012/06/03

2012/06/04

2012/06/05

2012/06/06

2012/06/07

2012/06/08

2012/06/09

2012/06/10

2012/06/11

2012/06/12

2012/06/13

2012/06/14

2012/06/15

2012/06/16

2012/06/17

2012/06/18

2012/06/19

2012/06/20

2012/06/21

2012/06/22

2012/06/23

2012/06/24

2012/06/25

2012/06/26

2012/06/27

2012/06/28

2012/06/29

2012/06/30

(30 rows)

 応用

この日付一覧をテーブルと結合してみます。

以下のデータを例とします。

テーブル名:test1

idfield1create_date
1test2012-09-01
2test22012-09-01
3test32012-09-09

9.xの場合

SELECT

cal.date1,

COALESCE( test1_date.cnt, 0 ) as cnt

FROM

  • 日付一覧

(SELECT

to_char(arr,’YYYY/MM/DD’) as date1

FROM

generate_series( cast(‘2012/09/01’ as timestamp) ,

DATE_TRUNC(‘month’, cast(‘2012/09/01’ as timestamp) + ‘1 months’) + ‘-1 days’,

‘1 days’) as arr

) as cal

  • 結合テーブル

LEFT JOIN

(

SELECT

to_char(create_date, ‘YYYY/MM/DD’) as date2,

count(*) as cnt

FROM

test1

GROUP BY

date2

) as test1_date

ON cal.date1 = test1_date.date2

ORDER BY

cal.date1

以下のように集計結果が出ました。

date1 | cnt

————+—–

2012/09/01 | 2

2012/09/02 | 0

2012/09/03 | 0

2012/09/04 | 0

2012/09/05 | 0

2012/09/06 | 0

2012/09/07 | 0

2012/09/08 | 0

2012/09/09 | 1

2012/09/10 | 0

2012/09/11 | 0

2012/09/12 | 0

2012/09/13 | 0

2012/09/14 | 0

2012/09/15 | 0

2012/09/16 | 0

2012/09/17 | 0

2012/09/18 | 0

2012/09/19 | 0

2012/09/20 | 0

2012/09/21 | 0

2012/09/22 | 0

2012/09/23 | 0

2012/09/24 | 0

2012/09/25 | 0

2012/09/26 | 0

2012/09/27 | 0

2012/09/28 | 0

2012/09/29 | 0

2012/09/30 | 0

8.xの場合

SELECT

cal.date1,

COALESCE( test1_date.cnt, 0 ) as cnt

FROM

  • 日付一覧

(

SELECT

to_char( ‘2012-09-01’::Date + arr.i, ‘YYYY/MM/DD’ ) as date1

FROM

generate_series( 0,

(select EXTRACT(DAY FROM DATE_TRUNC(‘month’, cast(‘2012/09/01’ as timestamp) + ‘1 months’) + ‘-1 days’) – 1)::int

) as arr(i)

) as cal

  • 結合テーブル

LEFT JOIN

(

SELECT

to_char(create_date, ‘YYYY/MM/DD’) as date2,

count(*) as cnt

FROM

test1

GROUP BY

date2

) as test1_date

ON cal.date1 = test1_date.date2

ORDER BY

cal.date1

以下のように集計結果が出ました。

date1 | cnt

————+—–

2012/09/01 | 2

2012/09/02 | 0

2012/09/03 | 0

2012/09/04 | 0

2012/09/05 | 0

2012/09/06 | 0

2012/09/07 | 0

2012/09/08 | 0

2012/09/09 | 1

2012/09/10 | 0

2012/09/11 | 0

2012/09/12 | 0

2012/09/13 | 0

2012/09/14 | 0

2012/09/15 | 0

2012/09/16 | 0

2012/09/17 | 0

2012/09/18 | 0

2012/09/19 | 0

2012/09/20 | 0

2012/09/21 | 0

2012/09/22 | 0

2012/09/23 | 0

2012/09/24 | 0

2012/09/25 | 0

2012/09/26 | 0

2012/09/27 | 0

2012/09/28 | 0

2012/09/29 | 0

2012/09/30 | 0

このように、generate_series関数を駆使すれば、カレンダーテーブルが無くとも

データの集計処理に使えます。

generate_series関数は他にも使い方があるので、詳しくは、PostgreSQLのドキュメントを参照ください。

記事を共有

最近人気な記事