How to concatenate strings of a string field in a PostgreSQL 'group by' query?

前端 未结 14 1422
北荒
北荒 2020-11-22 02:37

I am looking for a way to concatenate the strings of a field within a group by query. So for example, I have a table:

ID   COMPANY_ID   EMPLOYEE
1    1               


        
相关标签:
14条回答
  • 2020-11-22 03:04

    I found this PostgreSQL documentation helpful: http://www.postgresql.org/docs/8.0/interactive/functions-conditional.html.

    In my case, I sought plain SQL to concatenate a field with brackets around it, if the field is not empty.

    select itemid, 
      CASE 
        itemdescription WHEN '' THEN itemname 
        ELSE itemname || ' (' || itemdescription || ')' 
      END 
    from items;
    
    0 讨论(0)
  • 2020-11-22 03:05

    If you are on Amazon Redshift, where string_agg is not supported, try using listagg.

    SELECT company_id, listagg(EMPLOYEE, ', ') as employees
    FROM EMPLOYEE_table
    GROUP BY company_id;
    
    0 讨论(0)
  • 2020-11-22 03:07

    This latest announcement list snippet might be of interest if you'll be upgrading to 8.4:

    Until 8.4 comes out with a super-effient native one, you can add the array_accum() function in the PostgreSQL documentation for rolling up any column into an array, which can then be used by application code, or combined with array_to_string() to format it as a list:

    http://www.postgresql.org/docs/current/static/xaggr.html

    I'd link to the 8.4 development docs but they don't seem to list this feature yet.

    0 讨论(0)
  • 2020-11-22 03:07

    Following up on Kev's answer, using the Postgres docs:

    First, create an array of the elements, then use the built-in array_to_string function.

    CREATE AGGREGATE array_accum (anyelement)
    (
     sfunc = array_append,
     stype = anyarray,
     initcond = '{}'
    );
    
    select array_to_string(array_accum(name),'|') from table group by id;
    
    0 讨论(0)
  • 2020-11-22 03:08

    Following yet again on the use of a custom aggregate function of string concatenation: you need to remember that the select statement will place rows in any order, so you will need to do a sub select in the from statement with an order by clause, and then an outer select with a group by clause to aggregate the strings, thus:

    SELECT custom_aggregate(MY.special_strings)
    FROM (SELECT special_strings, grouping_column 
            FROM a_table 
            ORDER BY ordering_column) MY
    GROUP BY MY.grouping_column
    
    0 讨论(0)
  • 2020-11-22 03:09

    PostgreSQL 9.0 or later:

    Recent versions of Postgres (since late 2010) have the string_agg(expression, delimiter) function which will do exactly what the question asked for, even letting you specify the delimiter string:

    SELECT company_id, string_agg(employee, ', ')
    FROM mytable
    GROUP BY company_id;
    

    Postgres 9.0 also added the ability to specify an ORDER BY clause in any aggregate expression; otherwise, the order is undefined. So you can now write:

    SELECT company_id, string_agg(employee, ', ' ORDER BY employee)
    FROM mytable
    GROUP BY company_id;
    

    Or indeed:

    SELECT string_agg(actor_name, ', ' ORDER BY first_appearance)
    

    PostgreSQL 8.4 or later:

    PostgreSQL 8.4 (in 2009) introduced the aggregate function array_agg(expression) which concatenates the values into an array. Then array_to_string() can be used to give the desired result:

    SELECT company_id, array_to_string(array_agg(employee), ', ')
    FROM mytable
    GROUP BY company_id;
    

    string_agg for pre-8.4 versions:

    In case anyone comes across this looking for a compatibilty shim for pre-9.0 databases, it is possible to implement everything in string_agg except the ORDER BY clause.

    So with the below definition this should work the same as in a 9.x Postgres DB:

    SELECT string_agg(name, '; ') AS semi_colon_separated_names FROM things;
    

    But this will be a syntax error:

    SELECT string_agg(name, '; ' ORDER BY name) AS semi_colon_separated_names FROM things;
    --> ERROR: syntax error at or near "ORDER"
    

    Tested on PostgreSQL 8.3.

    CREATE FUNCTION string_agg_transfn(text, text, text)
        RETURNS text AS 
        $$
            BEGIN
                IF $1 IS NULL THEN
                    RETURN $2;
                ELSE
                    RETURN $1 || $3 || $2;
                END IF;
            END;
        $$
        LANGUAGE plpgsql IMMUTABLE
    COST 1;
    
    CREATE AGGREGATE string_agg(text, text) (
        SFUNC=string_agg_transfn,
        STYPE=text
    );
    

    Custom variations (all Postgres versions)

    Prior to 9.0, there was no built-in aggregate function to concatenate strings. The simplest custom implementation (suggested by Vajda Gabo in this mailing list post, among many others) is to use the built-in textcat function (which lies behind the || operator):

    CREATE AGGREGATE textcat_all(
      basetype    = text,
      sfunc       = textcat,
      stype       = text,
      initcond    = ''
    );
    

    Here is the CREATE AGGREGATE documentation.

    This simply glues all the strings together, with no separator. In order to get a ", " inserted in between them without having it at the end, you might want to make your own concatenation function and substitute it for the "textcat" above. Here is one I put together and tested on 8.3.12:

    CREATE FUNCTION commacat(acc text, instr text) RETURNS text AS $$
      BEGIN
        IF acc IS NULL OR acc = '' THEN
          RETURN instr;
        ELSE
          RETURN acc || ', ' || instr;
        END IF;
      END;
    $$ LANGUAGE plpgsql;
    

    This version will output a comma even if the value in the row is null or empty, so you get output like this:

    a, b, c, , e, , g
    

    If you would prefer to remove extra commas to output this:

    a, b, c, e, g
    

    Then add an ELSIF check to the function like this:

    CREATE FUNCTION commacat_ignore_nulls(acc text, instr text) RETURNS text AS $$
      BEGIN
        IF acc IS NULL OR acc = '' THEN
          RETURN instr;
        ELSIF instr IS NULL OR instr = '' THEN
          RETURN acc;
        ELSE
          RETURN acc || ', ' || instr;
        END IF;
      END;
    $$ LANGUAGE plpgsql;
    
    0 讨论(0)
提交回复
热议问题