如果我使用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
)。如this小提琴
相反,如果我使用string_agg()
,它只显示名称(没有空逗号或空值),例如here
问题是我在服务器上安装了Postgres 8.4
,而string_agg()
在那里不起作用。 有没有办法让array_agg与string_agg()类似?
答案 0 :(得分:182)
使用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;
答案 1 :(得分:18)
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
答案 2 :(得分:14)
如果您要寻找有关如何从数组中删除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 [](数组),并将引发此错误:
错误:格式不正确的数组文字:“”
答案 3 :(得分:11)
在解决从数组聚合中删除空值的一般问题时,有两种主要方法可以解决问题:执行array_agg(unnest(array_agg(x))或创建自定义聚合。
第一种是above所示的形式:
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)
答案 4 :(得分:7)
我正在添加这个,即使这个线程很老,但我遇到了这个在小数组上运行良好的巧妙技巧。它在Postgres 8.4+上运行,没有额外的库或函数。
string_to_array(array_to_string(array_agg(my_column)))::int[]
array_to_string()
方法实际上摆脱了空值。
答案 5 :(得分: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 FIDDLE 强>
答案 6 :(得分:3)
你应该用 array_remove 包裹你的 array_agg
。
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;
答案 7 :(得分: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;
仅此而已
{Y,N}
答案 8 :(得分:-3)
更大的问题是为什么要同时拉出所有用户/组合。保证您的UI无法处理所有数据。向超大数据添加分页也是一个坏主意。让用户在看到数据之前过滤该集。确保您的JOIN选项集在列表中,以便他们可以根据需要过滤性能。有时,如果两个查询都很快,那么它们会让用户更快乐。