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

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中有一个内置函数可以做到这一点 组连接

有帮助吗?

解决方案

PostgreSQL 9.0 或更高版本:

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 或更高版本:

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
);

自定义变体(所有 Postgres 版本)

在 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 开始,您可以使用名为的聚合函数 字符串聚合. 。您的新 SQL 应该如下所示:

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

我不承认这个答案,因为我在一番搜索后找到了它:

我不知道的是 PostgreSQL 允许你定义自己的聚合函数 创建聚合

这个帖子 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()一起使用array_to_string()它作为列表:

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

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

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

再次关注字符串连接的自定义聚合函数的使用:您需要记住,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

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

使用 STRING_AGG 函数为 PostgreSQL谷歌 BigQuery SQL:

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

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

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

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

我正在使用 Jetbrains Rider,从上面的示例中复制结果来重新执行是一件很麻烦的事情,因为它似乎将所有内容都包装在 JSON 中。这将它们连接成一个更容易运行的语句

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

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

SELECT company_id, listagg(EMPLOYEE, ', ') as employees
FROM EMPLOYEE_table
GROUP BY company_id;
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top