从 Microsoft SQL Server 2012 开始,T-SQL window 函数中名为 "framing" 的功能有什么用

What is the use of a feature called "framing" in T-SQL window function starting from Microsoft SQL Server 2012

阅读有关窗口函数的 T-SQL 书(SQL Server 2012 T-SQL Fundamentals by Itzik Ben-Gan,Microsoft Press)。

显示示例:

USE TSQL2012;

SELECT 
    empid, 
    ordermonth, 
    val,
    SUM(val) OVER (PARTITION BY empid ORDER BY ordermonth
                   rows between unbounded preceding and current row) as runval
FROM 
    Sales.EmpOrders;

尝试忽略(注释掉--)令人困惑的,至少乍一看不是那么清楚,自 Microsoft SQL Server 2012 以来引入的所谓的框架子句,以便查看效果。

T-SQL变成了下面:

select empid, 
       ordermonth, 
       val,
       SUM(val) over(partition BY empid
                     order by ordermonth
                     --rows between unbounded preceding
                     --and current row
                     ) as runval
FROM Sales.EmpOrders;

结果:原来是一样的!

那么像作者给出的示例那样明确指定框架子句有什么意义呢?

框架子句的实际用法是什么,明确使用它是有意义的?或者我在这里有些困惑?

换句话说,它隐式暗示与显式指定框架子句相同的含义,用于注释掉这两行。

我应该首先指出 window/analytic 函数是 ANSI 标准的一部分,并且该规范在支持它们的所有数据库中都非常普遍。实施中没有(或很少?)SQL服务器特定的内容。

当您将 window/analytic 函数与 order by 一起使用时,您隐含地使用了 range between unbounded preceding and current row 的 window 框架,但 row_number() 函数除外( row_number() 的默认值是 rows 而不是 range.

然而,这只是 window 框架的一个例子。以下是其他示例:

获取包括当前值在内的最后三个值的总和:

rows between 2 preceding and current row

获取前一当前和下一个的总和:

rows between 1 preceding and 1 following

要获得三个先前值的总和,不包括当前值:

rows between 3 preceding and 1 preceding

因此,该子句比示例所暗示的要通用得多。

windowing 子句的另一种形式使用 range。这与 rows 处理关系的方式不同。您可以参考documentation了解更多信息。

问题

Results: it turns out to be same!

Then what's the point to explicitly specify the frame clause as given example by author?

回答

  1. 结果不会总是一样的
  2. 性能

当涉及到关系时,您会看到不同之处。

SELECT OrderCol,
       SUM(Val) OVER (ORDER BY OrderCol)
FROM   (VALUES (1, 100),
               (1, 100),
               (2, 100) ) V(OrderCol, Val) 

Returns

+----------+------------------+
| OrderCol | (No column name) |
+----------+------------------+
|        1 |              200 | /*Both OrderCol=1 get 200*/
|        1 |              200 |
|        2 |              300 |
+----------+------------------+

因为默认的 window 框架是 RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW 并且 RANGE 的行为是在 window 框架中包含具有相同 OrderCol 的所有行.

鉴于

SELECT OrderCol,
       SUM(Val) OVER (ORDER BY OrderCol ROWS UNBOUNDED PRECEDING)
FROM   (VALUES (1, 100),
               (1, 100),
               (2, 100) ) V(OrderCol, Val) 

(使用 rows between unbounded preceding and current row 的缩写语法)

Returns

+----------+------------------+
| OrderCol | (No column name) |
+----------+------------------+
|        1 |              100 | /*This is 100*/
|        1 |              200 |
|        2 |              300 |
+----------+------------------+

在排序列没有关系的情况下,结果将相同,但明确指定 ROWS 可以提高性能,因为默认行为是 RANGE 并且使用磁盘上的线轴。

请参阅 this article 了解与此相关的一些性能结果。