在 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)。
最佳答案
在 SELECT
中支持设置返回函数是一个 PostgreSQL 扩展,也是一个非常奇怪的 IMO 扩展。它被广泛认为已弃用,最好尽可能避免。
尽可能避免使用 SRF-in-SELECT
现在 LATERAL
在 9.3 中得到支持,两个主要用途之一已经不复存在。如果您想使用一个 SRF 的输出作为另一个 SRF 的输入,那么过去需要在 SELECT
中使用一个集合返回函数; LATERAL
不再需要它。
其他用法将在 9.4 中替换,当添加 WITH ORDINALITY
时,允许您保留 set-returning 函数的输出顺序。这是目前主要的剩余用途:将两个 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)
当前的主要异常(exception)是当您想要在锁步、成对(如
zip
)中运行多个函数时,您目前无法使用连接来执行此操作。WITH ORDINALITY
这将在 9.4 中使用
WITH ORDINALITY
进行改进,虽然它比 SELECT 中的多次 SRF 扫描效率稍低(除非添加了优化器改进),但它会更加理智。假设您想将
1..3
和 10..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)
关于arrays - 为什么这些连接会根据大小不同?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/24214657/