Can we define a GROUP_CONCAT function in PostgreSQL?

后端 未结 2 1671
滥情空心
滥情空心 2021-01-27 12:00

I have several lines of SQL code for my legacy database with GROUP_CONCAT statements, as in:

SELECT SUM(age), GROUP_CONCAT(sal) FROM Users;
<         


        
相关标签:
2条回答
  • 2021-01-27 12:29

    There is a string_agg() builtin which does what you want, but you specifically ask for it to be named group_concat for MySQL compatibility. Unfortunately, string_agg() uses an internal data type for accumulation (presumably to avoid copying the whole buffer on each append, I have not looked at the source though) and I didn't find a way to declare a SQL aggrerate identical to string_agg().

    Defining group_concat() function would not work either, as pg has to be made aware that it is an aggregate, not a function with an aggregate hidden inside, which would not work. Such a function would operate on one row at a time: any aggregate inside would just aggregate a single row and return it unchanged...

    Thus, this code will accumulate the elements into an array, then add the "," delimiters with array_to_string. I will use the array_agg() declaration (before it became a built-in) as a model, and simply add a finalizer function which will convert the aggregated array into text.

    CREATE OR REPLACE FUNCTION _group_concat_finalize(anyarray)
    RETURNS text AS $$
        SELECT array_to_string($1,',')
    $$ IMMUTABLE LANGUAGE SQL;
    
    CREATE AGGREGATE group_concat(anyelement) (
       SFUNC=array_append,
       STYPE=anyarray,
       FFUNC=_group_concat_finalize,
       INITCOND='{}'
    );
    
    SELECT group_concat(x) FROM foo;
    

    The nice thing is that it should work fine for any type, without hassle, thanks to the generic types "anyarray" and "anyelement".

    I would presume this would be slower than string_agg() if string_agg does indeed avoid to copy the whole aggregation array on each append. This should matter only if the number of rows to be grouped into each set is large, though. In this case, you probably can spend a minute editing the SQL query ;)

    http://sqlfiddle.com/#!17/c452d/1

    0 讨论(0)
  • 2021-01-27 12:45

    Yes this is possible. Have a look at https://github.com/2ndQuadrant/mysqlcompat/blob/master/sql_bits/aggregate.sql where it's already done, as such:

    -- GROUP_CONCAT()
    -- Note: only supports the comma separator
    -- Note: For DISTINCT and ORDER BY a subquery is required
    CREATE OR REPLACE FUNCTION _group_concat(text, text)
    RETURNS text AS $$
      SELECT CASE
        WHEN $2 IS NULL THEN $1
        WHEN $1 IS NULL THEN $2
        ELSE $1 operator(pg_catalog.||) ',' operator(pg_catalog.||) $2
      END
    $$ IMMUTABLE LANGUAGE SQL;
    
    CREATE AGGREGATE group_concat (
        BASETYPE = text,
        SFUNC = _group_concat,
        STYPE = text
    );
    
    0 讨论(0)
提交回复
热议问题