为什么这些根据大小加入不同?

时间:2014-06-13 22:35:30

标签: arrays postgresql

在Postgresql中,如果您不需要两个相同大小的数组,它们将一个数组中的每个值与另一个数组中的一个排成一行,但如果两个数组的大小不同,则它将每个值与每个值连接起来从另一个。

select unnest(ARRAY[1, 2, 3, 4, 5]::bigint[]) as id,
unnest(ARRAY['a', 'b', 'c', 'd', 'e']) as value

将返回

1 | "a"
2 | "b"
3 | "c"
4 | "d"
5 | "e"

但是

select unnest(ARRAY[1, 2, 3, 4, 5]::bigint[]) as id, -- 5 elements
unnest(ARRAY['a', 'b', 'c', 'd']) as value -- 4 elements
order by id

将返回

1 | "a"
1 | "b"
1 | "c"
1 | "d"
2 | "b"
2 | "a"
2 | "c"
2 | "d"
3 | "b"
3 | "d"
3 | "a"
3 | "c"
4 | "d"
4 | "a"
4 | "c"
4 | "b"
5 | "d"
5 | "c"
5 | "b"
5 | "a"

这是为什么?我假设正在使用某种隐式规则,并且我想知道我是否可以明确地执行它(例如,如果我想要第二种样式,当我有匹配的数组大小,或者我想要在一个数组中缺少值时视为NULL)。

1 个答案:

答案 0 :(得分:4)

SELECT中对集合返回函数的支持是PostgreSQL扩展,IMO非常奇怪。它被广泛认为已被弃用,并尽可能避免使用。

尽可能避免使用SRF-in - SELECT

现在9.3中支持LATERAL,其中一个主要用途已经消失。如果你想使用一个SRF的输出作为另一个SRF的输入,那么在SELECT中使用set-returns函数是必要的。 LATERAL不再需要。

当添加WITH ORDINALITY时,其他用法将在9.4中被替换,允许您保留set-returns函数的输出顺序。这是目前剩余的主要用途:将两个SRF的输出压缩成匹配值对的行集。 WITH ORDINALITY最期待unnest,但适用于任何其他SRF。

为什么奇怪的输出?

PostgreSQL在这里使用的逻辑(对于它最初在古代历史中引入的任何IMO疯狂的原因)是:每当任一函数产生输出时,发出一行。如果只有一个函数产生了输出,则再次扫描另一个函数的输出以获得所需的行。如果两者都没有产生输出,则停止发射行。

使用generate_series更容易看到。

regress=> SELECT generate_series(1,2), generate_series(1,2);
 generate_series | generate_series 
-----------------+-----------------
               1 |               1
               2 |               2
(2 rows)

regress=> SELECT generate_series(1,2), generate_series(1,3);
 generate_series | generate_series 
-----------------+-----------------
               1 |               1
               2 |               2
               1 |               3
               2 |               1
               1 |               2
               2 |               3
(6 rows)

regress=> SELECT generate_series(1,2), generate_series(1,4);
 generate_series | generate_series 
-----------------+-----------------
               1 |               1
               2 |               2
               1 |               3
               2 |               4
(4 rows)

在大多数情况下,你真正想要的是两者的简单交叉连接,这是非常理智的。

regress=> SELECT a, b FROM generate_series(1,2) a, generate_series(1,2) b;
 a | b 
---+---
 1 | 1
 1 | 2
 2 | 1
 2 | 2
(4 rows)

regress=> SELECT a, b FROM generate_series(1,2) a, generate_series(1,3) b;
 a | b 
---+---
 1 | 1
 1 | 2
 1 | 3
 2 | 1
 2 | 2
 2 | 3
(6 rows)

regress=> SELECT a, b FROM generate_series(1,2) a, generate_series(1,4) b;
 a | b 
---+---
 1 | 1
 1 | 2
 1 | 3
 1 | 4
 2 | 1
 2 | 2
 2 | 3
 2 | 4
(8 rows)

当你想要以锁定步骤,​​成对方式(如zip)运行多个函数时,当前的主要异常是当前无法对连接执行的。

WITH ORDINALITY

这将在9.4中使用WITH ORDINALITY进行改进,而广告虽然效率低于SELECT中的多次SRF扫描(除非添加了优化程序改进),但这样做会更加精明。

假设您希望将1..310..40与多余元素的空值配对。使用with ordinality(仅限PostgreSQL 9.4):

regress=# SELECT aval, bval 
           FROM generate_series(1,3) WITH ORDINALITY a(aval,apos) 
           RIGHT OUTER JOIN generate_series(1,4) WITH ORDINALITY b(bval, bpos) 
           ON (apos=bpos);

 aval | bval 
------+------
    1 |    1
    2 |    2
    3 |    3
      |    4
(4 rows)

当srf-in-from改为返回时:

regress=# SELECT generate_series(1,3) aval, generate_series(1,4) bval;
 aval | bval 
------+------
    1 |    1
    2 |    2
    3 |    3
    1 |    4
    2 |    1
    3 |    2
    1 |    3
    2 |    4
    3 |    1
    1 |    2
    2 |    3
    3 |    4
(12 rows)