0

I need to provide a number of stored procedures that return the result of an aggregate function run on a selection of values in the database, i.e. average, sum, min, max, etc. The selection of values is always done in a similar manner.

My first, naive implementation consists of one stored proc per aggregate function type, e.g. get_avg(...), get_sum(...). The functions obviously share quite a lot of duplicate code. I wonder if there is a way to move the aggregate type into a parameter or so and then use one stored proc only, e.g. get_aggregate(aggr_type, ...)

The only way I can think of as far as the pl/pgsql implementation is concerned is to select the values that should go into the aggregate and then use that in as many if aggr_type == ... / else clauses as there are aggregate types. This is not very flexible though and requires change of the code whenever a new aggregate type should be supported.

Is there any way to parameterize the name of a function in pl/pgsql ? Can ayone think of an approach that I fail to see ?

1 Answer 1

1

You could, technically, use the execute statement in your plpgsql function:

return query
execute $x$
select id, $x$ || quote_ident(agg_func) || $x$(col)::numeric as agg
from bar
$x$;

(See below for a proper example). The issue is, this will mean parsing/planning your query on every call.

On occasion, a better option will be to create a function that creates the various needed functions. Something like this:

create or replace function generate_agg_functions(_table regclass) returns void as $$
declare
  _agg_type text[] = '{sum, avg}';
  _ret_type regtype[] = '{numeric, numeric}';
  rec record;
begin
  for rec in select _agg_type[i] as agg_type, _ret_type[i] as ret_type
  from generate_subscripts(_agg_type, 1) i
  loop
  execute $x$
  create or replace function $x$ || quote_ident(_table || '_' || rec.agg_func) || $x$()
    returns table (id int, $x$ || quote_ident(rec.agg_type) || ' ' || ret_type || $x$)
  $def$
  begin
    return query
    select id,
           $x$ || quote_ident(rec.agg_type) || $x$(col)::$x$ || rec.ret_type || $x$
             as $x$ || quote_ident(rec.agg_type) || $x$
    from $x$ || _table || $x$;
  end;
  $def$ language plpgsql stable;
  $x$;
  end loop;
end;
$$ language plpgsql;

It's a bit trickier to write, but you'll end up not duplicating code without compromising the performance of the individual aggregates.

Sign up to request clarification or add additional context in comments.

1 Comment

wow! i feel right now like i still have a lot to learn about postgresql. maybe for the moment if aggr_type == ... / else is not such a bad option anymore. thanks for your help mate! :-)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.