SELECT 子句中多个集合返回函数的预期行为是啥?
Posted
技术标签:
【中文标题】SELECT 子句中多个集合返回函数的预期行为是啥?【英文标题】:What is the expected behaviour for multiple set-returning functions in SELECT clause?SELECT 子句中多个集合返回函数的预期行为是什么? 【发布时间】:2017-02-13 06:44:43 【问题描述】:我试图通过两个集合返回函数的结果获得“交叉连接”,但在某些情况下我没有获得“交叉连接”,请参见示例
行为1:当集合长度相同时,从每个集合中逐项匹配
postgres=# SELECT generate_series(1,3), generate_series(5,7) order by 1,2; 生成系列 |生成系列 -----------------+----------------- 1 | 5 2 | 6 3 | 7 (3 行)行为 2:当集合长度不同时,它会“交叉连接”集合
postgres=# SELECT generate_series(1,2), generate_series(5,7) order by 1,2; 生成系列 |生成系列 -----------------+----------------- 1 | 5 1 | 6 1 | 7 2 | 5 2 | 6 2 | 7 (6 行)我想我不明白这里的某些东西,有人可以解释一下加速的行为吗?
另一个例子,更奇怪:
postgres=# SELECT generate_series(1,2) x, generate_series(1,4) y order by x,y; x |是的 ---+--- 1 | 1 1 | 3 2 | 2 2 | 4 (4 行)我正在寻找标题中问题的答案,最好带有文档链接。
【问题讨论】:
我的建议:不要这样做。将它们放在from
子句中。
我已经这样做了,但我正在寻找优化路径。 (将两个子查询 WHERE 合二为一)。调查了一下,我想出了这个问题。您是说没有预期的行为吗?
【参考方案1】:
Postgres 10 或更新版本
为较小的集合添加空值。使用generate_series()
进行演示:
SELECT generate_series( 1, 2) AS row2
, generate_series(11, 13) AS row3
, generate_series(21, 24) AS row4;
第 2 行 |第 3 行 |第 4 行
-----+------+-----
1 | 11 | 21
2 | 12 | 22
空 | 13 | 23
空 | 空 | 24
dbfiddle here
The manual for Postgres 10:
如果查询的选择中有多个返回集合的函数 列表,该行为类似于您将 函数合并到单个
LATERAL ROWS FROM( ... )
FROM
子句项中。为了 来自底层查询的每一行,都有一个输出行使用 每个函数的第一个结果,然后是使用第二个函数的输出行 结果等等。如果某些返回集合的函数产生 输出比其他输出少,用空值代替缺失的 数据,以便为一个基础行发出的总行数 与产生最多的集合返回函数相同 输出。因此,集合返回函数“同步”运行,直到它们 都用完了,然后执行下一个 底层行。
这结束了传统上奇怪的行为。
Postgres 9.6 或更早版本
结果行数(有点令人惊讶!)是同一SELECT
列表中所有集合的最小公倍数。 (只有在所有集合大小都没有公约数的情况下,才会像 CROSS JOIN
一样工作!)演示:
SELECT generate_series( 1, 2) AS row2
, generate_series(11, 13) AS row3
, generate_series(21, 24) AS row4;
第 2 行 |第 3 行 |第 4 行
-----+------+-----
1 | 11 | 21
2 | 12 | 22
1 | 13 | 23
2 | 11 | 24
1 | 12 | 21
2 | 13 | 22
1 | 11 | 23
2 | 12 | 24
1 | 13 | 21
2 | 11 | 22
1 | 12 | 23
2 | 13 | 24
dbfiddle here
在manual for Postgres 9.6 the chapter SQL Functions Returning Sets 中记录,以及避免它的建议:
注意:在 select 中使用 set-returning 函数的关键问题 列表,而不是
FROM
子句,是放置多个 在同一个选择列表中设置返回函数的行为不是很 明智地。 (如果你这样做,你实际上得到的是一些输出 行数等于行数的最小公倍数 由每个集合返回函数产生。)LATERAL
语法产生 调用多个集合返回函数时结果不那么令人惊讶, 并且通常应该被使用。
我的大胆强调。
单个集合返回函数是可以的(但在FROM
列表中仍然更干净),但现在不鼓励在同一个SELECT
列表中使用多个。在我们加入 LATERAL
之前,这是一个有用的功能。现在它只是历史的镇流器。
相关:
Parallel unnest() and sort order in PostgreSQL Unnest multiple arrays in parallel What is the difference between LATERAL JOIN and a subquery in PostgreSQL?【讨论】:
【参考方案2】:the documentation 中有关于该问题的唯一说明。我不确定这是否解释了所描述的行为。也许更重要的是,这种函数用法已被弃用:
目前,返回集合的函数也可以在查询的选择列表中调用。对于查询自己生成的每一行,调用返回集的函数,并为函数结果集的每个元素生成一个输出行。但请注意,此功能已被弃用,可能会在未来的版本中删除。
【讨论】:
【参考方案3】:我找不到这方面的任何文档。但是,我可以描述我观察到的行为。
每个集合生成函数都返回一个有限行数。 Postgres 似乎会运行集合生成函数,直到 所有 它们都在最后一行 - 或者,更有可能在所有返回到它们的第一行时停止。从技术上讲,这将是系列长度的最小公倍数 (LCM)。
我不确定为什么会这样。而且,正如我在评论中所说,我认为通常将函数放在from
子句中会更好。
【讨论】:
以上是关于SELECT 子句中多个集合返回函数的预期行为是啥?的主要内容,如果未能解决你的问题,请参考以下文章
SELECT 子句中不存在聚合函数时的 GROUP BY 行为