我在 PostgreSQL 中有两个长度相同的数组值:
{a,b,c}
和{d,e,f}
我想把它们组合成
{{a,d},{b,e},{c,f}}
有没有办法做到这一点?
我在 PostgreSQL 中有两个长度相同的数组值:
{a,b,c}
和{d,e,f}
我想把它们组合成
{{a,d},{b,e},{c,f}}
有没有办法做到这一点?
array_agg
(anyarray
) →anyarray
将所有输入数组连接成一个更高维度的数组。(输入必须具有相同的维度,并且不能为空或 null。)
array_agg_mult()
这是下面演示的我的自定义聚合函数的直接替换。它是用 C 语言实现的,而且速度要快得多。用它。
使用将多个数组并行取消嵌套的ROWS FROM
构造或更新。unnest()
每个都可以有不同的长度。你得到(根据文档):
[...] 在这种情况下,结果行数是最大函数结果的行数,较小的结果用空值填充以匹配。
使用这个更简洁的变体:
SELECT ARRAY[a,b] AS ab
FROM unnest('{a,b,c}'::text[]
, '{d,e,f}'::text[]) x(a,b);
考虑以下 Postgres 9.3 或更早版本的演示:
SELECT ARRAY[a,b] AS ab
FROM (
SELECT unnest('{a,b,c}'::text[]) AS a
, unnest('{d,e,f}'::text[]) AS b
) x;
结果:
ab
-------
{a,d}
{b,e}
{c,f}
请注意,两个数组必须具有相同数量的元素才能并行取消嵌套,否则您将获得交叉连接。
如果您愿意,可以将其包装成一个函数:
CREATE OR REPLACE FUNCTION zip(anyarray, anyarray)
RETURNS SETOF anyarray LANGUAGE SQL AS
$func$
SELECT ARRAY[a,b] FROM (SELECT unnest($1) AS a, unnest($2) AS b) x;
$func$;
称呼:
SELECT zip('{a,b,c}'::text[],'{d,e,f}'::text[]);
结果相同。
现在,如果你想将一组新数组聚合成一个二维数组,它会变得更加复杂。
SELECT ARRAY (SELECT ...)
或者:
SELECT array_agg(ARRAY[a,b]) AS ab
FROM (
SELECT unnest('{a,b,c}'::text[]) AS a
,unnest('{d,e,f}'::text[]) AS b
) x
或者:
SELECT array_agg(ARRAY[ARRAY[a,b]]) AS ab
FROM ...
都会导致相同的错误消息(使用 pg 9.1.5 测试):
错误:找不到数据类型 text[] 的数组类型
但是有一种方法可以解决这个问题,正如我们在这个密切相关的问题下所解决的那样。
创建自定义聚合函数:
CREATE AGGREGATE array_agg_mult (anyarray) (
SFUNC = array_cat
, STYPE = anyarray
, INITCOND = '{}'
);
并像这样使用它:
SELECT array_agg_mult(ARRAY[ARRAY[a,b]]) AS ab
FROM (
SELECT unnest('{a,b,c}'::text[]) AS a
, unnest('{d,e,f}'::text[]) AS b
) x
结果:
{{a,d},{b,e},{c,f}}
注意附加ARRAY[]
层!没有它,只是:
SELECT array_agg_mult(ARRAY[a,b]) AS ab
FROM ...
你得到:
{a,d,b,e,c,f}
这可能对其他目的有用。
滚动另一个函数:
CREATE OR REPLACE FUNCTION zip2(anyarray, anyarray)
RETURNS SETOF anyarray LANGUAGE SQL AS
$func$
SELECT array_agg_mult(ARRAY[ARRAY[a,b]])
FROM (SELECT unnest($1) AS a, unnest($2) AS b) x;
$func$;
称呼:
SELECT zip2('{a,b,c}'::text[],'{d,e,f}'::text[]); -- or any other array type
结果:
{{a,d},{b,e},{c,f}}
这是另一种对不同长度的数组安全的方法,使用 Erwin 提到的数组多重聚合:
CREATE OR REPLACE FUNCTION zip(array1 anyarray, array2 anyarray) RETURNS text[]
AS $$
SELECT array_agg_mult(ARRAY[ARRAY[array1[i],array2[i]]])
FROM generate_subscripts(
CASE WHEN array_length(array1,1) >= array_length(array2,1) THEN array1 ELSE array2 END,
1
) AS subscripts(i)
$$ LANGUAGE sql;
regress=> SELECT zip('{a,b,c}'::text[],'{d,e,f}'::text[]);
zip
---------------------
{{a,d},{b,e},{c,f}}
(1 row)
regress=> SELECT zip('{a,b,c}'::text[],'{d,e,f,g}'::text[]);
zip
------------------------------
{{a,d},{b,e},{c,f},{NULL,g}}
(1 row)
regress=> SELECT zip('{a,b,c,z}'::text[],'{d,e,f}'::text[]);
zip
------------------------------
{{a,d},{b,e},{c,f},{z,NULL}}
(1 row)
如果你想去掉多余的而不是空填充,只需将>=
长度测试<=
改为。
这个函数不能处理相当奇怪的 PostgreSQL 特性,即数组可能有一个非 1 的声明元素,但实际上没有人真正使用该特性。例如,使用零索引的 3 元素数组:
regress=> SELECT zip('{a,b,c}'::text[], array_fill('z'::text, ARRAY[3], ARRAY[0]));
zip
------------------------
{{a,z},{b,z},{c,NULL}}
(1 row)
而 Erwin 的代码确实适用于此类数组,甚至适用于多维数组(通过展平它们),但不适用于不同长度的数组。
数组在 PostgreSQL 中有点特殊,它们对于多维数组、可配置的原点索引等有点过于灵活。
在 9.4 中,您将能够编写:
SELECT array_agg_mult(ARRAY[ARRAY[a,b])
FROM unnest(array1) WITH ORDINALITY as (o,a)
NATURAL FULL OUTER JOIN
unnest(array2) WITH ORDINALITY as (o,b);
这会更好,特别是如果将函数一起扫描而不是进行排序和连接的优化进入。