简体   繁体   中英

Pass extra parameter to PostgreSQL aggregate final function

Is the only way to pass an extra parameter to the final function of a PostgreSQL aggregate to create a special TYPE for the state value?

eg:

CREATE TYPE geomvaltext AS (
    geom public.geometry,
    val double precision,
    txt text
);

And then to use this type as the state variable so that the third parameter (text) finally reaches the final function?

Why aggregates can't pass extra parameters to the final function themselves? Any implementation reason?

So we could easily construct, for example, aggregates taking a method:

SELECT ST_MyAgg(accum_number, 'COMPUTE_METHOD') FROM blablabla

Thanks

You can define an aggregate with more than one parameter.

I don't know if that solves your problem, but you could use it like this:

CREATE OR REPLACE FUNCTION myaggsfunc(integer, integer, text) RETURNS integer
   IMMUTABLE STRICT LANGUAGE sql AS
$f$
   SELECT CASE $3
           WHEN '+' THEN $1 + $2
           WHEN '*' THEN $1 * $2
           ELSE NULL
        END
$f$;

CREATE AGGREGATE myagg(integer, text) (
   SFUNC = myaggsfunc(integer, integer, text),
   STYPE = integer
);

It could be used like this:

CREATE TABLE mytab
   AS SELECT * FROM generate_series(1, 10) i;

SELECT myagg(i, '+') FROM mytab;

 myagg 
-------
    55
(1 row)

SELECT myagg(i, '*') FROM mytab;

  myagg  
---------
 3628800
(1 row)

You would have to rewrite the final function itself, and in that case you might as well write a set of new aggregate functions, one for each possible COMPUTE_METHOD . If the COMPUTE_METHOD is a data value or implied by a data value, then a CASE statement can be used to select the appropriate aggregate method. Alternatively, you may want to create a custom composite type with fields for accum_number and COMPUTE_METHOD , and write a single new aggregate function that uses this new data type.

I solved a similar issue by making a custom aggregate function that did all the operations at once and stored their states in an array.

CREATE AGGREGATE myagg(integer)
(
    INITCOND = '{ 0, 1 }',
    STYPE = integer[],
    SFUNC = myaggsfunc
);

and:

CREATE OR REPLACE FUNCTION myaggsfunc(agg_state integer[], agg_next integer)
RETURNS integer[] IMMUTABLE STRICT LANGUAGE 'plpgsql' AS $$
BEGIN
    agg_state[1] := agg_state[1] + agg_next;
    agg_state[2] := agg_state[2] * agg_next;
    RETURN agg_state;
END;
$$;

Then made another function that selected one of the results based on the second argument:

CREATE OR REPLACE FUNCTION myagg_pick(agg_state integer[], agg_fn character varying)
RETURNS integer IMMUTABLE STRICT LANGUAGE 'plpgsql' AS $$
BEGIN
    CASE agg_fn
        WHEN '+' THEN RETURN agg_state[1];
        WHEN '*' THEN RETURN agg_state[2];
        ELSE RETURN 0;
    END CASE;
END;
$$;

Usage:

SELECT myagg_pick(myagg("accum_number"), 'COMPUTE_METHOD') FROM "mytable" GROUP BY ...

Obvious downside of this is the overhead of performing all the functions instead of just one. However when dealing with simple operations such as adding, multiplying etc. it should be acceptable in most cases.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM