列举MySQL对于Where子句的优化

列举MySQL对于Where子句的优化

在MySQL8之前,我们自己额外注意一些查询语句的写法,牺牲了代码的可读性。在MySQL8中,会自动进行类似的优化。可以保持查询的易理解性和可维护性。
这边文章会列举这些优化点。

查询语句优化

  • 删除不必要的括号
   ((a AND b) AND c OR (((a AND b) AND (c AND d))))
-> (a AND b AND c) OR (a AND b AND c AND d)
  • 括号省略
   (a<b AND b=c) AND a=5
-> b>5 AND b=c AND a=5
  • 折叠常量表达式
   (b>=5 AND b=5) OR (b=6 AND 5=5) OR (b=7 AND 5=6)
-> b=5 OR b=6

在MySQL 8.0.14或者更高版本中,这些甚至不是在优化阶段发生的,而是准备阶段。

常量表达式被索引字段使用时,仅被计算一次

常量表达式(Constant Expression),指的是在查询执行期间其值不会改变的表达式。这些表达式可能包括数字、字符串常量或者任何不依赖于表中数据来计算其值的表达式。

假设有一个查询,它的 WHERE 子句中包含了一个如下的条件:

WHERE column_value = 3*4

如果 column_value 是一个被索引的列,那么 3*4 这个常量表达式就会在查询执行之初被计算一次,其结果 12 会被用在索引查找中,而不是对表中的每一行都计算一次 3*4

上面只是一个简单的例子,实际场景中我们没必要写3*4这样的表达式,不过,常量表达式在索引使用上的优势并不限于这种简单的例子。在一些情况下,使用常量表达式可以让查询更灵活,尤其是当表达式涉及到当前日期或动态计算值时。以下是一些在索引上使用常量表达式可能更有必要的例子:

    1. 使用当前日期和时间

假设你想查询所有在过去一周内创建的记录,可以使用如下查询:

WHERE create_date >= CURDATE() - INTERVAL 7 DAY

在这里,CURDATE() - INTERVAL 7 DAY 是一个常量表达式,因为它在查询执行时被计算一次,然后该值在整个查询中保持不变。如果create_date列有索引,这样的查询可以高效利用索引。

    1. 根据计算结果过滤

如果你需要根据某些动态计算的结果来过滤记录,比如查询所有价格在平均价格之上的商品,可以写成:

WHERE price > (SELECT AVG(price) FROM products)

虽然这个例子中子查询的结果不是传统意义上的“常量表达式”,但它在外层查询执行前计算一次,然后作为常量值用于过滤。这种情况下,优化器的行为可能更复杂,但原理相似。

    1. 动态计算的范围查询

考虑一个场景,你需要根据用户输入的某个数值动态计算范围,例如查询某个数值在给定范围内的记录:

WHERE column_value BETWEEN 10 * input_value AND 20 * input_value

这里,input_value是一个外部提供的变量,10 * input_value20 * input_value 构成了基于用户输入动态计算的范围。如果column_value列有索引,且input_value在查询执行前是已知的,则这两个边界值会被视为常量表达式。

    1. 结合函数使用

有时候,查询条件中可能会结合使用函数,如:

WHERE YEAR(create_date) = YEAR(CURDATE())

这个查询检索create_date在当前年份的记录。YEAR(CURDATE())是一个常量表达式,因为它在查询执行时仅被计算一次。虽然直接在索引列上使用函数可能会阻止索引的使用,但这个例子展示了常量表达式在查询中的应用。

常量值超出数据类型范围时,移除或折叠

从 MySQL 8.0.16 版本开始,对数值类型列与常量值进行比较时,会检查并折叠或移除无效或超出范围的值:

# CREATE TABLE t (c TINYINT UNSIGNED NOT NULL);SELECT * FROM t WHERE c ≪ 256;
-SELECT * FROM t WHERE 1;

MyISAMMEMORY引擎中使用COUNT(*)时,如果查询单个表不带WHERE语句,将会直接查询表信息而不是数据行。也适用于任何NOT NULL的列

