0Pricing
SQL Interview Prep · 课时

FIRST_VALUE、LAST_VALUE 与窗口边界

提取边界值,并了解 LAST_VALUE 的窗口陷阱

FIRST_VALUE、LAST_VALUE 与窗口边界 是 CoddyKit 上的免费 SQL Interview Prep 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 SQL Interview Prep 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 SQL Interview Prep 课程共包含 4 节课。

提取边界值

面试官会问:“让每一行都显示其所在组的首个值和末个值。”例如,显示每位用户的首次登录日期,或在每个分区的每一行明细旁显示最新价格。

使用的函数是 FIRST_VALUE 和 LAST_VALUE。它们看起来很简单,但 LAST_VALUE 隐藏着 SQL 中最著名的窗口帧陷阱之一。本课将帮助您可靠地使用这两个函数。

FIRST_VALUE 基础

FIRST_VALUE(col) 会从窗口的第一行返回 col 的值,并将其附加到每一行。按日期排序时,它会为分区中的每一行提供最早的值。

由于默认窗口帧从分区的第一行开始,FIRST_VALUE 通常会按照人们预期的方式运行。

SELECT
  user_id,
  login_date,
  FIRST_VALUE(login_date) OVER (
    PARTITION BY user_id
    ORDER BY login_date
  ) AS first_login
FROM logins;

窗口的默认框架

关键在这里。当您向窗口添加 ORDER BY 时,默认框架是 RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW。

这意味着,每一行的窗口只从分区开头延伸到当前行,而不是延伸到末尾。FIRST_VALUE 不受影响(第一行始终在范围内),但 LAST_VALUE 会受到严重影响。

LAST_VALUE 陷阱

只使用 ORDER BY 运行 LAST_VALUE 时,大多数候选人会预期得到分区的最终值。但由于框架在当前行结束,“框架中的最后一个值”其实只是当前行自身的值。

因此,此查询在每一行返回的都是 login_date 本身,看起来像是出了问题。这是窗口函数中被问到次数最多的一个易错点。

SELECT
  user_id,
  login_date,
  LAST_VALUE(login_date) OVER (
    PARTITION BY user_id
    ORDER BY login_date
  ) AS wrong_last_login
FROM logins;

使用完整框架修复 LAST_VALUE

修复方法是扩大框架,使其覆盖整个分区:ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING。

现在,每一行的窗口都覆盖整个分区,因此 LAST_VALUE 会返回真正的最终值。在面试中请明确说出这一修复方法;这能证明您理解的是框架,而不只是函数名称。

SELECT
  user_id,
  login_date,
  LAST_VALUE(login_date) OVER (
    PARTITION BY user_id
    ORDER BY login_date
    ROWS BETWEEN UNBOUNDED PRECEDING
             AND UNBOUNDED FOLLOWING
  ) AS last_login
FROM logins;

更简单的替代方案

许多工程师会完全避开框架:要获取最后一个值,可以将 FIRST_VALUE 与反向的排序顺序结合使用。

FIRST_VALUE(login_date) OVER (... ORDER BY login_date DESC) 无需框架子句就能返回最新日期。这是一个简洁、易记且值得提及的技巧。

SELECT
  user_id,
  login_date,
  FIRST_VALUE(login_date) OVER (
    PARTITION BY user_id
    ORDER BY login_date DESC
  ) AS last_login
FROM logins;

框架中的 ROWS 与 RANGE

框架有两种形式。ROWS 按物理行计数;RANGE 按相同的 ORDER BY 值进行分组(同值行)。

默认框架使用 RANGE,这就是为什么排序值相同的行会共享框架边界。修复 LAST_VALUE 时,建议使用明确的 ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING,以避免排序值相同时出现意外。

用于任意位置的 NTH_VALUE

除了第一个值和最后一个值之外,NTH_VALUE(col, n) 还可以获取框架中位置为 n 的值,例如价格第二高的值。

