U kunt uw eigen aggregatiefunctie definiëren en deze vervolgens gebruiken met een vensterspecificatie om de geaggregeerde uitvoer in elke fase te krijgen in plaats van een enkele waarde.
Dus een aggregaat is een stuk toestand, en een transformatiefunctie om die toestand voor elke rij te wijzigen, en optioneel een finaliseringsfunctie om de toestand om te zetten in een uitvoerwaarde. Voor een eenvoudig geval als dit zou een transformatiefunctie voldoende moeten zijn.
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);
wat mij geeft:
[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
Deze getallen lijken overeen te komen met de spreadsheet die je aan de vraag hebt toegevoegd.
U kunt ook de functie definiëren om alpha door te geven als een parameter uit de instructie:
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
Deze functie is ook zo eenvoudig dat het helemaal niet in plpgsql hoeft te zijn, maar gewoon een sql-functie kan zijn, hoewel je in een van deze niet met naam naar parameters kunt verwijzen:
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
$$;