为什么 LAST_VALUE return 不是最后一个值?

Why doesn't LAST_VALUE return the last value?

我想使用如下查询在有序分区上找到 y 的最后一个值:

SELECT
  x,
  LAST_VALUE(y) OVER (PARTITION BY x ORDER BY y ASC)
FROM table

但是 LAST_VALUE returns 很多值不是给定分区的 y 的最后一个值(在本例中为最大值)。为什么?

(在这种情况下,可以用MAX代替LAST_VALUE求最大值,但是为什么LAST_VALUE return也不是最大值?)

TLDR:你要查询的是:

SELECT
  x,
  LAST_VALUE(y) OVER (PARTITION BY x ORDER BY y ASC
    ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
FROM table

可能后跟 GROUP BY 以折叠来自分析函数的重复输出行。

当然,如果您只需要在无序分区上使用 MAX 会更简单:

SELECT
  x,
  MAX(y) OVER (PARTITION BY x)
FROM table

在回答这个问题之前,先了解一下解析函数的背景知识(a.k.a。window 函数)。以下所有内容都是标准的 SQL,并非特定于 BigQuery。

首先,解析函数不是聚合函数。聚合函数将多个输入行合并为一个输出行,而分析函数为每个输入行准确计算一个输出行。因此,您需要确保考虑 每个 输入行的输出。

其次,分析函数对 "window" 行进行操作,这些行是该行所属的 "partition" 行的子集。输入行的分区由 PARTITION BY 子句确定,或者如果您希望分区是整个输入行集,则可以省略它。 window 由 ROWS 子句给出,但如果您不指定它(用户通常不指定),它默认为整个分区(未应用排序时)或分区中从第一行到当前行的行集(当存在 ORDER BY 时)。请注意,分区中每个输入行的 window 可能不同!

现在,回到 LAST_VALUE。虽然上面描述的默认 window 在许多情况下是合理的(例如,计算累积和),但它在 LAST_VALUE 下的效果非常差。 LAST_VALUE 函数 returns 中最后一行的值 window,默认情况下 window 中的最后一行是当前行。

因此,要解决此问题,您需要明确指定 LAST_VALUE 的 window 是整个分区,而不仅仅是当前行之前的行。您可以这样做:

SELECT x, LAST_VALUE(y) OVER (PARTITION BY x ORDER BY y ASC
  ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
FROM table

为了对此进行测试,这里有一个例子:

SELECT
  x,
  FIRST_VALUE(x) OVER (ORDER BY x ASC) first_asc,
  FIRST_VALUE(x) OVER (ORDER BY x DESC) first_desc,
  LAST_VALUE(x) OVER (ORDER BY x ASC) last_asc,
  LAST_VALUE(x) OVER (ORDER BY x DESC) last_desc,
FROM
  (SELECT 4 as x),
  (SELECT 2 as x),
  (SELECT 1 as x),
  (SELECT 3 as x)

x,first_asc,first_desc,last_asc,last_desc
1,1,4,1,1
2,1,4,2,2
3,1,4,3,3
4,1,4,4,4

请注意 LAST_VALUE returns 1, 2, 3, 4 而不仅仅是 4,因为每个输入行的 window 都会发生变化。

现在让我们指定一个 window 即整个分区:

SELECT
  x,
  FIRST_VALUE(x) OVER (ORDER BY x ASC
    ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) first_asc,
  FIRST_VALUE(x) OVER (ORDER BY x DESC
    ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) first_desc,
  LAST_VALUE(x) OVER (ORDER BY x ASC
    ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) last_asc,
  LAST_VALUE(x) OVER (ORDER BY x DESC
    ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) last_desc,
FROM
  (SELECT 4 as x),
  (SELECT 2 as x),
  (SELECT 1 as x),
  (SELECT 3 as x)

x,first_asc,first_desc,last_asc,last_desc
1,1,4,4,1
2,1,4,4,1
3,1,4,4,1
4,1,4,4,1

现在我们如预期的那样 LAST_VALUE 得到 4。

即使该问题标题也使用 LAST_VALUE - 问题本身要求 Largest Value!
我会选择以下内容:

SELECT x, MAX(y) OVER (PARTITION BY x) FROM table  

如果不涉及 table 中的其他字段 - 我只会进行简单的 GROUP BY:

SELECT x, MAX(y) FROM table GROUP BY x 

当然,我们应该记住,Largest value 和 MAX value 并不总是一回事。

您可以选择将查询顺序更改为 desc

SELECT
  x,
  LAST_VALUE(y) OVER (PARTITION BY x ORDER BY y ASC)
FROM table
order by x desc

但是您只会得到第一行的最后一个值