
Contents
- 昨日との違い、実行環境、参考サイト
- 複合型
配列(明日) JSON(明後日) 補足(状態遷移関数の NULL 判定を不要にする)、まとめ(明後日)
昨日との違い、実行環境、参考サイト
昨日は「自作の集約関数で、結果を複合型で返す」場合でした。今回は、集計途中の「遷移状態」で複数の値を持たせたい時の方法を遷移状態を平たく言うと、集計対象のレコードを一つずつスキャンする際に受け渡されるデータ。例えば単純な総和(sum)なら、1
いろいろな集約で、遷移状態に複数の値が必要なケースがあります。例えば単純な相加平均(avg)は全レコードの総和と個数、分散なら二乗和と総和と個数、など。ここで複数の値の持ち方に
下記が、昨日と重複しますが参考サイト。複合型を使う例が三つ目に、配列を使う例が四つ目にあります。ただし三つ目は複合型定義や状態遷移関数の中身が書かれてないため、分かりづらいかも。
- PostgreSQL 9.4.0
文書 : CREATE AGGREGATE -- 新しい集約関数を定義する - PostgreSQL 9.4.0
文書 : CREATE TYPE -- 新しいデータ型を定義する - PostgreSQL 9.4.0
文書 : ユーザ定義の集約 - DATABSE SOUP : A very simple custom aggregate
複合型
-- 複合型を定義 CREATE TYPE sum2_sum_cnt AS (sum2 float, sum float, cnt int); -- 状態遷移関数 CREATE OR REPLACE FUNCTION sfunc2_rec(sum2_sum_cnt, float) RETURNS sum2_sum_cnt LANGUAGE plpgsql IMMUTABLE AS $_$ BEGIN IF $2 IS NULL THEN RETURN $1; ELSE RETURN ($1.sum2 + pow($2, 2), $1.sum + $2, $1.cnt + 1); END IF; END; $_$; -- 集約関数 CREATE AGGREGATE agg2_rec(float) ( sfunc = sfunc2_rec, stype = sum2_sum_cnt, initcond = '(0, 0, 0)' );

例として「二乗和、総和、データ個数」を遷移させ、そのまま最終出力しました。最初に複合型を定義し、次に状態遷移関数を、最後に集約関数を定義します。
状態遷移関数では、NULL
↓ 使用例。数値は「統計計算の方法:個別データの分散、標準偏差など」(明星大学 船津好明先生)から拝借し、途中に
-- 使用例 SELECT agg2_rec(val) FROM unnest('{28,7,11,22,9,NULL,16,15,19}' :: float[]) AS val; agg2_rec -------------- (2361,127,8) (1 行)
-- 使用例、列をばらす SELECT (agg2_rec(val)).* FROM unnest('{28,7,11,22,9,NULL,16,15,19}' :: float[]) AS val; sum2 | sum | cnt ------+-----+----- 2361 | 127 | 8 (1 行)
-- 結果を使った計算例(相加平均と母分散) WITH a AS ( SELECT (agg2_rec(val)).* FROM unnest('{28,7,11,22,9,NULL,16,15,19}' :: float[]) AS val ) SELECT *, sum / cnt AS my_avg, sum2 / cnt - pow(sum / cnt, 2) AS my_var_pop FROM a; sum2 | sum | cnt | my_avg | my_var_pop ------+-----+-----+--------+------------ 2361 | 127 | 8 | 15.875 | 43.109375 (1 行)
-- 上の結果を、PostgreSQLに元からある集約関数で確認 SELECT avg(val), var_pop(val) FROM unnest('{28,7,11,22,9,NULL,16,15,19}' :: float[]) AS val; avg | var_pop --------+----------- 15.875 | 43.109375 (1 行)


複合型からの列の取り出しが少し面倒だけど、その後は普通の列と同様なので扱いやすいです。明日の配列は添字で取り出すしかなく、実用性が低い気がしますが一応、一つの方法として書きます。