我正在寻找一种通过查询连接组内字段的字符串的方法.例如,我有一张桌子:
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
最新版本的Postgres(自2010年末以来)具有的string_agg(expression, delimiter)
功能将完全满足问题的要求,甚至允许您指定分隔符字符串:
SELECT company_id, string_agg(employee, ', ') FROM mytable GROUP BY company_id;
Postgres 9.0还添加了在任何聚合表达式中指定ORDER BY
子句的功能; 否则,订单未定义.所以你现在可以写:
SELECT company_id, string_agg(employee, ', ' ORDER BY employee) FROM mytable GROUP BY company_id;
或者确实:
SELECT string_agg(actor_name, ', ' ORDER BY first_appearance)
PostgreSQL 8.4(2009年)引入了聚合函数array_agg(expression)
,它将值连接成一个数组.然后array_to_string()
可以用来给出想要的结果:
SELECT company_id, array_to_string(array_agg(employee), ', ') FROM mytable GROUP BY company_id;
string_agg
对于9.0之前的版本:如果有人遇到这个寻找9.0之前数据库的兼容垫片,可以实现string_agg
除ORDER BY
子句之外的所有内容.
因此,使用以下定义,这应该与9.x Postgres DB中的相同:
SELECT string_agg(name, '; ') AS semi_colon_separated_names FROM things;
但这将是一个语法错误:
SELECT string_agg(name, '; ' ORDER BY name) AS semi_colon_separated_names FROM things; --> ERROR: syntax error at or near "ORDER"
在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 );
在9.0之前,没有内置的聚合函数来连接字符串.最简单的自定义实现(由Vajda Gabo在此邮件列表中提供,以及许多其他内容)是使用内置textcat
函数(位于||
运算符后面):
CREATE AGGREGATE textcat_all( basetype = text, sfunc = textcat, stype = text, initcond = '' );
这是CREATE AGGREGATE
文档.
这简单地将所有字符串粘合在一起,没有分隔符.为了在它们之间插入","而不在最后,它可能想要创建自己的连接函数并将其替换为上面的"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;
如何使用Postgres内置数组函数?至少在8.4上这是开箱即用的:
SELECT company_id, array_to_string(array_agg(employee), ',') FROM mytable GROUP BY company_id;
从PostgreSQL 9.0开始,您可以使用名为string_agg的聚合函数.您的新SQL应该如下所示:
SELECT company_id, string_agg(employee, ', ') FROM mytable GROUP BY company_id;
我对答案没有任何赞誉,因为我经过一番搜索后发现它:
我不知道的是PostgreSQL允许您使用CREATE AGGREGATE定义自己的聚合函数
PostgreSQL列表中的这篇文章显示了创建一个函数来执行所需操作是多么微不足道:
CREATE AGGREGATE textcat_all( basetype = text, sfunc = textcat, stype = text, initcond = '' ); SELECT company_id, textcat_all(employee || ', ') FROM mytable GROUP BY company_id;
如前所述,创建自己的聚合函数是正确的做法.这是我的串联聚合函数(您可以在法语中找到详细信息):
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 ...
如果您要升级到8.4,可能会对此最新公告列表摘录感兴趣:
直到8.4出现一个超高效的本机,你可以在PostgreSQL文档中添加array_accum()函数,将任何列汇总到一个数组中,然后可以由应用程序代码使用,或者与array_to_string()结合使用以格式化它作为一个清单:
http://www.postgresql.org/docs/current/static/xaggr.html
我将链接到8.4开发文档,但它们似乎还没有列出此功能.
跟进Kev的回答,使用Postgres文档:
首先,创建一个元素数组,然后使用内置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;
再次使用字符串连接的自定义聚合函数:您需要记住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