如何使用postgres在string_agg中排除array_agg中的空值?

Dau*_*aud 77 sql postgresql postgresql-9.1 postgresql-8.4

如果我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;
Run Code Online (Sandbox Code Playgroud)

它返回,Larry,Phil而不是仅仅Larry,Phil(在我的9.1.2中,它显示NULL,Larry,Phil).就像在这个小提琴

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

问题是我已经Postgres 8.4安装在服务器上,并且string_agg()在那里不起作用.有没有办法使array_agg的工作类似于string_agg()?

Dal*_*ien 204

使用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;
Run Code Online (Sandbox Code Playgroud)

更新:使用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;
Run Code Online (Sandbox Code Playgroud)

  • 9.4更优雅.奇迹般有效 (10认同)
  • 如果它不明显,对于其他情况,如果您只想将过滤器直接应用于该列,则可以执行类似“array_agg(col_to_aggregate) FILTER (WHERE col_to_aggregate IS NOT NULL)”的操作。 (5认同)
  • 这是有效的,快速而优雅,它解决了类似于OP的问题.对于尚未执行此操作的用户,升级到9.3的原因.+1 (4认同)
  • 9.4 变体甚至更好,因为在我的情况下我需要过滤掉的是空值。 (2认同)

Ale*_*ore 29

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

array_remove(your_array, NULL)
Run Code Online (Sandbox Code Playgroud)

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

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
;
Run Code Online (Sandbox Code Playgroud)

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


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

例如:

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

如果你试试:

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

它将假设 '' 是 TEXT[] (array) 并且会抛出这个错误:

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

  • 注意到自 9.3 起支持 array_remove 可能会有所帮助 (2认同)

Clo*_*eto 23

SQL小提琴

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
Run Code Online (Sandbox Code Playgroud)

或者,更简单,可能更便宜,使用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
Run Code Online (Sandbox Code Playgroud)

SQL小提琴

  • @Clodoaldo如果你正在使用`array_to_string(array_agg(...))`你也可以使用`string_agg`. (3认同)

ror*_*ycl 12

在解决从数组聚合中删除空值的一般问题时,有两种主要方法可以解决问题:执行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;
Run Code Online (Sandbox Code Playgroud)

第二:

/*
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 = '{}'
);
Run Code Online (Sandbox Code Playgroud)

调用第二个(自然)看起来比第一个好一点:

从x中选择array_agg_notnull(v);


ced*_*d-b 8

我正在添加这个,即使这个线程很老,但我遇到了这个在小数组上运行良好的巧妙技巧.它运行在Postgres 8.4+上,没有额外的库或功能.

string_to_array(array_to_string(array_agg(my_column)))::int[]
Run Code Online (Sandbox Code Playgroud)

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


Lul*_*ulu 7

您应该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;
Run Code Online (Sandbox Code Playgroud)