当对一个单一表执行不带 WHERE 子句的 COUNT(*) 查询时,对于 MyISAM 和 MEMORY 类型的表,查询结果会直接从表的元数据中检索,而不是通过扫描表中的每一行来计算行数。这种优化可以显著提高查询的执行速度,因为直接访问元数据比逐行扫描要快得多。

当使用 COUNT(*) 并且查询中只涉及一个表时,对于任何标记为 NOT NULL 的表达式(即该表达式的结果永远不会为 NULL),MySQL 也会采取同样的优化措施。

这种优化方式仅适用于 MyISAM 和 MEMORY 表类型,并且是在查询不包含 WHERE 子句且只涉及一个表时。对于其他类型的表(如 InnoDB),由于其存储机制的不同,COUNT(*) 操作可能需要实际扫描表来确定行数,因此执行时间会更长。

提前发现无效常量表达式,不执行查询

Early detection of invalid constant expressions. MySQL quickly detects that some SELECT statements are impossible and returns no rows.

MySQL在处理查询时,会提前检测到一些基于常量表达式的查询条件是不可能满足的,因此它会立即返回空结果集,而不是执行无用的查询操作。

比如:

  1. 不可能满足的比较

    SELECT * FROM employees WHERE 1=0;
    

    这里的条件1=0是永远不可能为真的,因此MySQL不会实际执行这个查询,而是直接返回一个空结果集。

  2. 逻辑表达式中包含不可能的条件

    SELECT * FROM employees WHERE salary > 10000 AND salary < 5000;
    

    在这个例子中,salary字段同时满足大于10000和小于5000是不可能的,因此MySQL会识别这个查询为不可能的情况,不执行查询,直接返回空结果集。

  3. 使用常量表达式的结果为FALSE

    SELECT * FROM employees WHERE FALSE;
    

    因为条件永远不可能为真(FALSE就是假),所以MySQL不会执行这个查询,直接返回空结果集。

如果没有使用GROUP BY或者聚合函数的情况下使用HAVING关键字,会和WHERE子句合并处理。

通常,HAVING子句用于对GROUP BY聚合操作的结果进行条件过滤。如果查询中没有使用GROUP BY子句或者聚合函数(如COUNT(), MIN(), MAX(), SUM()等),MySQL会将HAVING子句的条件与WHERE子句的条件合并处理。

假设有一个简单的查询,既没有使用GROUP BY,也没有聚合函数,但是使用了HAVING

SELECT * FROM employees HAVING salary > 5000;

在这个例子中,HAVING子句的条件(salary > 5000)会被MySQL处理为WHERE子句的一部分,就好像这样写:

SELECT * FROM employees WHERE salary > 5000;

这样的处理可以提高查询效率,因为MySQL内部不需要区分WHEREHAVING的逻辑,直接在数据读取时过滤条件不匹配的行。

为每个参与连接的表构造更简单的WHERE子句,尽早跳过行。

MySQL在执行包含连接(JOIN)操作的查询时,优化WHERE子句的处理以提高查询效率。这种优化策略包括两个关键点:

  1. 为每个参与连接的表构造更简单的WHERE子句:在执行连接操作时,MySQL会尝试为每个单独的表构建一个简化版本的WHERE子句。这意味着,即使原始查询中的WHERE子句可能包含多个表的复杂条件,MySQL也会试图为每个表单独提取与之直接相关的条件。这样做的目的是使得在访问表数据时能够更快地评估这些条件。

  2. 尽早跳过行:通过为每个表单独构造简化的WHERE子句,MySQL可以在处理连接操作时更早地确定哪些行不满足条件,从而跳过这些行。这意味着,如果某个表的行不满足其简化WHERE子句的条件,MySQL可以立即排除这些行,而不必等到所有的连接操作完成。这种策略大大减少了不必要的数据处理,提高了查询的整体效率。

例子

假设有两个表employeesdepartments,并且我们想要查询所有在IT部门工作且工资超过5000的员工。原始查询可能如下:

SELECT e.name, e.salary, d.department_name
FROM employees e
JOIN departments d ON e.department_id = d.id
WHERE d.department_name = 'IT' AND e.salary > 5000;

