【问题标题】:How to concatenate strings of a string field in a PostgreSQL 'group by' query?如何在 PostgreSQL 'group by' 查询中连接字符串字段的字符串?
【发布时间】:2010-09-07 19:35:45
【问题描述】:

我正在寻找一种通过查询连接组内字段字符串的方法。例如,我有一张桌子:

ID   COMPANY_ID   EMPLOYEE
1    1            Anna
2    1            Bill
3    2            Carol
4    2            Dave

我想按 company_id 分组以获得类似:

COMPANY_ID   EMPLOYEE
1            Anna, Bill
2            Carol, Dave

mySQL 中有一个内置函数可以做到这一点group_concat

【问题讨论】:

标签: sql postgresql group-by string-aggregation


【解决方案1】:

我认为答案没有任何功劳,因为我经过一番搜索才找到它:

我不知道的是,PostgreSQL 允许您使用 CREATE AGGREGATE 定义自己的聚合函数

PostgreSQL 列表中的This post 显示了创建一个函数来完成所需的工作是多么微不足道:

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

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

【讨论】:

    【解决方案2】:

    PostgreSQL 9.0 或更高版本:

    现代 Postgres(自 2010 年以来)具有 string_agg(expression, delimiter) 功能,可以完全满足提问者的要求:

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

    Postgres 9 还添加了指定ORDER BY 子句in any aggregate expression 的功能;否则,您必须订购所有结果或处理未定义的订单。所以你现在可以写:

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

    PostgreSQL 8.4.x:

    PostgreSQL 8.4(2009 年)引入了the aggregate function array_agg(expression),它收集数组中的值。然后array_to_string() 可以用来给出想要的结果:

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

    PostgreSQL 8.3.x 及更早版本:

    最初提出这个问题时,没有内置的聚合函数来连接字符串。最简单的自定义实现(suggested by Vajda Gabo in this mailing list post 等)是使用内置的textcat 函数(位于|| 运算符后面):

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

    Here is the CREATE AGGREGATE documentation.

    这只是将所有字符串粘合在一起,没有分隔符。为了在它们之间插入“,”而不在末尾插入,您可能需要创建自己的连接函数并将其替换为上面的“textcat”。这是我在 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;
    

    即使该行中的值为 null 或为空,此版本也会输出逗号,因此您会得到如下输出:

    a, b, c, , e, , g
    

    如果您希望删除多余的逗号来输出:

    a, b, c, e, g
    

    然后像这样在函数中添加ELSIF 检查:

    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;
    

    【讨论】:

    • 我不得不 S&R varchar 到文本(最新的 pgsql 稳定),但这很棒!
    • 函数只能用SQL编写,安装更方便(plpgsql需要超级用户安装)。示例见我的帖子。
    • “没有内置的聚合函数来连接字符串” - 你为什么不使用array_to_string(array_agg(employee), ',')?
    • +1 用于 PostgreSQL 9.0 函数。如果您需要关注 9.0 之前的版本,Markus 的回答会更好。
    • 请注意,最新版本的 Postgres 还允许在聚合函数中使用 Order By 子句,例如string_agg(employee, ',' Order By employee)
    【解决方案3】:

    如前所述,创建自己的聚合函数是正确的做法。这是我的串联聚合函数(可以找到details in French):

    CREATE OR REPLACE FUNCTION concat2(text, text) RETURNS text AS '
        SELECT CASE WHEN $1 IS NULL OR $1 = \'\' THEN $2
                WHEN $2 IS NULL OR $2 = \'\' THEN $1
                ELSE $1 || \' / \' || $2
                END; 
    '
     LANGUAGE SQL;
    
    CREATE AGGREGATE concatenate (
      sfunc = concat2,
      basetype = text,
      stype = text,
      initcond = ''
    

    );

    然后将其用作:

    SELECT company_id, concatenate(employee) AS employees FROM ...
    

    【讨论】:

      【解决方案4】:

      如果您要升级到 8.4,可能会对这个最新的公告列表 sn-p 感兴趣:

      直到 8.4 推出 超级高效的原生一个,可以加 array_accum() 函数在 用于滚动的 PostgreSQL 文档 将任何列添加到一个数组中,这可以 然后由应用程序代码使用,或 结合 array_to_string() 到 将其格式化为列表:

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

      我会链接到 8.4 开发文档,但他们似乎还没有列出此功能。

      【讨论】:

        【解决方案5】:

        我发现这个 PostgreSQL 文档很有帮助:http://www.postgresql.org/docs/8.0/interactive/functions-conditional.html。

        在我的例子中,如果字段不为空,我会使用纯 SQL 来连接带有括号的字段。

        select itemid, 
          CASE 
            itemdescription WHEN '' THEN itemname 
            ELSE itemname || ' (' || itemdescription || ')' 
          END 
        from items;
        

        【讨论】:

          【解决方案6】:

          使用 Postgres 文档跟进 Kev 的回答:

          首先,创建一个元素数组,然后使用内置的array_to_string 函数。

          CREATE AGGREGATE array_accum (anyelement)
          (
           sfunc = array_append,
           stype = anyarray,
           initcond = '{}'
          );
          
          select array_to_string(array_accum(name),'|') from table group by id;
          

          【讨论】:

            【解决方案7】:

            再次使用字符串连接的自定义聚合函数:您需要记住 select 语句将按任意顺序放置行,因此您需要在 select带有 order by 子句的 from 语句,然后是带有 group by 子句的外部 select 子句来聚合字符串,因此:

            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
            

            【讨论】:

              【解决方案8】:

              使用 Postgres 内置数组函数怎么样?至少在 8.4 上,这是开箱即用的:

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

              【讨论】:

              • 遗憾的是,这对我们在 Greenplum (v8.2) 上不起作用。 +1 都一样
              • 在 Greenplum 4.3.4.1(基于 PostgreSQL 8.2.15 构建)上对我来说很好。
              【解决方案9】:

              从 PostgreSQL 9.0 开始,您可以使用名为 string_agg 的聚合函数。您的新 SQL 应如下所示:

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

              【讨论】:

                【解决方案10】:

                根据 PostgreSQL 9.0 及更高版本,您可以使用名为 string_agg 的聚合函数。您的新 SQL 应如下所示:

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

                【讨论】:

                  【解决方案11】:

                  您还可以使用格式功能。它还可以自行隐式处理 text、int 等的类型转换。

                  create or replace function concat_return_row_count(tbl_name text, column_name text, value int)
                  returns integer as $row_count$
                  declare
                  total integer;
                  begin
                      EXECUTE format('select count(*) from %s WHERE %s = %s', tbl_name, column_name, value) INTO total;
                      return total;
                  end;
                  $row_count$ language plpgsql;
                  
                  
                  postgres=# select concat_return_row_count('tbl_name','column_name',2); --2 is the value
                  

                  【讨论】:

                  • 这与使用聚合连接字符串值有什么关系?
                  【解决方案12】:

                  我正在使用 Jetbrains Rider,复制上述示例的结果以重新执行很麻烦,因为它似乎将其全部包装在 JSON 中。这将它们连接成一个更容易运行的语句

                  select string_agg('drop table if exists "' || tablename || '" cascade', ';') 
                  from pg_tables where schemaname != $$pg_catalog$$ and tableName like $$rm_%$$
                  

                  【讨论】:

                    【解决方案13】:

                    如果您在不支持 string_agg 的 Amazon Redshift 上,请尝试使用 listagg。

                    SELECT company_id, listagg(EMPLOYEE, ', ') as employees
                    FROM EMPLOYEE_table
                    GROUP BY company_id;
                    

                    【讨论】:

                      【解决方案14】:

                      对PostgreSQL 和Google BigQuery SQL 使用STRING_AGG 函数:

                      SELECT company_id, STRING_AGG(employee, ', ')
                      FROM employees
                      GROUP BY company_id;
                      

                      【讨论】:

                        猜你喜欢
                        • 1970-01-01
                        • 1970-01-01
                        • 2013-02-22
                        • 2010-09-14
                        • 2010-09-21
                        • 2012-04-23
                        • 1970-01-01
                        • 2012-04-22
                        相关资源
                        最近更新 更多