其他
SQL 窗口函数的优化和执行
什么是窗口函数?
[ PARTITION BY part_list ]
[ ORDER BY order_list ]
[ { ROWS | RANGE } BETWEEN frame_start AND frame_end ] )
PARTITION BY表示将数据先按 part_list
进行分区ORDER BY表示将各个分区内的数据按 order_list
进行排序
ROWS选择前后几行,例如 ROWS BETWEEN 3 PRECEDING AND 3 FOLLOWING
表示往前3行到往后3行,一共7行数据(或小于7行,如果碰到了边界)RANGE选择数据范围,例如 RANGE BETWEEN 3 PRECEDING AND 3 FOLLOWING
表示所有值在[c−3,c+3][c−3,c+3]这个范围内的行,cc为当前行的值
按窗口定义,将所有输入数据分区、再排序(如果需要的话) 对每一行数据,计算它的Frame范围 将 Frame 内的行集合输入窗口函数,计算结果填入当前行
ROW_NUMBER() OVER (PARTITION BY dealer_id ORDER BY sales) AS rank,
AVG(sales) OVER (PARTITION BY dealer_id) AS avgsales
FROM sales
rank
列表示在当前经销商下,该雇员的销售排名;avgsales
表示当前经销商下所有雇员的平均销售额。查询结果如下:| dealer_id | emp_name | sales | rank | avgsales |
+------------+-----------------+--------+------+---------------+
| 1 | Raphael Hull | 8227 | 1 | 14356 |
| 1 | Jack Salazar | 9710 | 2 | 14356 |
| 1 | Ferris Brown | 19745 | 3 | 14356 |
| 1 | Noel Meyer | 19745 | 4 | 14356 |
| 2 | Haviva Montoya | 9308 | 1 | 13924 |
| 2 | Beverly Lang | 16233 | 2 | 13924 |
| 2 | Kameko French | 16233 | 3 | 13924 |
| 3 | May Stout | 9308 | 1 | 12368 |
| 3 | Abel Kim | 12369 | 2 | 12368 |
| 3 | Ursa George | 15427 | 3 | 12368 |
+------------+-----------------+--------+------+---------------+
注:语法中每个部分都是可选的:
如果不指定PARTITION BY,则不对数据进行分区;换句话说,所有数据看作同一个分区 如果不指定ORDER BY,则不对各分区做排序,通常用于那些顺序无关的窗口函数,例如SUM() 如果不指定Frame子句,则默认采用以下的Frame定义:
若不指定ORDER BY,默认使用分区内所有行RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING 若指定了ORDER BY,默认使用分区内第一行到当前值RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
聚合(Aggregate):AVG()、COUNT()、MIN()、MAX()、SUM()... 取值(Value):FIRST_VALUE()、LAST_VALUE()、LEAD()、LAG()... 排序(Ranking):RANK()、DENSE_RANK()、ROW_NUMBER()、NTILE()...
https://drill.apache.org/docs/sql-window-functions-introduction/#types-of-window-functions
注:Frame定义并非所有窗口函数都适用,比如ROW_NUMBER()、RANK()、LEAD()等。这些函数总是应用于整个分区,而非当前 Frame。
窗口函数 VS. 聚合函数
窗口函数的执行
对于整个分区的Frame(例如RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING),只要对整个分区计算一次即可,没什么好说的 对于逐渐增长的Frame(例如RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW),可以用Aggregator维护累加的状态,这也很容易实现 对于滑动的Frame(例如ROWS BETWEEN 3 PRECEDING AND 3 FOLLOWING)相对困难一些。一种经典的做法是要求Aggregator不仅支持增加还支持删除(Removable),这可能比你想的要更复杂,例如考虑下MAX()的实现
窗口函数的优化
AVG(sales) OVER (PARTITION BY dealer_id) AS avgsales ...
窗口函数的并行执行
作者:Eric Fu
来源:https://ericfu.me/sql-window-function/
推荐文章: