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 行为

开窗函数是啥?

mysql group by 的用法,集合后取出指定的字段

在SELECT语句中,对查询结果进行排序的子句是啥?能消除重复行的关键字是啥?

in在sql中是啥意思

TSQL 分组集(Grouping Sets)