MySQL派生条件下推优化导致自定义变量结果错误问题分析

MySQL核心点笔记 条件查询 -- 1、关系运算符:> < >= <= = <> (也可以用!=,是一种宽容处理,不建议) select count(last_name),last_name,first_name FROM employees WHERE salary<>20000 -- 聚集函数只能显示一行 -- 2、按逻辑表达式筛选:and or -- 3、范围表达式:between and,not between and -- 4、确定集合:in,not in --... 阅读详情

MySQL派生条件下推优化导致自定义变量结果错误问题分析

1. 问题描述

现场 MySQL 8.0.25 版本运行中,发现带有用户变量的SQL执行结果集与预期不符。复现场景如下:

-- 起锅烧油,连接 MySQL 8.0.25 数据库
mysql -uroot -S /var/lib/mysql/mysql-8.0.25-linux-x86_64/mysql-test/var/tmp/mysqld.1.sock -p
 
-- 执行以下SQL
SELECT version();
CREATE database testdb;
use testdb
CREATE TABLE t1(c1 int);
INSERT INTO t1 VALUES(100),(200),(300),(400),(500);
SELECT * FROM (
  SELECT c1, (@rownum_r:=@rownum_r+1) AS r 
  FROM t1, (SELECT @rownum_r:=0) AS b
) AS q WHERE q.c1=300;   -- 这里查询到的结果是300  1,实际应该是300  3

分析上述的执行结果中的 rownum 返回1,可以推断派生表返回的条数少于预期,导致 rownum 值偏小。由此想到看下优化器改写后的SQL

mysql> explain SELECT * FROM (SELECT c1, (@rownum_r := @rownum_r + 1) AS r FROM  t1, (SELECT @rownum_r := 0) AS b) AS q WHERE q.c1 = 300;
+----+-------------+------------+------------+--------+---------------+------+---------+------+------+----------+----------------+
| id | select_type | table      | partitions | type   | possible_keys | key  | key_len | ref  | rows | filtered | Extra          |
+----+-------------+------------+------------+--------+---------------+------+---------+------+------+----------+----------------+
|  1 | PRIMARY     | <derived2> | NULL       | ALL    | NULL          | NULL | NULL    | NULL |    2 |   100.00 | NULL           |
|  2 | DERIVED     | <derived3> | NULL       | system | NULL          | NULL | NULL    | NULL |    1 |   100.00 | NULL           |
|  2 | DERIVED     | t1         | NULL       | ALL    | NULL          | NULL | NULL    | NULL |    5 |    20.00 | Using where    |
|  3 | DERIVED     | NULL       | NULL       | NULL   | NULL          | NULL | NULL    | NULL | NULL |     NULL | No tables used |
+----+-------------+------------+------------+--------+---------------+------+---------+------+------+----------+----------------+
4 rows in set, 3 warnings (0.01 sec)

mysql> SHOW warnings;
+---------+------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Level   | Code | Message                                                                                                                                                                                                       |
+---------+------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
.......
| Note    | 1003 | /* select#1 */ select `q`.`c1` AS `c1`,`q`.`r` AS `r` from (/* select#2 */ select `testdb`.`t1`.`c1` AS `c1`,(@rownum_r:=((@`rownum_r`) + 1)) AS `r` from `testdb`.`t1` where (`testdb`.`t1`.`c1` = 300)) `q` |
+---------+------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

印证了上面的推断,优化器将 WHERE 条件下推到派生表中,减少了派生表返回行数,以此加快 SQL 执行速度。但是引起了 rownum 递增减少问题。

由此可知这么来看,属于优化器的 Bug,而在 Bug home 中也找到了现象匹配的问题

  • Derived condition pushdown rewrite ignores user variables https://bugs.mysql.com/bug.php?id=104918

2. 问题分析

2.1 什么是派生条件下推

引自 10.2.2.5 Derived Condition Pushdown Optimization 的相关描述:

