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

dan*_*dan 21 sql arrays postgresql

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

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

我想把它们结合起来

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

有没有办法做到这一点?

Erw*_*ter 36

Postgres 9.3或更早

简单拉链()

考虑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;
Run Code Online (Sandbox Code Playgroud)

结果:

  ab
-------
 {a,d}
 {b,e}
 {c,f}
Run Code Online (Sandbox Code Playgroud)

请注意,两个数组必须具有相同数量的并行排除的元素,或者您获得交叉连接.

如果你想:你可以将它包装成一个函数:

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$;
Run Code Online (Sandbox Code Playgroud)

呼叫:

SELECT zip('{a,b,c}'::text[],'{d,e,f}'::text[]);
Run Code Online (Sandbox Code Playgroud)

结果相同.

zip()到多维数组:

现在,如果你想聚集新的阵列集成一个2-dimenstional阵列,它变得更加复杂.

SELECT ARRAY (SELECT ...)
Run Code Online (Sandbox Code Playgroud)

要么:

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
Run Code Online (Sandbox Code Playgroud)

要么:

SELECT array_agg(ARRAY[ARRAY[a,b]]) AS ab
FROM  ...
Run Code Online (Sandbox Code Playgroud)

将导致相同的错误消息(使用第9.1.5页测试):

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

但是,正如我们在这个密切相关的问题下制定的那样,有一种解决方法.
创建自定义聚合函数:

CREATE AGGREGATE array_agg_mult (anyarray) (
    SFUNC    = array_cat
   ,STYPE    = anyarray
   ,INITCOND = '{}'
);
Run Code Online (Sandbox Code Playgroud)

并像这样使用它:

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
Run Code Online (Sandbox Code Playgroud)

结果:

{{a,d},{b,e},{c,f}}
Run Code Online (Sandbox Code Playgroud)

注意附加ARRAY[]层!没有它,只是:

SELECT array_agg_mult(ARRAY[a,b]) AS ab
FROM ...
Run Code Online (Sandbox Code Playgroud)

你得到:

{a,d,b,e,c,f}
Run Code Online (Sandbox Code Playgroud)

这对其他目的可能有用.

滚动另一个功能:

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$;
Run Code Online (Sandbox Code Playgroud)

呼叫:

SELECT zip2('{a,b,c}'::text[],'{d,e,f}'::text[]); -- or any other array type
Run Code Online (Sandbox Code Playgroud)

结果:

{{a,d},{b,e},{c,f}}
Run Code Online (Sandbox Code Playgroud)

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);
Run Code Online (Sandbox Code Playgroud)

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)
Run Code Online (Sandbox Code Playgroud)

这是我array_agg_mult()在C中实现的自定义聚合函数的直接替代,它的速度要快得多.用它.

  • @CraigRinger:对。不过,出于这个问题的目的,OP 定义了“相同长度”的数组。 (2认同)
  • 是的,只需确保已注明,因为其他人可能找不到。我个人将其用PL / PgSQL编写并添加完整性检查。 (2认同)

Cra*_*ger 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)
Run Code Online (Sandbox Code Playgroud)

如果你想砍掉多余的而不是空填充,只需改变>=长度测试即可<=.

这个函数不能处理相当离奇的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)
Run Code Online (Sandbox Code Playgroud)

wheras欧文的代码与这样的阵列的工作,甚至与多维阵列(通过压平它们)但并没有与不同长度的数组.

数组在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);
Run Code Online (Sandbox Code Playgroud)

这会更好,特别是如果一起优化扫描功能而不是进行排序和连接.