Pass extra parameter to PostgreSQL aggregate final function

前端 未结 3 503
甜味超标
甜味超标 2020-12-18 09:39

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?

e.g.:

CREATE         


        
相关标签:
3条回答
  • 2020-12-18 10:11

    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)
    
    0 讨论(0)
  • 2020-12-18 10:25

    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.

    0 讨论(0)
  • 2020-12-18 10:33

    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.

    0 讨论(0)
提交回复
热议问题