对于 SELECT * FROM (SELECT i, j FROM t1) AS dt WHERE i > constant 这种子查询语句,大多情况下可以将外层的where条件下推到派生表内部,改写为 SELECT * FROM (SELECT i, j FROM t1 WHERE i > constant) AS dt,来减少派生表返回条数,达到提升执行效率的作用。

比如以下场景,就很适用 WHERE 条件下推:

  • 当派生表不使用聚合或窗口函数时,可以直接向下推送外部WHERE条件。这包括具有多个与AND、OR或两者结合的谓词的WHERE条件。比如 SELECT * FROM (SELECT f1, f2 FROM t1) AS dt WHERE f1 < 3 AND f2 > 11 改写为 SELECT f1, f2 FROM (SELECT f1, f2 FROM t1 WHERE f1 < 3 AND f2 > 11) AS dt
  • 当派生表具有 GROUP BY 并且不使用窗口函数时,引用一个或多个不属于GROUP BY的列的外部WHERE条件可以作为HAVING条件下推到派生表中。比如 SELECT * FROM (SELECT i, j, SUM(k) AS sum FROM t1 GROUP BY i, j) AS dt WHERE sum > 100 被改写为SELECT * FROM (SELECT i, j, SUM(k) AS sum FROM t1 GROUP BY i, j HAVING sum > 100) AS dt.
  • 当派生表使用GROUP BY并且外部WHERE条件中的列是GROUP BY列时,引用这些列的WHERE条件可以直接下推到派生表中。比如 SELECT * FROM (SELECT i,j, SUM(k) AS sum FROM t1 GROUP BY i,j) AS dt WHERE i > 10 被改写为 SELECT * FROM (SELECT i,j, SUM(k) AS sum FROM t1 WHERE i > 10 GROUP BY i,j) AS dt.

derived_condition_pushdown 开关在 optimizer_switch 参数中设置,也可以使用相关hint控制在语句级生效。方法如下:

-- 开启:
SET optimizer_switch="derived_condition_pushdown=on";
SELECT /*+ DERIVED_CONDITION_PUSHDOWN() */ ... FROM ...

-- 关闭:
SET optimizer_switch="derived_condition_pushdown=off";
SELECT /*+ NO_DERIVED_CONDITION_PUSHDOWN() */ ... FROM ...

2.2 Bug 影响范围有多大

经过查阅 releasenote,可知条件下推是在 8.0.22 中引入的,修复的版本是 8.0.28

-- 8.0.22的发布日志:
MySQL now implements derived condition pushdown for eligible queries. What this means is that, for a query such as SELECT * FROM (SELECT i, j FROM t1) AS dt WHERE i > constant, it is now possible in many cases to push the outer WHERE condition down to the derived table, in this case resulting in SELECT * FROM (SELECT i, j FROM t1 WHERE i > constant) AS dt. Previously, if the derived table was materialized and not merged, MySQL materialized the entire table—in this case t1—then qualified the rows with the WHERE condition.

-- 8.0.28的修复日志:
Documented fix as follows in the MySQL 8.0.28 changelog:

    When a condition was pushed down, the result of evaluating
    assignments to user variables in the SELECT list of the subquery
    were sometimes affected. For this reason, we now prevent
    condition pushdown for statements with assignments to user
    variables.

3. 业务规避措施

  1. 升级到MySQL 8.0.28及以上版本,或者GreatSQL分支版本 https://gitee.com/GreatSQL/GreatSQL/releases/tag/GreatSQL-8.0.32-27。
  2. 数据库实例级别关闭该优化,set optimizer_switch="derived_condition_pushdown=off";
  3. 在涉及的SQL语句上添加 hint /*+ NO_DERIVED_CONDITION_PUSHDOWN() */ ,语句级别禁用derived_condition_pushdown 优化,可以规避 MySQL Bug 104918。

