【问题标题】:Why doesn't LAST_VALUE return the last value?为什么 LAST_VALUE 不返回最后一个值?
【发布时间】:2016-05-07 21:21:59
【问题描述】:

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

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

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

(这种情况下MAX可以代替LAST_VALUE求最大值,但为什么LAST_VALUE也不返回最大值呢?)

【问题讨论】:

    标签: google-bigquery


    【解决方案1】:

    您的其他选择是将查询顺序更改为 desc

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

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

    【讨论】:

      【解决方案2】:

      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
      

      在回答这个问题之前,这里先介绍一下分析函数(又名窗口函数)的背景知识。以下所有内容都是标准 SQL,并非特定于 BigQuery。

      首先,分析函数不是聚合函数。聚合函数将多个输入行折叠成单个输出行,而分析函数为每个输入行精确计算一个输出行。因此,您需要确保您正在考虑 每个 输入行的输出。

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

      现在,回到LAST_VALUE。尽管上面描述的默认窗口在许多情况下是合理的(例如,计算累积和),但它在 LAST_VALUE 上的效果非常糟糕。 LAST_VALUE函数返回窗口最后一行的值,默认窗口最后一行就是当前行。

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

      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 返回 1、2、3、4 而不仅仅是 4,因为每个输入行的窗口都会发生变化。

      现在让我们指定一个作为整个分区的窗口:

      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
      

      现在我们得到 4 个 LAST_VALUE 符合预期。

      【讨论】:

      • 感谢您的解释。这让我难倒了好久。我通过 DESC 解决方法找到了 FIRST_VALUE,但不明白为什么 LAST_VALUE 不起作用。
      • 很高兴它有帮助! :-)
      • 太棒了。你的回答太完整了。非常感谢。
      【解决方案3】:

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

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

      如果不涉及表中的其他字段 - 我只会做简​​单的 GROUP BY:

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

      当然,我们应该记住,最大值和最大值并不总是相同的。

      【讨论】:

      • 我提出并回答了这个问题,因为我们经常收到客户投诉,用户认为他们在 LAST_VALUE 中发现了错误,我认为将其作为 SO 的常见问题解答会很有用。我已经修改了问题,因此 MAX 不是有效答案,并且我在自己的答案中添加了注释。感谢您的来信!
      • 当然,这正是我的想法。我不确定用户的问题到底是什么,因为措辞有点模糊:o)
      • @MikhailBerlyant “..我们应该记住并不总是..” - 当我将链接附加到我认为您所指的内容时,我是否直言不讳? stackoverflow.com/questions/9398457/…
      猜你喜欢
      • 2020-02-07
      • 2021-12-27
      • 2021-05-24
      • 1970-01-01
      • 2020-09-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多