title: $MySQL8.0 Window Functions 剖析

author: $马腾

什么是window function

window function 是在满足某种条件的记录集合上执行一个特殊的函数。这一句话,记录集合就是窗口,特殊的函数就是在这个窗口上执行的函数。

SELECT
function_name OVER ( window_definition )
1. FROM (...)

window_definition : 定义要计算的记录的集合。 function_name : 指定对于集合要使用的函数。 接下来我们通过一个简单的例子看一个窗口函数:

SELECTf.id, f.release_year, f.category_id, f.rating,AVG(rating) OVER (PARTITION BY release_year) AS year_avg
FROM films f

这个语句是使用了partition by 作为了集合的定义,即把release year 相同的放在了一起,然后对集合进行 avg() 函数运算。 可以通过这样一个图来较为清晰的看到

ps:下图来源:https://www.jb51.net/article/129447.htm

窗口函数的分类

我个人的理解是窗口函数的分类可以通过两个基准分,一个是窗口大小是否固定,一个是按照函数功能分类

按照窗口大小是否固定划分可以分为:

窗口大小固定:静态窗口函数 窗口大小不固定,不同的记录对应着不同的窗口:动态(滑动)窗口函数

按照功能划分

序号函数:row_number(); rank(); dense_rank(); 分布函数:percent_rank(); cume_dist(); 前后函数:lag(expr, n); // 返回当前行的前 n 行expr 的值; lead(expr, n); // 返回当前行的后n 行expr 的值 头尾函数:first(expr); // 返回第一个 expr 的值; last_value(expr); //返回最后一个expr 的值 其他函数:nth_value(expr, n); // 返回第 n 个expr 的值; ntile(n); // 将有序数据分为n 个桶,记录等级数

窗口集合的定义

窗口函数的基本用法是

函数名 over 子句

over 是定义窗口集合的关键字; 一般有四种定义集合的方法 1. 什么也不写; 这样意味着窗口包含满足where 所有的行,窗口是基于所有的行进行计算。 2. partition 字句: 窗口按照哪些字段进行分组。 3. order by 字句: 按照哪些字段进行排序。 4. frame 字句: frame 是当前分区的一个子集,子句用来定义子集的规则通常用来作为滑动窗口使用。

滑动窗口

滑动窗口有两种指定范围的方式,一种是基于行,一种是基于范围。

基于行

通常使用BETWEEN frame_start AND frame_end语法来表示行范围,frame_start和frame_end可以支持如下关键字,来确定不同的动态行记录, 也可以自己定义行来表示范围

CURRENT ROW 边界是当前行,一般和其他范围关键字一起使用 UNBOUNDED PRECEDING 边界是分区中的第一行 UNBOUNDED FOLLOWING 边界是分区中的最后一行 expr PRECEDING 边界是当前行减去expr的值 expr FOLLOWING 边界是当前行加上expr的值

select avg(amount) over(partition by user_no  ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) as row_num from order_info;

基于范围

有一些范围不是可以直接使用行数表示,这种情况就要用范围,比如窗口范围是一周前的订单开始,截止到当前行; INTERVAL 7 DAY PRECEDING

Hint: 有些函数不管有没有用frame 子句,它的窗口都是固定的,使用这种函数一定是静态窗口函数

cume_dist() dense_rank() lag() lead() ntile() percent_rank() rank() row_number()

mysql8.0 源码分析窗口函数的执行过程

以下内容通过源码角度分析执行过程

优化阶段:

  1. setup windows : 在优化阶段判断出select_lex->m_windows 不为空,就首先调用 Window::setup_windows; 这个方法里面的核心接口是 Window::check_window_functions(THD *thd, SELECT_LEX *select)

a. 这个方法中首先判断的是当前是静态窗口还是动态窗口; 静态窗口即判断了 frame 的定义是否有定义上下边界。m_static_aggregates 为 true, 意味着是静态窗口,同时对每一个分区都可以进行一次评估。

如果 ma_static_aggregates 为 false, 则进一步判断其滑动窗口使用的是基于范围还是基于行。 m_row_optimizable 基于行 m_range_optimizable 基于范围 b. 获取聚合函数作为窗口函数时候窗口的特殊规格要求 wfs->check_wf_semantics(thd, select, &reqs) 这个方法其实就是判断是不是需要 row_buffer 作为评估,什么时候需要使用 row_buffer 呢:如果我们只看当前分区的行无法进行正确的计算,我们需要看之后的或者之前的行,就需要使用row_buffer。

