窗口函数中ORDER BY是否新增帧?不同写法的计算逻辑问询
窗口函数SUM() OVER()的分组与排序逻辑解析
一、基础分组求和(无ORDER BY)
使用语句:
sum(Age) over(partition by Country) as Total
执行结果:
Id FirstName LastName Age Country Total ----------- --------------- ---------- ----------- --------------- ----------- 5 Betty Doe 28 UAE 28 6 Fred XXX 1 UK 99731 7 Angela XXX 37 UK 99731 8 Dave XXX 2000 UK 99731 9 Tony YYY 9000 UK 99731 10 Tony ZZZ 323 UK 99731 11 Pete ZZZ 88323 UK 99731 3 David Robinson 22 UK 99731 4 John Reinhardt 25 UK 99731 1 John Doe 31 USA 53 2 Robert Luna 22 USA 53
这个逻辑很清晰:按Country将数据分成3个独立分区(UAE、UK、USA),每个分区内计算所有行的Age总和,因此同一分区的所有行Total值完全一致。
二、分组加排序的窗口求和(partition by + order by)
使用语句:
sum(Age) over(partition by Country order by LastName) as Total
执行结果:
Id FirstName LastName Age Country Total ----------- --------------- ---------- ----------- --------------- ----------- 5 Betty Doe 28 UAE 28 4 John Reinhardt 25 UK 25 3 David Robinson 22 UK 47 6 Fred XXX 1 UK 2085 7 Angela XXX 37 UK 2085 8 Dave XXX 2000 UK 2085 9 Tony YYY 9000 UK 11085 10 Tony ZZZ 323 UK 99731 11 Pete ZZZ 88323 UK 99731 1 John Doe 31 USA 31 2 Robert Luna 22 USA 53
这里的核心规则是:当窗口函数的OVER()子句中加入ORDER BY时,默认启用「累积求和」的窗口帧规则——窗口范围是「分区起始行到当前行(及所有与当前行排序键值相同的行)」。
以UK分区为例,按LastName排序后顺序为:Reinhardt → Robinson → XXX → YYY → ZZZ:
- 第一行(Reinhardt):窗口仅包含自身,Total=25
- 第二行(Robinson):窗口包含Reinhardt + Robinson,Total=25+22=47
- 第三到第五行(XXX):这三行
LastName相同,窗口包含前面所有行(Reinhardt+Robinson+所有XXX行),Total=25+22+1+37+2000=2085 - 第六行(YYY):窗口包含前面所有行到YYY,Total=2085+9000=11085
- 第七到第八行(ZZZ):窗口包含所有UK行,Total=11085+323+88323=99731
这看起来类似按Country, LastName分组后累加,但本质是排序后的累积求和,相同排序键的行会共享同一个累积值。
三、多列分组加排序的窗口求和
使用语句:
sum(Age) over(partition by Country, LastName order by FirstName) as Total
执行结果:
Id FirstName LastName Age Country Total ----------- --------------- ---------- ----------- --------------- ----------- 5 Betty Doe 28 UAE 28 4 John Reinhardt 25 UK 25 3 David Robinson 22 UK 22 7 Angela XXX 37 UK 37 8 Dave XXX 2000 UK 2037 6 Fred XXX 1 UK 2038 9 Tony YYY 9000 UK 9000 11 Pete ZZZ 88323 UK 88323 10 Tony ZZZ 323 UK 88646 1 John Doe 31 USA 31 2 Robert Luna 22 USA 22
这个语句的逻辑拆解:
- 先按
Country + LastName组合分区,比如UK的XXX是一个独立分区,UK的ZZZ是另一个独立分区 - 每个分区内按
FirstName排序 - 同样遵循累积求和的窗口帧规则:窗口范围是「当前分区的起始行到当前行」
拿UK的XXX分区举例,按FirstName排序后顺序是Angela → Dave → Fred:
- 第一行(Angela):窗口仅包含自身,Total=37
- 第二行(Dave):窗口包含Angela+Dave,Total=37+2000=2037
- 第三行(Fred):窗口包含Angela+Dave+Fred,Total=2037+1=2038
而UK的ZZZ分区,按FirstName排序后是Pete → Tony:
- 第一行(Pete):Total=88323
- 第二行(Tony):Total=88323+323=88646
和第二个语句的区别在于:第二个语句的分区是Country,排序后累积整个分区内的行;而这个语句的分区是Country+LastName,每个小分区内独立排序累积,因此不会跨LastName求和。
测试代码
-- create CREATE TABLE Customer ( Id int, FirstName varchar(15), LastName varchar(10), Age int, Country varchar(15) ); -- insert INSERT INTO Customer(Id,FirstName,LastName,Age,Country) VALUES (1, 'John', 'Doe', 31, 'USA'); INSERT INTO Customer(Id,FirstName,LastName,Age,Country) VALUES (2, 'Robert', 'Luna', 22, 'USA'); INSERT INTO Customer(Id,FirstName,LastName,Age,Country) VALUES (3, 'David', 'Robinson', 22, 'UK'); INSERT INTO Customer(Id,FirstName,LastName,Age,Country) VALUES (4, 'John', 'Reinhardt', 25, 'UK'); INSERT INTO Customer(Id,FirstName,LastName,Age,Country) VALUES (5, 'Betty', 'Doe', 28, 'UAE'); INSERT INTO Customer(Id,FirstName,LastName,Age,Country) VALUES (6, 'Fred', 'XXX', 1, 'UK'); INSERT INTO Customer(Id,FirstName,LastName,Age,Country) VALUES (7, 'Angela', 'XXX', 37, 'UK'); INSERT INTO Customer(Id,FirstName,LastName,Age,Country) VALUES (8, 'Dave', 'XXX', 2000, 'UK'); INSERT INTO Customer(Id,FirstName,LastName,Age,Country) VALUES (9, 'Tony', 'YYY', 9000, 'UK'); INSERT INTO Customer(Id,FirstName,LastName,Age,Country) VALUES (10, 'Tony', 'ZZZ', 323, 'UK'); INSERT INTO Customer(Id,FirstName,LastName,Age,Country) VALUES (11, 'Pete', 'ZZZ', 88323, 'UK'); SELECT Id, FirstName, LastName, Age, Country, sum(Age) over(partition by Country) as Total --sum(Age) over(partition by Country order by LastName) as Total --sum(Age) over(partition by Country, LastName order by FirstName) as Total FROM Customer
内容的提问来源于stack exchange,提问作者jessejbweld
相关产品推荐
相关产品推荐

