136

如果我array_agg用来收集姓名,我会用逗号分隔我的姓名,但如果有一个null值,该 null 也将作为聚合中的名称。例如 :

SELECT g.id,
       array_agg(CASE WHEN g.canonical = 'Y' THEN g.users ELSE NULL END) canonical_users,
       array_agg(CASE WHEN g.canonical = 'N' THEN g.users ELSE NULL END) non_canonical_users
FROM groups g
GROUP BY g.id;

它返回,Larry,Phil而不是仅仅返回Larry,Phil(在我的 9.1.2 中,它显示NULL,Larry,Phil)。

相反,如果我使用string_agg(),它只显示名称(没有空逗号或空值)。

问题是我已经Postgres 8.4安装在服务器上,并且string_agg()在那里不起作用。有什么方法可以让 array_agg 工作类似于 string_agg() ?

4

8 回答 8

321

使用 postgresql-9.3 可以做到这一点;

SELECT g.id,
   array_remove(array_agg(CASE WHEN g.canonical = 'Y' THEN g.users ELSE NULL END), NULL) canonical_users,
   array_remove(array_agg(CASE WHEN g.canonical = 'N' THEN g.users ELSE NULL END), NULL) non_canonical_users
FROM groups g 
GROUP BY g.id;

更新:使用 postgresql-9.4;

SELECT g.id,
   array_agg(g.users) FILTER (WHERE g.canonical = 'Y') canonical_users,
   array_agg(g.users) FILTER (WHERE g.canonical = 'N') non_canonical_users
FROM groups g 
GROUP BY g.id;

更新(2022-02-19):也使用 postgresql-9.4;

当数组中的所有值都为 null 而不是返回 null 时,这会导致一个空数组;

SELECT g.id,
  coalesce( array_agg(g.users) FILTER (WHERE g.canonical = 'Y'), '{}' ) canonical_users,
  coalesce( array_agg(g.users) FILTER (WHERE g.canonical = 'N'), '{}' ) non_canonical_users
FROM groups g 
GROUP BY g.id;
于 2014-04-14T02:17:54.593 回答
38
select
    id,
    (select array_agg(a) from unnest(canonical_users) a where a is not null) canonical_users,
    (select array_agg(a) from unnest(non_canonical_users) a where a is not null) non_canonical_users
from (
    SELECT g.id,
           array_agg(CASE WHEN g.canonical = 'Y' THEN g.users ELSE NULL END) canonical_users,
           array_agg(CASE WHEN g.canonical = 'N' THEN g.users ELSE NULL END) non_canonical_users
    FROM groups g
    GROUP BY g.id
) s

或者,更简单并且可能更便宜,使用array_to_string它可以消除空值:

SELECT
    g.id,
    array_to_string(
        array_agg(CASE WHEN g.canonical = 'Y' THEN g.users ELSE NULL END)
        , ','
    ) canonical_users,
    array_to_string(
        array_agg(CASE WHEN g.canonical = 'N' THEN g.users ELSE NULL END)
        , ','
    ) non_canonical_users
FROM groups g
GROUP BY g.id
于 2012-10-29T14:11:24.563 回答
36

如果您正在寻找有关如何从数组中删除 NULL 的一般问题的现代答案,它是:

array_remove(your_array, NULL)

我对性能特别好奇,并想将其与最佳替代方案进行比较:

CREATE OR REPLACE FUNCTION strip_nulls(
    IN array_in ANYARRAY
)
RETURNS anyarray AS
'
SELECT
    array_agg(a)
FROM unnest(array_in) a
WHERE
    a IS NOT NULL
;
'
LANGUAGE sql
;

进行 pgbench 测试证明(非常有把握地)array_remove() 快两倍多一点。我对具有各种数组大小(10、100 和 1000 个元素)以及介于两者之间的随机 NULL 的双精度数进行了测试。


还值得注意的是,这可用于删除空格(''!= NULL)。但是第二个参数接受anyelement,并且由于它们很可能会用字符串文字指示空白,因此请确保将其转换为您想要的形式,通常是非数组。

例如:

select array_remove(array['abc', ''], ''::text);

如果你试试:

select array_remove(array['abc', ''], '');