bool Window::check_window_functions(THD *thd, SELECT_LEX *select) {List_iterator<Item_sum> li(m_functions);Item *wf;m_static_aggregates =(m_frame->m_from->m_border_type == WBT_UNBOUNDED_PRECEDING &&m_frame->m_to->m_border_type == WBT_UNBOUNDED_FOLLOWING);// If static aggregates, inversion isn't necessarym_row_optimizable = (m_frame->m_unit == WFU_ROWS) && !m_static_aggregates;m_range_optimizable = (m_frame->m_unit == WFU_RANGE) && !m_static_aggregates;. ..while ((wf = li++)) {Evaluation_requirements reqs;Item_sum *wfs = down_cast<Item_sum *>(wf);if (wfs->check_wf_semantics(thd, select, &reqs)) return true;m_needs_frame_buffering |= reqs.needs_buffer;....
}

  1. Optimize-> make_tmp_tables_info: 这里是看是否需要创建一个临时表作为 window frame buffer. 而是否创建的判断条件就是之前 Window::check_window_functions(THD *thd, SELECT_LEX *select) 接口中 求得的 row_buffer 决定的,如果row_buffer 为 true 则需要创建一个 temp table.
...if (m_windows[wno]->needs_buffering()) {/*Create the window frame buffer tmp table.  We create atemporary table with same contents as the output tmp tablein the windowing pipeline (columns defined bycurr_all_fields), but used for intermediate storage, savingthe window's frame buffer now that we know the window needsbuffering.*/Temp_table_param *par =new (thd->mem_root) Temp_table_param(tmp_table_param);par->m_window = nullptr;  // Only OUT table needs access to WindowList<Item> tmplist(*curr_all_fields, thd->mem_root);TABLE *table =create_tmp_table(thd, par, tmplist, nullptr, false, false,select_lex->active_options(), HA_POS_ERROR, "");if (table == nullptr) DBUG_RETURN(true);if (alloc_ref_item_slice(thd, fbidx)) DBUG_RETURN(true);if (change_to_use_tmp_fields(thd, ref_items[fbidx], tmp_fields_list[fbidx],tmp_all_fields[fbidx], curr_fields_list->elements,*curr_all_fields))DBUG_RETURN(true);m_windows[wno]->set_frame_buffer_param(par);m_windows[wno]->set_frame_buffer(table);}
...

执行阶段

执行调用栈

unit->first_select()->join->exec()->evaluate_join_record()->sub_select_op()->QEP_tmp_table::put_record()->end_write_wf()

重点在 end_write_wf() 这个接口上面

整个window function 的计算是要有两个或者三个temp table 参与的 分别为: Input table: 对应于 qep_tab-1 这个表中是准备进行计算的window 窗口记录 output table: 对用于 qep_tab 这个表是用来写入窗口函数计算结果的 buffer_tmp_table: 如果之前setup 时候判断出需要使用 row_buffer, 那么在优化阶段 make_tmp_tables_info 也会创建一个临时表。

下面简述整个计算过程入下图所示:

在这个过程中比较难理解的是第三个临时表作为 frame buffer 的使用和 process_buffered_windowing_record 从代码中注释给出的一个例子简述。

SUM(A+FLOOR(B)) OVER (ROWS 2 FOLLOWING)

首先要做的事情是先把和 window frame 无关的函数计算都做完,然后把结果放入到 frame buffer 中,在 frame buffer 中判断 是否满足window frame 的集合 row 都已经计算了,(这个就是 process_buffered_windowing_record 做的事情)如果当前的结果不满足 window frame 的定义,我们把结果拿出来,再继续计算。整个过程是一个循环处理,直到最后 窗口函数确实计算完毕了,把结果也放回 frame buffer 中。进而继续计算一些非window function 算子。

  • Tips :process_buffered_windowing_record 这个方法中涉及了两种 move 滑动窗口的策略 分别是 native strategy 和 optimizable strategy; 而具体选择哪一种策略,正是在优化阶段 对 m_row_optimizable 和 m_row_optimizable 的赋值。 if (m_row_optimizable || m_row_optimizable)== true 就选择使用optimizable strategy. 两种策略的滑动方式如代码注释所述。
Moving (sliding) frames can be executed using a naive or optimized strategyfor aggregate window functions, like SUM or AVG (but not MAX, or MIN).In the naive approach, for each row considered for processing from the buffer,we visit all the rows defined in the frame for that row, essentially leadingto N*M complexity, where N is the number of rows in the result set, and M isthe number for rows in the frame. This can be slow for large frames,obviously, so we can choose an optimized evaluation strategy using inversion.This means that when rows leave the frame as we move it forward, we re-usethe previous aggregate state, but compute the *inverse* function to eliminatethe contribution to the aggregate by the row(s) leaving the frame, and thenuse the normal aggregate function to add the contribution of the rows movinginto the frame

mysql8 create table 语法错误_MySQL8.0 Window Function 剖析相关推荐

  1. Oracle创建表语句(Create table)语法详解及示例

    Oracle创建表语句(Create table)语法详解及示例   创建表(Create table)语法详解 1. ORACLE常用的字段类型ORACLE常用的字段类型有 VARCHAR2 (si ...

  2. MySQL 创建表 CREATE TABLE 语法

    MySQL中create table语句的基本语法是: . CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name [(create_definition, ...

  3. mysql create table 语法详解

    create table 可以分成三类 一.一般create table 语句: 1 语法 create [temporary] table [if not exists] tbl_name(crea ...

  4. Mysql8秒级加字段_Mysql8.0秒级加字段

    Mysql 8.0版本合并了腾讯互娱数据库团队的Patch,可以实现秒级添加字段,这个功能可谓是mysql数据库攻城狮的福音,解决了之前5.6,5.7版本添加字段很高的运维成本. 下面是验证mysql ...

  5. MySQL create table语法中的key与index的区别

    在create table的语句中,key和index混淆在一起,官方手册中的解释是这样: KEY is normally a synonym for INDEX. The key attribute ...

  6. Mysql8秒级加字段_MySQL8.0大表秒加字段,是真的吗?

    前言:? 很早就听说 MySQL8.0 支持快速加列,可以实现大表秒级加字段.笔者自己本地也有8.0环境,但一直未进行测试.本篇文章我们就一起来看下 MySQL8.0 快速加列到底要如何操作. 1.了 ...

  7. MySQL8的inodb参数设置_MySQL8.0自适应参数innodb_dedicated_server

    MySQL8.0有了一个新参数又叫自适应参数 innodb_dedicated_server 将innodb_dedicated_server开启的时候,它可以自动的调整下面这四个参数的值: inno ...

  8. mysql8和5.7区别_mysql8.0与mysql5.7安全加密小差别

    今天升级到了mysql8.0 做主从同步遇到下面问题 2020-07-21T14:09:52.626718Z 13 [ERROR] [MY-010584] [Repl] Slave I/O for c ...

  9. mysql8安装版安装教程_MySQL8.0版本安装教程

    这几天被各种环境的配置搞疯了,查询了很多资料,也算有点经验,对于Mysql的安装,以及可能会出现的问题做出解决方案. 我这里讲的是zip的安装教程. 1.进入官网后的界面是: 3.点击进去后,选择第一 ...

最新文章

  1. GPT-3 1750亿参数少样本无需微调,网友:「调参侠」都没的当了
  2. C语言程序练习-L1-019 谁先倒 (15分)
  3. 解决 LINK : fatal error LNK1123: 转换到 COFF 期间失败: 文件无效或损坏
  4. 微软腾讯京东都在高薪招.NET Core,你准备好了吗!
  5. CSS ::selection 选中文字效果
  6. nginx 改变 缺省banner_Nginx隐藏服务器端各类信息的方法
  7. Spring Boot实战解决高并发数据入库: Redis 缓存+MySQL 批量入库
  8. php 赋予变量现在时间,PHP关于变量和日期处理的面试题
  9. C语言学习笔记--数组参数和指针参数
  10. 4款好用流程图软件,都是经验总结出来的
  11. zerglurker的C语言教程001——开发环境搭建
  12. 第八十二章 Caché 函数大全 $ZCSC 函数
  13. cocos creator 绘制闪电特效
  14. HDU 4602 Partition 规律题
  15. python读取bin文件,转成16进制通信协议
  16. C语言中fscanf和fprintf函数的使用
  17. Excel公式-提取字符串中首次出现的数值
  18. python编程求长方体体积_python编程求长方体体积_【Python编程特训连载80】答案公布:圆柱体体积计算...
  19. HCIE证书真的有用吗?
  20. 计算机图形学(七):三维对象的表示(分形图形的基本性质、递归模型、L系统模型)

热门文章

  1. 递归:若函数包含了对其自身的调用,该函数为递归的。《Python核心编程》P305...
  2. 黑马程序员——Java高新技术枚举和自动装箱
  3. 重定义 UIImagePickerController
  4. python 决策树回归参数_python决策树之CART分类回归树详解
  5. Win10: adb: error: remote could not create file(解决)
  6. Win10之广告弹窗终结器:Process Explorer
  7. PCM data flow - 1 - Overview
  8. emacs coding UTF-8 保存报错
  9. Linux搭建Git服务器教程
  10. 非标准硬件控制之增加系统API