MySQL 8.0 官方文档 第八章 优化(二十一)—— 窗口函数优化

目录

第八章 优化(二十一)—— 窗口函数优化

8.2 优化SQL语句

8.2.1 优化 SELECT 语句

8.2.1.21 窗口函数优化

【译者:本节涉及到SQL高级函数——窗口函数,这里做简单的介绍,这些内容是搜索而来,不是官方文档上的:

窗口函数,也叫OLAP函数(Online Anallytical Processing,联机分析处理),可以对数据库数据进行实时分析处理。

窗口函数的基本语法如下:

	<窗口函数()> over (partition by <用于分组的列名>
	                order by <用于排序的列名>) as 别名

窗口函数有以下两类函数:

  • 专用窗口函数,包括rank, dense_rank, row_number等函数。例如:

    select *,
       rank() over (partition by 班级
                     order by 成绩 desc) as ranking
    from 班级表
    

    结果如下:(rank函数用于排名)
    在这里插入图片描述

  • 聚合函数,如sum. avg, count, max, min

    因为窗口函数是对WHERE或者GROUP BY子句处理后的结果集进行操作,所以窗口函数原则上只能写在SELECT子句中。

    为什么叫“窗口”函数呢?这是因为partition by分区后的结果称为“窗口”,这里的窗口不是我们家里的门窗,而是表示“区域”的意思。

    参考网页:https://zhuanlan.zhihu.com/p/92654574

窗口函数(Window functions)会影响优化器考虑的策略:

  • 如果子查询中使用了窗口函数,则派生于子查询的表合并将被禁止。因为子查询总是物化的(materialized)。

  • 半连接不适用于窗口函数优化,因为半连接适用于WHEREJOIN ... ON中的子查询,而它不能包含窗口函数。

  • 优化器按顺序处理具有相同排序要求的多个窗口,因此第一个窗口之后的窗口的排序可以跳过。

  • 优化器不尝试合并可以在单个步骤中计算的窗口(例如,当多个OVER子句包含相同的窗口定义时)。解决方法是在window子句中定义窗口,并在OVER子句中引用该窗口名称。

没有作为窗口函数使用的聚合函数将在最外层可能的查询中要求聚合。例如,在以下查询中,MySQL发现,因为COUNT(t1.b)放在WHERE子句中,因此它不能存在于外部查询中:

SELECT * FROM t1 
		WHERE t1.a = (SELECT COUNT(t1.b) FROM t2);

因此,MySQL在子查询中进行聚合,将t1.b视为常量并返回t2表的行数。

【译者:进行测试:

CREATE TABLE t1(
	a INT,
	b INT
);

CREATE TABLE t2(
	a INT
);

INSERT t1(a, b) VALUES(1, 2),(3, 4),(4, 5),(5, 6);
INSERT t2(a) VALUES(1), (2), (3), (4);

SELECT * FROM t1 
		WHERE t1.a = (SELECT COUNT(t1.b) FROM t2);   # 子查询的结果是4,是t2表的行数!与t1.b没有关系。

+-----+-----+
|  a  |  b  | 
+-----+-----+
|  4  |  5  |
+-----+-----+

如果将WHERE替换为HAVING将导致一个错误:

mysql> SELECT * FROM t1 
				HAVING t1.a = (SELECT COUNT(t1.b) FROM t2);
ERROR 1140 (42000): In aggregated query without GROUP BY, expression #1
of SELECT list contains nonaggregated column 'test.t1.a'; this is
incompatible with sql_mode=only_full_group_by

# 译者对以上进行翻译:
# 错误 1140 (42000):在没有分组的聚合查询中,选择列表中包含的第一个表达式是非聚合列'test.t1.a';
# 这是与sql_mode=only_full_group_by(sql模式=只能完全分组)不兼容。

发生错误的原因是:HAVING子句中可以存在COUNT(t1.b),所以要求外部查询也要使用聚合。

窗口函数(包括用作窗口函数的聚合函数)没有前面的那么复杂。它们总是在书写它们的子查询中进行聚合,永远不会在外部查询中进行聚合。

窗口函数的计算可能会受到windowing_use_high_precision(窗口使用高精度)系统变量的影响,该变量决定是否在不损失精度的情况下进行窗口计算操作。默认情况下,该变量是启用的。

对于某些移动帧的聚合,可以应用逆向聚合函数从聚合中移除值。这可以提高性能,但可能会降低精度。例如,将一个非常小的浮点值添加到一个非常大的值上会导致这个非常小的值被大的值“隐藏”。当以后反转该大值时,这个小值的影响就消失了。

由于逆向聚合而导致的精度损失,仅仅是对浮点(近似值)数据类型进行操作的一个因素。对于其他类型,逆向聚合是安全的,包括有小数部分的 DECIMAL类型,它是一种精确的值类型。

为了更快地执行,在安全的情况下,MySQL总是使用逆向聚合:

  • 对于浮点值,逆向聚合并不总是安全的,可能会导致精度损失。默认是避免逆向聚合的,这样执行速度较慢,但保留了精度。如果允许为速度而牺牲安全性,则可以禁用windowing_use_high_precision以允许逆向聚合。

  • 对于非浮点数据类型,逆向聚合始终是安全的,并且无论windowing use high precision值是多少都可以使用。

  • windowing_use_high_precisionMIN()MAX()没有影响,因为它们在任何情况下都不会使用逆向聚合。

对于方差函数STDDEV_POP()、STDDEV_SAMP()、VAR_POP()、VAR_SAMP()及其同义词的计算,可以在优化模式或默认模式下进行计算。而优化模式可能在最后的有效数字中产生略微不同的结果。如果允许这样的差异,可以禁用windowing_use_high_precision,以允许优化模式。

对于EXPLAIN,窗口执行计划信息过于宽泛,无法以传统输出格式显示。要查看窗口信息,请使用EXPLAIN FORMAT=JSON并查找窗口元素。

上一集 MySQL 8.0 官方文档 第八章 优化(二十)—— 函数调用优化

下一集 MySQL 8.0 官方文档 第八章 优化(二十二)—— 行构造器表达式优化和避免全表扫描


版权声明:本文为qq_39785418原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。