在PostgreSQL中是否有类似两个数组的zip()函数?

时间:2012-09-13 21:03:18

标签: sql arrays postgresql

我在PostgreSQL中有两个相同长度的数组值:

{a,b,c}{d,e,f}

我希望将它们合并到

{{a,d},{b,e},{c,f}}

有办法吗?

2 个答案:

答案 0 :(得分:34)

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()到多维数组:

现在,如果您想将聚合这些新数组集合到一个 2-dimensionaltional 数组中,它会变得更加复杂。

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页测试):

  

错误:找不到数据类型text []

的数组类型

但正如我们在this closely related question下制定的那样,有一种解决方法  创建自定义聚合函数:

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}}

Postgres 9.4 +

使用ROWS FROM构造或更新的unnest()并行取消多个阵列。每个都可以有不同的长度。你得到(per documentation):

  

[...]在这种情况下,结果行的数量是最大函数的数量   结果,用较小的值填充较小的结果以匹配。

使用这种更简洁的变体:

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()的直接替代,速度要快得多。使用它。

答案 1 :(得分:7)

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

这将更好,特别是如果进行优化以一起扫描函数而不是进行排序和连接。