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

如果我使用array_agg来收集名称,我得到我的名字用逗号分隔,但如果有一个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()?

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 

或者,更简单,可能更便宜,使用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 

SQL小提琴

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

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

第一种是上面显示的forms:

 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 = '{}' ); 

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

从xselectarray_agg_notnull(v);

即使这个线程已经很老了,我也join了这个,但是我遇到了这个在小arrays上工作得很好的巧妙技巧。 它在Postgres 8.4+上运行,无需额外的库或函数。

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

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

正如在注释中提到的那样,你可以编写一个函数来replace数组中的空值,但是也可以在注释中链接的线程中指出,如果必须创build一个聚合函数,这种方法会破坏聚合函数的效率,拆分它然后再聚集它。

我认为保留数组中的空值只是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 FIDDLE

更大的问题是为什么一次拉所有用户/组合。 保证你的UI不能处理所有的数据。 添加分页过大的数据也是一个坏主意。 让用户在看到数据之前对其进行过滤。 确保您的JOIN选项集在列表中,以便他们可以根据需要筛选性能。 有时候2个查询会让用户更快乐。