postgresql 标量子查询中的窗口函数未按预期工作

sxpgvts3  于 2023-06-22  发布在  PostgreSQL
关注(0)|答案(1)|浏览(124)

以下查询可用于计算累计总和:

select 
  (select sum(c) over (ROWS UNBOUNDED PRECEDING) as a),
  sum(c) over (ROWS UNBOUNDED PRECEDING ) as b 
from (select unnest(ARRAY[1, 2, 3, 3, 4, 5]) as c) x

b给出了预期的结果,而a没有。
| 一种|B|
| - -----|- -----|
| 1| 1|
| 2| 3|
| 3|六|
| 3|九个|
| 4|十三个|
| 5个|十八岁|
我在Google和Stackoverflow上搜索过,但没有找到类似的问题。
以下是我的问题:

  • 为什么简单的select Package 器会导致完全不同的结果?
  • 是否可以将子查询保留在SELECT列表中,同时仍使窗口函数按预期工作?
lc8prwob

lc8prwob1#

为什么简单的select Package 器会导致完全不同的结果?
输出列a相关子查询的结果,该查询对底层派生表x生成的每一行运行一次。这些计算中的每一个都只能看到计算它的单行。所以总和总是与单个值相同。
另一方面,输出列b基于完整的派生表x。所以你可以看到多行的实际总和。
是否可以将子查询保留在SELECT列表中,同时仍使窗口函数按预期工作?

**否。**除非您从相关子查询中的基础关系x重新联接到其他行。您必须在CTE中生成x才能重用它(或使用实际的表),这将是一个奇怪的、昂贵的构造,而且很可能是无稽之谈。(使用情况是什么?)

虽然没有在窗口框架中添加显式的ORDER BY,但b也不完全可靠。简单的查询不会中断。但如果你做更多的事情(比如添加连接等),它可能会。参见:

  • 带有元素编号的PostgreSQL unnest()

unnest()本身也可以作为“表功能”。不需要额外的 Package 。考虑:

SELECT sum(c) OVER (ORDER BY ord ROWS UNBOUNDED PRECEDING) AS b
FROM   unnest(ARRAY[1, 2, 3, 3, 4, 5]) WITH ORDINALITY x(c, ord);

注意,这是按照数组中的原始 * 位置 * 排序的。您的示例在这方面是不明确的,因为数组具有严格升序的值,这些值在按 value 排序时恰好排序相同。我想你关心的是“位置”。
旁白:窗口函数的默认框架选项是RANGE UNBOUNDED PRECEDING,所以(逻辑上)将其更改为ROWS UNBOUNDED PRECEDING,而排序顺序具有对等值排序。如果窗口框架中没有ORDER BY,则 all 行都是对等行。
这在我的查询中并不重要(逻辑上),因为它是按“有序性”排序的--具有确定性的排序顺序,没有对等体。
但它仍然影响性能。ROWS更快,因为它不需要检查对等体。Postgres 16在这方面包括了实质性的性能改进。但是sum()不受影响,因为RANGEROWS可以产生实际差异。当您知道排序顺序是明确的时,切换到ROWS仍然是值得的,就像我的查询中一样。参见:

相关问题