参考文章

  1. MySQL 8.0.22 releasenote:https://dev.mysql.com/doc/relnotes/mysql/8.0/en/news-8-0-22.html
  2. Derived condition pushdown rewrite ignores user variables https://bugs.mysql.com/bug.php?id=104918
  3. Derived Condition Pushdown Optimization https://dev.mysql.com/doc/refman/8.4/en/derived-condition-pushdown-optimization.html
  4. GreatSQL分支版本 https://gitee.com/GreatSQL/GreatSQL/releases/tag/GreatSQL-8.0.32-27
MySQL数据分析实战:从SQL查询到业务洞察的完整指南 1. 先搞清楚“MySQL数据分析”到底要解决什么问题 很多人一看到“MySQL数据分析”这个标题,第一反应是去背SQL命令,或者急着安装MySQL。这其实把顺序搞反了。这个主题的核心,是解决一个非常具体的问题: 如何把存储在数据库里的原始数据,变成能支持决策的信息。 它不是一个单纯的数据库管理课程,也不是一个纯理论的数据科学讲座。它的价值在于 连接 :连接数据库操作(增删改查)和业务分析(趋势、洞察、报告)。如果你只会写SQL但不知道业务想问什么,或者你懂业务但数据拿不出来,这个技能缺口就是“MySQL数 阅读详情

相关推荐

RK3588与银河麒麟V10 SP1 2403深度适配:国产化硬件与操作系统的完美融合

本文深入探讨了RK3588处理器与银河麒麟V10 SP1 2403操作系统的深度适配,展示了国产化硬件与操作系统的完美融合。通过性能验证和实际应用测试,证明了这套组合在多媒体处理、AI推理和日常办公中的卓越表现,实现了完全自主可控的计算平台。

weixin_29194255的博客 249

MySQL 8.0 官方文档 第八章 优化(二十八)—— 派生的条件下推优化

目录第八章 优化(二十八)—— 派生条件下推优化8.2 优化SQL语句8.2.2 优化子查询、派生表、视图引用和公共表表达式8.2.2.5 派生条件下推优化 第八章 优化(二十八)—— 派生条件下推优化 8.2 优化SQL语句 8.2.2 优化子查询、派生表、视图引用和公共表表达式 8.2.2.5 派生条件下推优化 Mysql 8.0.22及更高的版本支持为合适的子查询进行派生条件下推。对于诸如 SELECT * FROM (SELECT i, j FROM t1) AS dt WHERE i >

HZ_Samuel博客 727

1553B总线介绍及其硬件方案设计

