Chapter 12. SQLの拡張――集約

PostgreSQLにおける集約は、状態値状態遷移関数の集合で表現されています。つまり、1つの集約はインスタンスの処理により変更された状態として定義することができます。新しい集約関数を定義するためには、状態のデータ型、状態の初期値、そして状態遷移関数のためのデータ型を選択します。状態遷移関数はただの普通の関数で、集約以外の文脈でも使うことができます。求められる集約の結果が動いている状態値のなかにあるべきデータと違う場合は、最終関数も指定することができます。

したがって、集約のユーザーに見える入力と結果のデータ型に加え、入力と結果の型のどちらとも違う内部状態値のデータ型があります。

もし最終関数を使わない集約を定義したとすると、それぞれの行のカラム値の動いている関数を計算する集約ができます。Sumはそのような集約の一例です。Sumは0から始まり常に現在の行の値をその時点までの総和に追加します。たとえば、もしSum集約を複素数のデータ型で動作するようにしたければ、そのデータ型の加算関数だけが必要になります。集約定義は以下のようになります。

CREATE AGGREGATE complex_sum (
    sfunc = complex_add,
    basetype = complex,
    stype = complex,
    initcond = '(0,0)'
);

SELECT complex_sum(a) FROM test_complex;

complex_sum
-------------
 (34,53.9)

実際の使用では、その集約はsumとだけ名付け、complex型の列にどの合計をあてはめるかはPostgreSQLにまかせます)

上記のsumの定義は、もし非NULLの入力値がなければ 0(初期状態)を返します。その場合本来は代わりにNULLを返したいのではないかと思いますし、SQLではsumがそのように動作することを期待しています。そうするためには単にinitcond 句を省略すれば、初期状態がNULLになります。通常はそうすると、sfuncがNULL状態の入力をチェックしなければいけないということを意味しますが、summaxminのような他の簡単な集約にとっては、最初の非NULL入力値を状態変数に挿入し、2番目の非NULL入力値で状態遷移関数をあてはめ始めれば十分です。PostgreSQLは、もし初期状態がNULLで状態遷移関数が"厳密(strict)"とマークされている場合、自動的にそれを実行します(つまりNULL入力として呼び出されないためにです)。

もう1つの"厳密(strict)"な状態遷移関数のデフォルト動作としては、NULL入力値が現われると前の状態値が変わらずに維持されるということがあります。 したがって、NULL 値は無視されます。もしNULL入力に対し他の動作が必要な場合は、単に状態遷移関数を非厳密として定義し、NULL入力の検査を行うようにコーディングし、必要なことをすればよいのです。

Avg (平均値計算)はもっと複雑な集約の一例です。それには2つの変動する状態が必要になります。入力の合計と入力数のカウントです。最終的な結果はこれらの値を割算することによって得られます。Averageは典型的に二要素の配列を状態遷移値として使って実装されます。たとえば、avg(float8)の組み込みの実装は以下のようになっています。

CREATE AGGREGATE avg (
    sfunc = float8_accum,
    basetype = float8,
    stype = float8[],
    finalfunc = float8_avg,
    initcond = '{0,0}'
);

さらに詳しい情報はリファレンスマニュアルCREATE AGGREGATEを参照してください。