它将假定 '' 是 TEXT[] (数组)并会抛出此错误:

错误:格式错误的数组文字:“”

于 2020-05-02T01:05:48.927 回答
15

在解决从数组聚合中删除空值的一般问题时,有两种主要的解决方法:执行 array_agg(unnest(array_agg(x)) 或创建自定义聚合。

第一个是上面显示的形式:

SELECT 
    array_agg(u) 
FROM (
    SELECT 
        unnest(
            array_agg(v)
        ) as u 
    FROM 
        x
    ) un
WHERE 
    u IS NOT NULL;

第二:

/*
With reference to
http://ejrh.wordpress.com/2011/09/27/denormalisation-aggregate-function-for-postgresql/
*/
CREATE OR REPLACE FUNCTION fn_array_agg_notnull (
    a anyarray
    , b anyelement
) RETURNS ANYARRAY
AS $$
BEGIN

    IF b IS NOT NULL THEN
        a := array_append(a, b);
    END IF;

    RETURN a;

END;
$$ IMMUTABLE LANGUAGE 'plpgsql';

CREATE AGGREGATE array_agg_notnull(ANYELEMENT) (
    SFUNC = fn_array_agg_notnull,
    STYPE = ANYARRAY,
    INITCOND = '{}'
);

调用第二个(自然)比第一个更好看:

从 x 中选择 array_agg_notnull(v);

于 2013-06-25T17:39:29.827 回答
9

即使这个线程已经很老了,我也会添加它,但是我遇到了这个在小型数组上效果很好的巧妙技巧。它在 Postgres 8.4+ 上运行,无需额外的库或函数。

string_to_array(array_to_string(array_agg(my_column)))::int[]

array_to_string()方法实际上摆脱了空值。

于 2014-02-25T00:41:14.373 回答
5

你应该array_aggarray_remove包装你的。

SELECT g.id,
       array_remove(array_agg(CASE WHEN g.canonical = 'Y' THEN g.users ELSE NULL END), NULL) canonical_users,
       array_remove(array_agg(CASE WHEN g.canonical = 'N' THEN g.users ELSE NULL END), NULL) non_canonical_users
FROM groups g
GROUP BY g.id;
于 2021-02-04T11:11:37.910 回答
3

正如评论中所建议的,您可以编写一个函数来替换数组中的空值,但是正如评论中链接的线程中所指出的那样,如果您必须创建一个聚合,这种方式会降低聚合函数的效率,拆分它然后再次聚合它。

我认为在数组中保留空值只是 Array_Agg 的一个(也许是不需要的)特性。您可以使用子查询来避免这种情况:

SELECT  COALESCE(y.ID, n.ID) ID,
        y.Users,
        n.Users
FROM    (   SELECT  g.ID, ARRAY_AGG(g.Users) AS Users
            FROM    Groups g
            WHERE   g.Canonical = 'Y'
            GROUP BY g.ID
        ) y
        FULL JOIN 
        (   SELECT  g.ID, ARRAY_AGG(g.Users) AS Users
            FROM    Groups g
            WHERE   g.Canonical = 'N'
            GROUP BY g.ID
        ) n
            ON n.ID = y.ID

SQL 小提琴

于 2012-10-29T14:54:56.497 回答
0

这很简单,首先为text[]创建一个新的- (减号)运算符:

CREATE OR REPLACE FUNCTION diff_elements_text
    (
        text[], text[] 
    )
RETURNS text[] as 
$$
    SELECT array_agg(DISTINCT new_arr.elem)
    FROM
        unnest($1) as new_arr(elem)
        LEFT OUTER JOIN
        unnest($2) as old_arr(elem)
        ON new_arr.elem = old_arr.elem
    WHERE old_arr.elem IS NULL
$$ LANGUAGE SQL IMMUTABLE;

CREATE OPERATOR - (
    PROCEDURE = diff_elements_text,
    leftarg = text[],
    rightarg = text[]
);

并简单地减去数组[null]:

select 
    array_agg(x)-array['']
from
    (   select 'Y' x union all
        select null union all
        select 'N' union all
        select '' 
    ) x;

就这样:

{是,否}

于 2019-10-29T12:37:01.167 回答