军用1553B总线 1.简介 简单介绍可参考(https://blog.csdn.net/ScilogyHunter/article/details/108444231)博主写的很详细。 1553B总线是MIL-STD-1553总线的简称,其中B就是BUS,MIL-STD-1553B总线是飞机内部时分制命令/响应式多路复用数据总线。1553B数据总线标准是20世纪70年代由美国公布的一种串行多路数据总线标准。1553B总线能挂31个远置终端,1553B总线采用指令/响应型通信协议,它有三种终端类型: BC:

小幽余生不加糖 1万+

Oceanbase查询改写:查询下推

默认情况下,当查询语句中存在视图时,需要等待视图中的查询执行完毕后再执行父查询。为此,Oceanbase中定义了查询下推规则,能够将满足条件的父查询下推合并到视图查询中,从而消除视图,提升查询性能。

weixin_64863780的博客 552

气象大模型光伏功率预测中的应用:从短期,超短期,中长期的实现与开源代码详解

本文详细介绍了基于大模型的光伏功率预测方法,涵盖超短期(15分钟-1小时)、短期(1-6小时)和中长期(1天-1周)预测。文章首先讨论了数据准备与预处理,包括历史功率、气象、天文和电站特性数据的收集与特征工程。随后,针对不同预测需求,提出了多种深度学习模型:超短期预测采用1DCNN-LSTM混合模型,短期预测使用Transformer模型,中长期预测则通过多尺度特征提取模型和概率预测方法处理不确定性。文章还提供了模型训练、评估、部署和优化的完整流程,并开源了代码实现。未来研究方向包括结合数值天气预报、开发时

专注AI大模型,软件混淆,授权 1573

openGauss学习笔记-280 openGauss性能调优-实际调优案例09-修改启动参数解决TPCC大幅度波动

openGauss学习笔记-280 openGauss性能调优-实际调优案例09-修改启动参数解决TPCC大幅度波动280.1 现象描述280.2 优化分析 openGauss学习笔记-280 ope

超哥的博客 309

mysql8.0.0以上的 版本,列名为rank,会语法不通过

near rank

D_Boat的博客 1444

YOLOv11改进 | Conv/卷积篇 | 利用Hype-YOLO提出MANet增强YOLOv11特征提取能力

本文给大家带来的最新改进机制是由Hype-YOLO提出的MANet结构,其是一种C3、C2f、C3k2的升级版,用于增强特征提取能力。MANet可以理解为C2f的升级版,其整合了DSConv和C2f结构,这些模块的组合使得信息流动更加丰富,有助于在训练过程中提高梯度流动,从而增加基础特征的语义深度。官方发布的数据中HypeYOLO的精度是高于YOLOv11的,本文内容包含MAnet替换C3k2的教程以及原理介绍。欢迎大家订阅我的专栏一起学习YOLO,购买专栏读者联系读者入群获取进阶项目文件!

Snu77的博客 2161

mysql的关键字问题

MySQL关键字冲突

weixin_44001317的博客 382

【SpringBoot 3.x 第51节】带你玩转 GraalVM Native Image:启动快到飞起,内存省到心疼,你还不试试?

🏆本文收录于《滚雪球学SpringBoot 3》,专门攻坚指数提升,本年度国内最系统+最专业+最详细(永久更新)。    本专栏致力打造最硬核 SpringBoot3 从零基础到进阶系列学习内容,🚀均为全网独家首发,打造精品专栏,专栏持续更新中…欢迎大家订阅持续学习。 如果想快速定位学习,可以看这篇【SpringBoot3教程导航帖】,你想学习的都被收集在内,快速投入学习!!两不误。    若还想学习更多,可直接前往《滚雪球学SpringBoot(全版本合集)》,涵盖SpringBoot所有版本教学文章

**My Coding Family** 1304

XCP详解「3.1·ASAP2新建A2L文件」

后面是ASAP2新建A2L的操作步骤,如果是APE中新建的A2L就无需以下操作,APE只需要导入现成的A2L即可。退出点击保存时会提示保存A2L文件,是因为前面导出的是1.70版本,工程最低1.71,可以另外起个名字即可。导入elf文件,elf路径建议直接索引编译生成的Debug下的,防止编译后忘记更新。导出V1.70格式A2L文件,APE17支持V1.70。通过ASAP2生成需要A2L文件更常用些。同一个信号可以创建不同属性量。ASAP2中新建A2L文件。设置标定信号数量最大值。XCP详解「总目录」

光明磊学习&积累的地方 3060

别再用子查询了!MySQL 8.0 窗口函数:一行代码搞定“组内排名”

告别变量,拥抱标准。MySQL 8.0 的窗口函数是后端开发的必备技能。用用RANK()用(推荐)

郑龙飞 722

SAP 系统中的数字签名解决方案及应用案例分析

在当前企业信息化及数字化转型的大背景下,数据安全与身份认证显得尤为重要。数字签名技术作为保障数据真实性和完整性的重要手段,在 SAP 系统中得到了广泛应用。本文将详细探讨 SAP 系统中部分应用场景如何利用数字签名技术满足业务需求,涵盖 SAP Single Sign-On、质量管理、过程工业生产计划以及 SAP ArchiveLink 内容服务器等方面,力求通过深入解读,帮助读者更好地理解和应用这一关键技术。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 527

MySQL查询结果自动生成序号的5种方法

在数据库操作中,结果集排序与序号生成是数据处理的基础需求。通过窗口函数、会话变量等技术手段,可以在SQL查询层面实现高效的行号标记,避免全量数据传输到应用层的性能损耗。ROW_NUMBER()等窗口函数利用数据库引擎的优化能力,在排序计算时能有效利用索引提升性能。实际业务场景如金融对账、成绩排名等都需要保持序号的连续性和可追溯性,特别是在分页查询时要求全局序号一致。本文详解的会话变量方案兼容MySQL全版本,而临时表技巧则适合处理千万级大数据量,这些方法配合适当的索引设计,能显著提升查询效率。

weixin_34021089的博客 440

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

MySQL查询结果动态生成序号的5种实现方案

在数据库查询中,动态生成行号是常见的业务需求,特别是在数据展示和报表导出场景。SQL中的行号功能可以帮助开发者快速标识数据位置,实现分页排序等核心功能。从技术实现看,MySQL提供了多种生成序号的方法,包括用户变量自增、窗口函数ROW_NUMBER()等。其中用户变量方案兼容MySQL各版本,通过变量初始化和自增实现简单计数;而MySQL 8.0引入的窗口函数则遵循SQL标准,支持PARTITION BY分组编号。这些技术在用户管理系统、数据分析平台等场景都有广泛应用,特别是结合索引优化后,能有效提升大数据

weixin_33462927的博客 244

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

MySQL查询结果添加序号的五种实用方案与性能对比

在数据库查询中,为结果集添加序号是常见的需求,尤其在数据分析和报表生成场景。SQL通过变量自增、窗口函数等技术实现行号生成,其核心原理是利用会话变量或分析函数进行计数。从技术价值看,正确的序号生成方案能显著提升查询效率,特别是在处理大数据量时。MySQL 8.0+的窗口函数方案符合SQL标准且性能优越,而用户变量方案则具有更好的版本兼容性。实际应用中需要根据MySQL版本、数据量大小和排序需求选择合适方案,例如用户变量适合MySQL 5.6+环境,窗口函数则在分组排序场景表现更佳。合理使用这些技术可以优化分

weixin_34221775的博客 421

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

MySQL查询结果添加序号的五种高效方案

在数据库查询结果中添加序号是数据分析和报表生成的常见需求。MySQL作为主流关系型数据库,提供了多种实现方案。从基础的用户变量到标准SQL窗口函数,不同技术方案在性能、兼容性和功能上各有特点。用户变量方案利用会话级变量特性,在百万级数据场景下比窗口函数快40%;窗口函数则符合SQL标准,支持分组序号等高级功能。这些技术在数据分页、排名计算、报表导出等场景有广泛应用,特别是在需要保证序号连续性的分页查询和动态更新业务中体现核心价值。本文详解的五大方案均通过生产环境验证,涵盖从MySQL 5.6到8.0+的全版

weixin_33691700的博客 468

MySQL查询结果自动添加序号的五种实战方案

在数据库查询结果中添加序号是数据处理中的常见需求,尤其在报表生成和数据分析场景中。从技术原理看,序号生成本质是对结果集的有序标记,实现方式可分为数据库层和应用层两种路径。数据库层方案利用MySQL的用户变量、窗口函数等特性,在数据检索过程中直接计算序号,避免了不必要的数据传输,显著提升处理效率。其中窗口函数(ROW_NUMBER)作为SQL标准实现,在MySQL 8.0+版本中表现优异,而用户变量方案则具有全版本兼容优势。在千万级数据量的电商订单分析、用户行为统计等实际业务中,合理的序号生成方案能使查询性能

weixin_34252090的博客 411

MySQL子查询深度解析:从基础语法到性能优化实战

在数据库查询中,子查询是嵌套在其他SQL语句内部的查询结构,它允许开发者以分步思考的方式构建复杂的数据筛选逻辑。其核心原理在于将内层查询的结果作为外层查询的条件或数据源,从而实现动态数据关联。这一技术价值在于能够优雅地处理聚合数据作为条件的场景,例如查找高于平均值的记录,这是连接查询难以直接替代的。在实际应用场景中,子查询广泛用于WHERE、FROM、SELECT等子句,配合IN、EXISTS等操作符进行数据过滤和关联。然而,使用不当易引发性能问题,尤其是关联子查询可能导致N次执行。本文聚焦MySQL子查询

weixin_33725270的博客 437

MySQL UPDATE语句优化:避免‘FROM clause‘错误的3种实用方法(含代码示例)

本文详细解析了MySQL UPDATE语句中常见的'FROM clause'错误,并提供了三种实用优化方法:临时表方案、JOIN重写和变量技巧。每种方法均附有代码示例和性能对比,帮助开发者高效解决复杂数据更新问题,提升数据库操作效率。

weixin_29276847的博客 263

技术分享 | 关于隐式的临时表导致磁盘空间耗尽的问题分析

作者:Corrado Pandiani 翻译:管长龙 背景 最近遇到了一个关于 MySQL 完全耗尽磁盘空间的问题。这是个严重的问题,因为会导致服务不断地中断,客户会不断的重启服务并在不久后再次宕机。 究竟发生了什么?在本文中,将解释原因并找到解决方案。 隐式临时表 MySQL 需要创建隐式临时表来解决某些类型的查询。往往查询的排序阶段需要依赖临时表。例如,当您使用 GR...

ActionTech的博客 376

union all动态表_技术分享 | 关于隐式的临时表导致磁盘空间耗尽的问题分析

作者:Corrado Pandiani背景最近遇到了一个关于 MySQL 完全耗尽磁盘空间的问题。这是个严重的问题,因为会导致服务不断地中断,客户会不断的重启服务并在不久后再次宕机。究竟发生了什么?在本文中,将解释原因并找到解决方案。隐式临时表MySQL 需要创建隐式临时表来解决某些类型的查询。往往查询的排序阶段需要依赖临时表。例如,当您使用 GROUP BY,ORDER BY 或DIS...

weixin_39594457的博客 124

mysql 中断查询_一个关于MySQL中隐式的临时表完全耗尽磁盘空间的问题-爱可生

背景最近遇到了一个关于 MySQL 完全耗尽磁盘空间的问题。这是个严重的问题,因为会导致服务不断地中断,客户会不断的重启服务并在不久后再次宕机。究竟发生了什么?在本文中,将解释原因并找到解决方案。隐式临时表MySQL 需要创建隐式临时表来解决某些类型的查询。往往查询的排序阶段需要依赖临时表。例如,当您使用 GROUP BY,ORDER BY 或DISTINCT 时。这样的查询分两个阶段执行:首先是...

weixin_39597318的博客 248

MySQL存储过程实战:从核心语法到性能优化的完整指南

在数据库开发中,存储过程是一种将复杂业务逻辑封装在数据库服务器端执行的技术方案。其核心原理是将一系列SQL语句和控制流程(如条件判断、循环)预编译为一个可重复调用的单元,从而减少网络通信开销,提升执行效率,并确保数据操作的原子性与安全性。从技术价值看,存储过程能有效实现业务逻辑的封装与复用,简化应用程序的数据库交互,尤其适用于订单结算、批量数据处理等需要多步原子性操作的场景。然而,若设计不当,也可能带来性能瓶颈和维护难题。本文以MySQL存储过程为例,深入解析其创建、调试、错误处理及性能优化策略,并重点探讨

weixin_34269583的博客 344

mysql分组后,取每组第一条数据或最新一条

环境 MySQL:5.7 Java:1.8 SQL语句的写法: select * from (select distinct(a.id) tid, a.* from template_detail a where a.template_id in (3, 4) order by a.id desc) tt group by tt.template_id; 思路:先进行排序,然后再进行分组,获取每组的第一条。 Q: 为什么要写distinct(a.i

山鬼谣的专栏 8万+
上一篇: 针对大事务问题对业务存储过程改造
下一篇: MySQL 派生表查询导致 Crash 的根源分析与解决方案
GreatSQL社区
博客等级 码龄5年 1210粉丝 390原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值