ChatGPT解决这个技术问题 Extra ChatGPT

如何在 PostgreSQL 'group by' 查询中连接字符串字段的字符串?

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

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

Markus Döring 的回答在技术上更好。
@pstanton,Döring 的答案只适用于 8.4 及以下版本。
这个问题似乎更适合 dba.stackexchange.com
这应该是现在的有效答案stackoverflow.com/a/47638417/243233

N
Neall

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)
M
Markus Döring

使用 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 构建)上对我来说工作正常。
d
dirbacke

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

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


G
Guy C

我对答案没有任何贡献,因为我经过一番搜索后找到了它:

我不知道的是 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;

b
bortzmeyer

如前所述,创建自己的聚合函数是正确的做法。这是我的串联聚合函数(您可以找到 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 ...

K
Kev

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

在 8.4 推出超高效的原生版本之前,您可以在 PostgreSQL 文档中添加 array_accum() 函数,用于将任何列汇总到一个数组中,然后可以由应用程序代码使用,或者与 array_to_string() 结合使用来格式化它作为一个列表:http://www.postgresql.org/docs/current/static/xaggr.html

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


B
Brad Koch

使用 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;

B
Brad Koch

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

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

V
Valentin Podkamennyi

PostgreSQLGoogle BigQuery SQL 使用 STRING_AGG 函数:

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

佚名

我发现这个 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;

G
Gobinath

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

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

S
Sandip Debnath

您还可以使用格式功能。它也可以隐式地自行处理 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

这与使用聚合连接字符串值有什么关系?
D
Damien Sawyer

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

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

A
Arnaud Peralta

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

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