Reputation: 7009
Assuming that my subquery
yields a number of rows with the columns (x,y), I would like to calculate the value avg(abs(x-mean)/y)
. where mean
effectively is avg(x)
.
select avg(abs(x-avg(x))/y) as incline from subquery
fails because I cannot nest aggregation functions. Nor can I think of a way to calculate the mean in a subquery while keeping the original result set. An avgdev function as it exists in other dialects would not exactly help me, so here I am stuck. Probably just due to lack of sql knowledge - calculating the value from the result set in postprocessing is easy.
Which SQL construct could help me?
Edit: Server version is 8.3.4. No window functions with WITH
or OVER
available here.
Upvotes: 1
Views: 579
Reputation:
if your data sets are not too large you could accumulate them into an array and then return the incline from a function:
create type typ as (x numeric, y numeric);
create aggregate array_accum( sfunc = array_append,
basetype = anyelement,
stype = anyarray,
initcond = '{}' );
create or replace function unnest(anyarray) returns setof anyelement
language sql immutable strict as $$
select $1[i] from generate_series(array_lower($1,1), array_upper($1,1)) i;$$;
create function get_incline(typ[]) returns numeric
language sql immutable strict as $$
select avg(abs(x-(select avg(x) from unnest($1)))/y) from unnest($1);$$;
select get_incline((select array_accum((x,y)::typ) from subquery));
sample view for testing:
create view subquery as
select generate_series(1,5) as x, generate_series(1,6) as y;
Upvotes: 1
Reputation: 7009
One option I found is to use a temporary table:
begin;
create temporary table sub on commit drop as (...subquery code...);
select avg(abs(x-mean)/y) as incline from (SELECT x, y, (SELECT avg(x) FROM sub) AS mean FROM sub) as sub2;
commit;
But is that overkill?
Upvotes: 0
Reputation:
Not sure I understand you correctly, but you might be looking for something like this:
SELECT avg(x - mean/y) FROM ( SELECT x, y, avg(x) as mean over(partition by your_grouping_column) FROM your_table ) t
If you do not need to group your results to get the correct avg(x) then simply leave out the "partition by" using an empty over: over()
Upvotes: 1