在这个查询中:

  • 对于employees表,MySQL会构造一个简化的WHERE子句,比如e.salary > 5000,以便尽早排除不满足条件的员工记录。
  • 对于departments表,简化的WHERE子句可能是d.department_name = 'IT',用于快速排除非IT部门的记录。

查询中,所有常量表会优先于其他表被读取。

在MySQL中,一个“常量表”指的是满足以下任一条件的表:

  • 空表或只有一行数据的表:这样的表在查询中被视为常量表,因为它们的返回结果是固定的(要么是空,要么是唯一的行)。

  • 使用PRIMARY KEYUNIQUE索引的WHERE子句进行查询的表,其中所有索引部分都与常量表达式进行比较,并且这些索引列都被定义为NOT NULL。这意味着,查询可以直接通过索引定位到特定的行,因此这样的表也被视为常量表。

比如:

SELECT * FROM t WHERE primary_key=1;
SELECT * FROM t1,t2WHERE t1.primary_key=1 AND t2.primary_key=t1.id;

ORDER BYGROUP BY子句中所有的列都来自同一个表时,MySQL查询优化器会优先考虑这个表进行连接。

当执行包含多个表的JOIN查询时,确定哪种连接顺序最有效是一个复杂的问题。MySQL查询优化器会尝试所有可能的表连接组合来找到最佳的执行计划。这个过程考虑了多种因素,包括但不限于:

  • 各个表的大小
  • 可用的索引
  • JOIN条件
  • 查询中的过滤条件等

优化器的目标是最小化查询的总执行时间,这可能包括减少磁盘I/O、减少网络传输开销(在分布式数据库系统中)以及减少CPU处理时间。

ORDER BYGROUP BY子句中所有的列都来自同一个表时,MySQL查询优化器会优先考虑这个表进行连接。这是因为:

  • 如果可以先从这个表开始查询,并且在查询的早期阶段就对数据进行排序或分组,那么可能会减少后续操作中需要处理的数据量。
  • 这种优化特别有用,因为它可以利用索引来快速完成排序和分组操作,从而避免了昂贵的临时表操作或文件排序。
  • 如果ORDER BYGROUP BY可以完全通过一个表的索引来满足,那么这个查询可能会更快地执行,因为数据可以在读取时就已经是排序或分组好的,减少了后续处理的需要。

含有ORDER BYGROUP BY子句查询不同表的字段时,会创建一个临时表来处理查询。

为什么需要临时表?

  • 不同的ORDER BYGROUP BY逻辑:当ORDER BYGROUP BY涉及不同的列时,MySQL需要先根据GROUP BY条件对结果集进行分组,然后再根据ORDER BY条件对这些分组的结果进行排序。由于这两个操作涉及不同的数据处理逻辑,MySQL可能需要使用临时表来存储GROUP BY的中间结果,然后再应用ORDER BY条件进行排序。

  • 涉及多表的列:在连接操作中,如果ORDER BYGROUP BY子句使用了除了连接队列中第一个表以外的表的列,那么MySQL需要在所有相关表的数据都可用后才能执行排序或分组操作。在这种情况下,使用临时表可以存储连接和过滤后的中间结果,以便于之后执行ORDER BYGROUP BY操作。

临时表的影响

  • 性能考虑:使用临时表可能会影响查询的性能,尤其是在处理大量数据时。创建临时表需要额外的磁盘I/O(如果临时表很大而不能完全放在内存中的话)和CPU资源来管理额外的数据处理步骤。

  • 优化策略:了解这一点对于数据库设计和查询优化非常重要。例如,尽可能地让ORDER BYGROUP BY子句使用相同的列,或者确保它们引用的是连接队列中的第一个表的列,可以避免创建临时表,从而提高查询效率。

使用索引,或者全表扫描,优化器的决定。

除非优化器认为使用表扫描更高效,否则会使用最佳索引。曾经,是否使用扫描是基于最佳索引覆盖了超过表中30%的数据来决定的,但现在固定的百分比不再决定使用索引还是扫描。优化器现在更加复杂,它的估计基于额外的因素,如表的大小、行数和I/O块大小。

参考文档

https://dev.mysql.com/doc/refman/8.0/en/where-optimization.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/648387.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

