Du kan definere din egen aggregerede funktion og derefter bruge den med en vinduesspecifikation for at få det samlede output på hvert trin i stedet for en enkelt værdi.
Så et aggregat er et stykke tilstand og en transformationsfunktion til at ændre denne tilstand for hver række, og eventuelt en afsluttende funktion til at konvertere tilstanden til en outputværdi. For et simpelt tilfælde som dette burde blot en transformationsfunktion være tilstrækkelig.
create function ema_func(numeric, numeric) returns numeric
language plpgsql as $$
declare
alpha numeric := 0.5;
begin
-- uncomment the following line to see what the parameters mean
-- raise info 'ema_func: % %', $1, $2;
return case
when $1 is null then $2
else alpha * $2 + (1 - alpha) * $1
end;
end
$$;
create aggregate ema(basetype = numeric, sfunc = ema_func, stype = numeric);
hvilket giver mig:
[email protected]@[local] =# select x, ema(x, 0.1) over(w), ema(x, 0.2) over(w) from data window w as (order by n asc) limit 5;
x | ema | ema
-----------+---------------+---------------
44.988564 | 44.988564 | 44.988564
39.5634 | 44.4460476 | 43.9035312
38.605724 | 43.86201524 | 42.84396976
38.209646 | 43.296778316 | 41.917105008
44.541264 | 43.4212268844 | 42.4419368064
Disse tal ser ud til at svare til det regneark, du føjede til spørgsmålet.
Du kan også definere funktionen til at sende alfa som en parameter fra sætningen:
create or replace function ema_func(state numeric, inval numeric, alpha numeric)
returns numeric
language plpgsql as $$
begin
return case
when state is null then inval
else alpha * inval + (1-alpha) * state
end;
end
$$;
create aggregate ema(numeric, numeric) (sfunc = ema_func, stype = numeric);
select x, ema(x, 0.5 /* alpha */) over (order by n asc) from data
Desuden er denne funktion faktisk så enkel, at den slet ikke behøver at være i plpgsql, men kan kun være en sql-funktion, selvom du ikke kan henvise til parametre ved navn i en af disse:
create or replace function ema_func(state numeric, inval numeric, alpha numeric)
returns numeric
language sql as $$
select case
when $1 is null then $2
else $3 * $2 + (1-$3) * $1
end
$$;