PostgreSQL中是否有类似zip()函数的组合两个数组?
我在PostgreSQL中有两个相同长度的数组值:
{a,b,c}
和{d,e,f}
我想把它们结合起来
{{a,d},{b,e},{c,f}}
有没有办法做到这一点?
Postgres 9.3或更高版本
简单的zip()
考虑下面的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[]);
同样的结果。
zip()到multidimensional array:
现在,如果你想把这个新的数组集合到一个二维数组中,就会变得更加复杂。
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 ...
将全部导致相同的错误信息(使用第9.1.5节进行testing):
错误:找不到数据types为text []的数组types
但是有一个办法可以解决这个问题,就像我们在这个密切相关的问题下所做的那样
创build一个自定义聚合函数:
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}
这可能对其他目的有用。
滚动另一个function:
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}}
Postgres 9.4+
使用ROWS FROM
构造或者更新的unnest()
,它将多个数组并行地取消。 每个可以有不同的长度。 你得到( 每个文件 ):
在这种情况下结果行的数量是最大函数结果的数量,用较小的结果填充空值来匹配。
使用这个更清洁和更简单的变种:
SELECT ARRAY[a,b] AS ab FROM unnest('{a,b,c}'::text[] , '{d,e,f}'::text[]) x(a,b);
Postgres 9.5+
船array_agg(array expression)
:
Function Argument Type(s) Return Type array_agg(expression) any array type same as argument data type Description input arrays concatenated into array of one higher dimension (inputs must all have same dimensionality, and cannot be empty or NULL)
这是在C中实现的自定义聚合函数array_agg_mult()
一个直接替代,这个速度相当快。 用它。
下面是另一种对于不同长度的数组安全的方法,使用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)
如果你想砍掉多余的而不是零填充,只要把>=
length test改为<=
。
这个函数不能处理相当离奇的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)
尽pipe Erwin的代码可以处理这样的数组,甚至可以处理multidimensional array(通过展平它们),但不能处理不同长度的数组。
在PostgreSQL中,数组有点特别,它们对于multidimensional array,可configuration的原始索引等有点太灵活了。
在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);
这将是一个更好的,尤其是如果一个优化扫描function,而不是做一个sorting和join进去。