软件测试生命周期

本章简要介绍了软件开发项目中常用的生命周期模型&#xff0c;并解释了测试在每个模型中扮演的角色。它讨论了各种测试级别和测试类型之间的区别&#xff0c;并解释了这些在开发过程中的应用位置和方式。 大多数软件开发项目是按照事先选择的软件开发生命周期模型来计划和执行…

Spring Boot + EasyExcel实现Excel文件导入导出

Java解析、生成Excel比较有名的框架有Apache poi、jxl等&#xff0c;使用者可自行斟酌。 一、 为什么使用 EasyExcel 1.1 内存控制 Apache poi、jxl也能解析Excel&#xff0c;但他们都存在一个问题就是非常的耗内存&#xff0c;poi有一套SAX模式的API可以一定程度的解决一些…

ZK监控方法以及核心指标

文章目录 1. 监控指标采集1.1 zk版本高于3.6.0监控指标采集1.2 zk版本低于3.6.0监控指标采集1.3 配置promethues采集和大盘 2. 核心告警指标3. 参考文章 探讨zk的监控数据采集方式以及需要关注的核心指标&#xff0c;便于日常生产进行监控和巡检。 1. 监控指标采集 3.6.0 版本…

apache 前30个开源项目

由于Apache软件基金会的开源项目 前30个具有代表性的项目 序号项目名称功能描述业务范围活跃度&#xff08;参考性描述&#xff09;1Apache HTTP Server高性能Web服务器提供HTTP服务支持非常活跃2Apache TomcatJava应用服务器部署Java Web应用程序非常活跃3Apache Hadoop分布式…

ORA-12528: TNS: 监听程序: 所有适用例程都无法建立新连

用了网上的办法&#xff1a; 1、修改listener.ora的参数,把动态的参数设置为静态的参数,红色标注部分 位置D:\oracle\product\10.2.0\db_1\NETWORK\ADMIN SID_LIST_LISTENER (SID_LIST (SID_DESC (SID_NAME PLSExtProc) (ORACLE_HOME D:\oracle\produ…

基于PHP反序列化练习

PHP创建一个以自己姓名命名的类&#xff0c;要求存在两个属性&#xff0c;name&#xff0c;age&#xff0c;进行序列化&#xff0c;输出序列化以后的数据。 <!-- PHP创建一个以自己姓名命名的类&#xff0c;要求存在两个属性&#xff0c;name&#xff0c;age --> <?…

【C/C++】C/C++编程——第一个 C++ 程序:HelloWorld

第一个 C 程序&#xff1a;HelloWorld 大家好&#xff0c;我是 shopeeai&#xff0c;也可以叫我虾皮&#xff0c;中科大菜鸟研究生。昨天我们成功搭建好了 C 的开发环境&#xff0c;今天我们来介绍一下第一个 C 程序,打印一个"hello world"。首先我们先贴一下示例代…

《WebKit技术内幕》学习之十三(2):移动WebKit

2 移动化用户界面 HTML5为移动领域做了大量的工作&#xff0c;其中“meta”标签中的众多设置值能够帮助提供非常好的移动用户体验。一个典型的例子就是上面提到的用该标签来控制网页缩放&#xff0c;如示例代码13-2使用了一些JavaScript代码来完成&#xff0c;而实际上&#x…

【FPGA Verilog开发实战指南】初识Verilog HDL-基础语法

这里写目录标题 Verilog HDL简介与VHDL比较 Verilog HDL基础语法逻辑值关键字moduleendmodule 模块名输入信号输出信号既做输入也做输出线网型变量 wire寄存器型变量 reg参数 parameter参数 localparam常量赋值方式阻塞赋值非阻塞赋值 always语句assign 语句 算数运算符归元运算…

【C语言】分支循环语句

分支 if elseif else if else if elseswitch() case default 循环 while 防止死循环for 已知所需循环次数do while 至少执行一次 break 和continue在循环中的应用 break &#xff1a;退出当前循环&#xff1b; continue&#xff1a;推出本次/轮循环 goto 太乱了&#xff0c;不…

C++ Qt day2

