SQLによるカレンダープログラム

Last modified: 2007/08/09 20:40:26

ホームへ戻る


情報処理学会 夏のプログラミングシンポジウム「First Programming Languages プログラミング言語の実力と美学」の応援演説として、SQLを使ったカレンダープログラムを紹介しました。

基本的にZellerの公式を使って朔日の曜日を求め、各月の日数情報と組み合わせて整形して表示します。朔日から1ヶ月分を並べてしまうので、グレゴリオ暦への切り替え月は正常に表示されません。DBMSが持つ日付計算の機能を使ったり、PL/SQLの類の制御構造を使ってはつまらないので、SQLだけにしました。PostgreSQL-8.2.4を使っているので、PostgreSQLの制約に引きずられているかもしれません。create sequenceのmaxvalueやstartにサブクエリが使えると良かったのですが…。

drop table if exists result;
create table result
	(Sun integer, Mon integer, Tue integer, Wed integer,
	Thu integer, Fri integer, Sat integer);

drop table lastDays;
create table lastDays (nDays integer[]);

drop function if exists leap(integer);
create function leap (integer) returns integer as $$
	select case when (($1 % 4 = 0) and ($1 % 100 <>0) or ($1 % 400 = 0)) then 1 else 0 end;
$$ language 'sql';

drop sequence if exists days;
create sequence days minvalue -9 maxvalue 31 cycle;

drop function if exists getYear (integer, integer);
create function getYear (integer, integer) returns integer as $$
	select case when (($1 = 1) or ($1 = 2)) then $2 - 1 else $2 end;
$$ language 'sql';

drop function if exists getMonth (integer);
create function getMonth (integer) returns integer as $$
	select case when (($1 = 1) or ($1 = 2)) then $1 + 12 else $1 end;
$$ language 'sql';

drop function if exists getDayOfWeek (integer, integer);
create function getDayOfWeek (integer, integer) returns integer as $$
	select (getYear($1,$2) + (getYear($1,$2)/4) - (getYear($1,$2)/100) + (getYear($1,$2)/400) + (13*getMonth($1)+8)/5 +  1)%7;
$$ language 'sql';

drop function if exists getDay (integer);
create function getDay (integer) returns bigint as $$
	select case when (nextval('days') < 1) then 0 else (case when (currval('days') > $1) then 0 else currval('days') end) end;
$$ language 'sql';

drop function if exists buildCal (integer, integer);
create function buildCal (integer, integer) returns void as $$
	insert into result values(getDay($2), getDay($2), getDay($2), getDay($2), getDay($2), getDay($2), getDay($2));
	select case when ($1 > 1) then buildCal($1 - 1, $2) end;
$$ language 'sql';

drop function if exists cal (integer, integer);
create function cal (integer, integer) returns void as $$
	delete from result;
	delete from lastDays;
	select setval('days', (select 0 - getDayOfWeek($1, $2)));
	insert into lastDays values ('{31,28,31,30,31,30,31,31,30,31,30,31}');
	update lastDays set nDays[2] = (select nDays[2] from lastDays) + leap($2);
	select buildCal(6, (select nDays[$1] from lastDays));
$$ language 'sql';

select cal(8, 2007);
select * from result;

ホームへ戻る
Kazutaka Maruyama <kazutaka_(!!at-mark!!)_sanpo-lab.jp>(SPAM対策)