在 OVER 子句中使用 ORDER BY

Posted

技术标签:

【中文标题】在 OVER 子句中使用 ORDER BY【英文标题】:Use ORDER BY in OVER clause 【发布时间】:2019-05-06 01:04:02 【问题描述】:

我是 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;

SELECT 
    empid, ordermonth, val,
    SUM(val) OVER(PARTITION BY empid ORDER BY ordermonth) AS runval
FROM 
    Sales.EmpOrders;

输出是一样的:

第二个查询不是应该为每个 empid 产生相同的总值吗?还是ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW是默认的,在over子句中使用order by时是可选的?

【问题讨论】:

这是默认设置(或者可能是RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,但在这种情况下它们是相同的。 【参考方案1】:

对于运行总和(和类似的),当两行之间的ORDER BY ... 存在平局时,差异是可见的。考虑这个例子,员工在2006-09-01 上有两个订单:

DECLARE @T TABLE (empid INT, ordermonth DATE, val INT);
INSERT INTO @T VALUES
(1, '2006-07-01', 100),
(1, '2006-08-01', 100),
(1, '2006-09-01', 100),
(1, '2006-09-01', 100),
(1, '2006-10-01', 100);

SELECT empid, ordermonth, val,
   runval_rows = SUM(val) OVER (PARTITION BY empid ORDER BY ordermonth ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW),
   runval_auto = SUM(val) OVER (PARTITION BY empid ORDER BY ordermonth)
FROM @t

empid | ordermonth | val | runval_rows | runval_auto
1     | 2006-07-01 | 100 | 100         | 100
1     | 2006-08-01 | 100 | 200         | 200
1     | 2006-09-01 | 100 | 300*        | 400*
1     | 2006-09-01 | 100 | 400*        | 400*
1     | 2006-10-01 | 100 | 500         | 500

如果未指定行/范围子句,则 SQL Server 默认为:

如果没有指定 ROWS/RANGE 但指定了 ORDER BY,RANGE UNBOUNDED PRECEDING AND CURRENT ROW 用作窗口的默认值 框架。

用最简单的话来说,SQL Server 将 range* 定义为分区内的一组行,这些行在ORDER BY 子句中指定的列中具有相同的值。因此,第二个变体将第 3 和第 4 视为同一范围的一部分,并在计算运行总和时将它们都包括在内。

* 请注意,此定义与“标准”定义不同,答案仅适用于 SQL Server。

【讨论】:

你能看看这篇文章吗? ***.com/questions/56107566/… 您好,您的回答总是简洁明了。你能看看这个问题吗?感谢一百万dba.stackexchange.com/questions/241509/…【参考方案2】:

如果您希望每个empid 具有相同的值,则不要使用ORDER BY

SELECT empid, ordermonth, val,
       SUM(val) OVER (PARTITION BY empid) AS runval
FROM Sales.EmpOrders;

否则,您的两个表达式是相同的——如果排序键是唯一的。默认在documentation中解释:

如果没有指定 ROWS/RANGE 但指定了 ORDER BY,RANGE UNBOUNDED PRECEDING AND CURRENT ROW 用作窗口的默认值 框架。

【讨论】:

以上是关于在 OVER 子句中使用 ORDER BY的主要内容,如果未能解决你的问题,请参考以下文章

优化 sum() over(order by...) 子句抛出“超出资源”错误

无法在 Oracle 的窗口函数中使用 ORDER BY 子句

ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) 是不是保留订单?

order by 在 POSTGRESQL 中的 partition by 子句中不起作用?

OVER ORDER BY 中的多个列

如何在 over 函数中使用 partition by 和 order by?