自己封装一个矩形类(Rect)&#xff0c;拥有私有属性:宽度(width)、高度(height)&#xff0c; 定义公有成员函数: 初始化函数:void init(int w, int h) 更改宽度的函数:set_w(int w) 更改高度的函数:set_h(int h) 输出该矩形的周长和面积函数:void show() #include <io…

翻译: 使用 GPT-4 将您的 Streamlit 应用程序提升到一个新的水平一

帮助您更快地设计、调试和优化 Streamlit 应用的专业技巧 设计和扩展 Streamlit 应用程序可能是一项艰巨的任务&#xff01;作为开发人员&#xff0c;我们经常面临一些挑战&#xff0c;例如设计良好的 UI、快速调试我们的应用程序以及快速制作它们。 如果有一个工具可以加快速…

Tomcat运维

目录 一、Tomcat简介 二、系统环境说明 1、关闭防火墙&#xff0c;selinux 2、安装JDK 3、安装Tomcat 三、Tomcat目录介绍 1、tomcat主目录介绍 2、webapps目录介绍 3、Tomcat配置介绍&#xff08;conf&#xff09; 4、Tomcat的管理 四、Tomcat 配置管理页面(了解) …

类和对象 第三部分第三小节:const修饰成员函数

一.常函数&#xff1a; &#xff08;一&#xff09;成员函数后面加const后我们成这个函数为常函数 &#xff08;二&#xff09;常函数内不可以修改成员函数属性 额外补充&#xff1a; this指针的本质&#xff0c;是指针常量&#xff0c;指针指向的是不可以修改的 但是指针指向的…

点击查看灯塔工厂

“灯塔工厂”项目由达沃斯世界经济论坛与管理咨询公司麦肯锡合作开展遴选&#xff0c;被誉为“世界上最先进的工厂”&#xff0c;是工业4.0技术应用的最佳实践工厂&#xff0c;代表着全球智能制造的最高水平。目前全球“灯塔工厂”共有 153 座&#xff0c;包含中国 62 座。 做…

响应式Web开发项目教程(HTML5+CSS3+Bootstrap)第2版 例4-11 HTML5 表单验证

代码 <!doctype html> <html> <head> <meta charset"utf-8"> <title>HTML5 表单验证</title> </head><body> <form action"#" method"get" novalidate>请输入您的邮箱:<input type&q…

无限学模式-“科研创新的加速器:全面掌握ChatGPT,推动研究方法和工作模式现代化!“

2023年随着OpenAI开发者大会的召开&#xff0c;最重磅更新当属GPTs&#xff0c;多模态API&#xff0c;未来自定义专属的GPT。微软创始人比尔盖茨称ChatGPT的出现有着重大历史意义&#xff0c;不亚于互联网和个人电脑的问世。360创始人周鸿祎认为未来各行各业如果不能搭上这班车…

小型商用机器人,如何做到小而强?

兼顾体型和性能。 体型和性能的矛盾 一直以来&#xff0c;商用清洁机器人的应用场景主要集中在大型商场、超市、写字楼等&#xff0c;为什么1000平米以下的小型商超等中小场景却很少涉足&#xff1f;原因可以说有很多&#xff0c;但核心为两方面&#xff0c;一方面&#xff0…

工厂模式-策略工厂模式 两者的区别

模拟支付分为 微信支付 &#xff0c;支付宝支付&#xff0c;银联支付 1、工厂模式 &#xff08;1&#xff09;、支付枚举类 import lombok.Getter;/*** author zwh* date 2024/1/26*/ Getter public enum PayEnum {/*** 支付类型*/WX_PAY(1, "微信支付"),ALI_PAY(…

YOLOv8优化策略:注意力涨点系列篇 | 一种轻量级的加强通道信息和空间信息提取能力的MLCA注意力

🚀🚀🚀本文改进:一种轻量级的加强通道信息和空间信息提取能力 MLCA注意力 🚀🚀🚀在YOLOv8中如何使用 1)作为注意力机制使用;2)与c2f结合使用; 🚀🚀🚀YOLOv8改进专栏:http://t.csdnimg.cn/hGhVK 学姐带你学习YOLOv8,从入门到创新,轻轻松松搞定科研…