You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

Presto窗口函数OVER子句中ROWS BETWEEN与RANGE BETWEEN的差异

PrestoSQL 0.172/Athena引擎1中ROWS vs RANGE窗口函数深度解析

本文针对旧版本PrestoSQL(尤其是亚马逊Athena引擎1基于的0.172版本,以及引擎2基于的0.217版本),这些版本的窗口函数ROWS/RANGE相关问题已在Trino(原PrestoSQL)346及以后版本修复。

核心问题解答

1. ROWS BETWEEN与RANGE BETWEEN是同义词吗?有何核心差异?

PrestoSQL 0.172/Athena引擎1版本中,当仅使用UNBOUNDEDCURRENT ROW作为边界时,二者的表现看起来完全一致——返回结果没有区别。但它们本质是不同的概念:

  • ROWS是基于物理行的数量来划定窗口范围
  • RANGE是基于排序键的数值范围来划定窗口范围

只不过在这个旧版本中,RANGE的功能被大幅阉割了,仅支持UNBOUNDEDCURRENT ROW,导致它的行为和ROWS在这些场景下完全重合。

2. 为什么ROWS BETWEEN支持更多选项?

这是旧版本PrestoSQL的功能限制问题。在这个版本中,RANGE的实现不支持基于数值偏移的边界(比如1 PRECEDING1 FOLLOWING),仅允许UNBOUNDED PRECEDINGCURRENT ROWUNBOUNDED FOLLOWING这几个选项;而ROWS的实现是完整的,支持任意数值的前后偏移(比如5 PRECEDING2 FOLLOWING),以及各种组合。

你可以试试这段代码,RANGE会报错,但ROWS能正常执行:

-- 报错的RANGE语句
use tpch.tiny;
select custkey, orderdate, array_agg(orderdate) over (
 partition by custkey
 order by orderdate asc
 range between 1 preceding and 1 following
) previous_orders
from orders
where custkey in (419, 320) and orderdate < date('1996-01-01')
order by custkey, orderdate asc;

报错信息:

ERROR: Window frame RANGE PRECEDING is only supported with UNBOUNDED

换成ROWS则能正常返回结果:

-- 正常执行的ROWS语句
use tpch.tiny;
select custkey, orderdate, array_agg(orderdate) over (
 partition by custkey
 order by orderdate asc
 rows between 1 preceding and 1 following
) previous_orders
from orders
where custkey in (419, 320) and orderdate < date('1996-01-01')
order by custkey, orderdate asc;

3. 是否存在相同参数下,二者返回不同结果的场景?

在这个旧版本中,当仅使用UNBOUNDEDCURRENT ROW时,二者返回的结果完全一致。比如:

  • range between unbounded preceding and current rowrows between unbounded preceding and current row 返回的聚合结果完全相同
  • range between current row and unbounded following 和对应的ROWS语句结果也一致

但如果是在支持完整RANGE功能的新版本Trino中,当排序键存在重复值时,二者会有明显差异:RANGE会把所有排序键值等于当前行的行都包含进窗口,而ROWS只按物理行数量计算。不过在PrestoSQL 0.172/Athena引擎1中,因为RANGE不支持偏移量,所以不存在这种场景。

4. 仅使用UNBOUNDED/CURRENT ROW时,是否需要优先选择RANGE或ROWS?

在这个旧版本中,二者行为完全一致,性能上也没有明显差异,所以选哪个都可以。不过考虑到ROWS支持更多场景,如果你以后需要扩展窗口范围(比如添加偏移量),直接修改ROWS的参数即可,不需要替换关键字,所以优先选ROWS会更灵活。

5. 既然ROWS支持更多选项,为何官方文档完全未提及?

这确实是旧版本PrestoSQL的文档疏漏。从代码来看,ROWS的解析器和测试用例都是存在的,但文档没有同步更新。这种情况在开源项目的早期版本中偶尔会出现——功能先实现了,但文档没跟上。

补充测试示例汇总

RANGE可用场景(仅支持UNBOUNDED/CURRENT ROW)

  1. range between unbounded preceding and current row
    返回当前行及之前所有行的聚合:
custkey | orderdate | previous_orders
---------+------------+--------------------------------------------------------------------------
320 | 1992-07-10 | [1992-07-10]
320 | 1992-07-30 | [1992-07-10, 1992-07-30]
320 | 1994-07-08 | [1992-07-10, 1992-07-30, 1994-07-08]
...
  1. range between current row and unbounded following
    返回当前行及之后所有行的聚合:
custkey | orderdate | previous_orders
---------+------------+--------------------------------------------------------------------------
320 | 1992-07-10 | [1992-07-10, 1992-07-30, 1994-07-08, 1994-08-04, 1994-09-18, 1994-10-12]
320 | 1992-07-30 | [1992-07-30, 1994-07-08, 1994-08-04, 1994-09-18, 1994-10-12]
...
  1. range between unbounded preceding and unbounded following
    返回分区内所有行的聚合:
custkey | orderdate | previous_orders
---------+------------+--------------------------------------------------------------------------
320 | 1992-07-10 | [1992-07-10, 1992-07-30, 1994-07-08, 1994-08-04, 1994-09-18, 1994-10-12]
320 | 1992-07-30 | [1992-07-10, 1992-07-30, 1994-07-08, 1994-08-04, 1994-09-18, 1994-10-12]
...

ROWS扩展场景(支持偏移量)

  1. rows between 1 preceding and 1 following
    返回当前行、前1行、后1行的聚合:
custkey | orderdate | previous_orders
---------+------------+--------------------------------------
320 | 1992-07-10 | [1992-07-10, 1992-07-30]
320 | 1992-07-30 | [1992-07-10, 1992-07-30, 1994-07-08]
320 | 1994-07-08 | [1992-07-30, 1994-07-08, 1994-08-04]
...
  1. rows between current row and 1 following
    返回当前行和后1行的聚合:
custkey | orderdate | previous_orders
---------+------------+--------------------------
320 | 1992-07-10 | [1992-07-10, 1992-07-30]
320 | 1992-07-30 | [1992-07-30, 1994-07-08]
...
  1. rows between 5 preceding and 2 preceding
    返回当前行前5行到前2行的聚合(不足则返回NULL):
custkey | orderdate | previous_orders
---------+------------+--------------------------------------------------
320 | 1992-07-10 | NULL
320 | 1992-07-30 | NULL
320 | 1994-07-08 | [1992-07-10]
...

内容的提问来源于stack exchange,提问作者mbafford

火山引擎 最新活动