它遵循与 LAST_VALUE 相同的框架规则。因此,当您想获取整个分区中的第 n 个值,而不是截至当前行的第 n 个值时,请将它与完整框架结合使用。

SELECT
  product_id,
  price,
  NTH_VALUE(price, 2) OVER (
    PARTITION BY product_id
    ORDER BY price DESC
    ROWS BETWEEN UNBOUNDED PRECEDING
             AND UNBOUNDED FOLLOWING
  ) AS second_highest_price
FROM prices;

实战示例:FIRST_VALUE 与 LAST_VALUE 一起使用

常见的报告会在每笔交易旁显示该客户的第一笔和最后一笔交易金额。请结合两个函数,并记得为 LAST_VALUE 指定明确的框架。

现在,每一行都带有整个分区中的首个值和最后一个值,可以直接用于计算差值或进行标注。

SELECT
  customer_id,
  txn_date,
  amount,
  FIRST_VALUE(amount) OVER w AS first_amt,
  LAST_VALUE(amount) OVER w AS last_amt
FROM transactions
WINDOW w AS (
  PARTITION BY customer_id
  ORDER BY txn_date
  ROWS BETWEEN UNBOUNDED PRECEDING
           AND UNBOUNDED FOLLOWING
);

命名窗口保持 DRY

请注意,前一个查询使用了 WINDOW w AS (...) 子句,并两次引用了 OVER w。只定义一次窗口,可以避免重复冗长的框架规范,也能防止两个函数逐渐产生差异。

大多数主流数据库都支持命名窗口。当多个列共享一个窗口时,使用命名窗口是一种简洁的做法,面试官会对此表示认可。

实战示例:首笔到末笔的差值

常见的后续问题是:客户从第一笔到最后一笔交易的变化是多少。每一行都有两个边界值后,将它们相减;如有需要,再去重,使每位客户只保留一行。

这将完整框架的修复与简单算术结合起来,正是面试官希望看到的、清晰组装出的端到端答案。

SELECT DISTINCT
  customer_id,
  LAST_VALUE(amount) OVER w - FIRST_VALUE(amount) OVER w AS first_to_last_delta
FROM transactions
WINDOW w AS (
  PARTITION BY customer_id
  ORDER BY txn_date
  ROWS BETWEEN UNBOUNDED PRECEDING
           AND UNBOUNDED FOLLOWING
);

快速检查

经典的 LAST_VALUE 易错点。

回顾

边界值函数的关键在于框架:

  • FIRST_VALUE 在默认框架下可以正常工作;LAST_VALUE 则不行。
  • 默认框架在当前行结束,因此请使用 ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING 修复 LAST_VALUE,或者反转排序顺序并使用 FIRST_VALUE。
  • NTH_VALUE(col, n) 可以获取任意位置的值;命名窗口让多列规范保持 DRY。

至此,LAG、LEAD、NTILE 和边界值工具集就介绍完了。

常见问题解答

「FIRST_VALUE、LAST_VALUE 与窗口边界」课时是免费的吗?

是的 — 「FIRST_VALUE、LAST_VALUE 与窗口边界」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 SQL Interview Prep 课程的其余内容,请升级到 CoddyKit PRO。 SQL Interview Prep 课程共包含 4 节课。

「FIRST_VALUE、LAST_VALUE 与窗口边界」这节课中我会学到什么?

提取边界值,并了解 LAST_VALUE 的窗口陷阱 你通过在浏览器中直接运行的动手代码来练习 SQL Interview Prep,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 SQL Interview Prep 需要有经验吗?

无需任何先前经验。CoddyKit 上的 SQL Interview Prep 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。

「FIRST_VALUE、LAST_VALUE 与窗口边界」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 SQL Interview Prep 课中编写并运行代码吗?

能。每节 SQL Interview Prep 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 使用 LAG 和 LEAD 获取相邻行
  2. 期间环比变化
  3. 使用 NTILE 分桶
  4. FIRST_VALUE、LAST_VALUE 与窗口边界
← 返回 SQL Interview Prep