mysql5.7的group by后的order by失效解决方案

本文深入探讨MySQL5.7版本中优化器如何处理派生表和视图引用,包括合并和实现两种策略,以及优化器如何在查询执行过程中智能地决定是否延迟实现派生表,从而提高查询效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

版本5.7 

MySQL 5.7参考手册  /  ...  /  使用合并或实现优化派生表和视图引用

8.2.2.4使用合并或实现来优化派生表和视图引用

优化器可以使用两种策略(也适用于视图引用)处理派生表引用:

  • 将派生表合并到外部查询块中

  • 将派生表实现为内部临时表

例1:

SELECT * FROM (SELECT * FROM t1) AS derived_t1;

通过合并派生表 derived_t1,该查询的执行类似于:

SELECT * FROM t1;

例2:

SELECT *
  FROM t1 JOIN (SELECT t2.f1 FROM t2) AS derived_t2 ON t1.f2=derived_t2.f1
  WHERE t1.f1 > 0;

通过合并派生表 derived_t2,该查询的执行类似于:

SELECT t1.*, t2.f1
  FROM t1 JOIN t2 ON t1.f2=t2.f1
  WHERE t1.f1 > 0;

具有实现,derived_t1并 derived_t2在每个查询中被视为单独的表。

优化器以相同的方式处理派生表和视图引用:它尽可能避免不必要的实现,这样可以将条件从外部查询推送到派生表,并生成更高效的执行计划。(有关示例,请参见 第8.2.2.2节“使用实现优化子查询”。)

如果合并将导致外部查询块引用超过61个基表,则优化程序将选择实现。

ORDER BY如果这些条件都为真 ,则优化器将派生表或视图引用中的子句传播到外部查询块:

  • 外部查询未分组或聚合。

  • 外部查询不指定 DISTINCTHAVING或 ORDER BY

  • 外部查询将此派生表或视图引用作为FROM子句中的唯一源。

否则,优化器会忽略该ORDER BY子句。

以下方法可用于影响优化程序是否尝试将派生表和查看引用合并到外部查询块中:

  • 可以使用系统变量 的derived_merge标志 optimizer_switch,假设没有其他规则阻止合并。请参见第8.9.2节“可切换的优化”。默认情况下,启用该标志以允许合并。禁用该标志可防止合并并避免 ER_UPDATE_TABLE_USED 错误。

    derived_merge标志也适用于不包含ALGORITHM子句的视图。因此,如果 ER_UPDATE_TABLE_USED使用等效于子查询的表达式的视图引用发生错误,则添加ALGORITHM=TEMPTABLE到视图定义会阻止合并并优先于该 derived_merge值。

  • 可以通过在子查询中使用任何阻止合并的构造来禁用合并,尽管这些构造对实现的影响并不明确。防止合并的构造对于派生表和视图引用是相同的:

    • 聚合函数(SUM(), MIN(), MAX(), COUNT(),等等)

    • DISTINCT

    • GROUP BY

    • HAVING

    • LIMIT

    • UNION 要么 UNION ALL

    • 选择列表中的子查询

    • 分配给用户变量

    • 仅引用文字值(在这种情况下,没有基础表)

derived_merge标志也适用于不包含ALGORITHM子句的视图。因此,如果 ER_UPDATE_TABLE_USED使用等效于子查询的表达式的视图引用发生错误,则添加ALGORITHM=TEMPTABLE 到视图定义会阻止合并并优先于当前derived_merge值。

如果优化器选择实现策略而不是合并派生表,它将按如下方式处理查询:

  • 优化器推迟派生表实现,直到在查询执行期间需要其内容。这提高了性能,因为延迟实现可能导致根本不必执行此操作。考虑将派生表的结果连接到另一个表的查询:如果优化器首先处理该另一个表并发现它不返回任何行,则不需要进一步执行连接,并且优化器可以完全跳过实现派生表的实现。

  • 在查询执行期间,优化器可以向派生表添加索引以加速从中检索行。

EXPLAIN 对于SELECT包含派生表的查询, 请考虑以下语句:

EXPLAIN SELECT * FROM (SELECT * FROM t1) AS derived_t1;

优化程序通过延迟派生表来避免实现派生表,直到SELECT执行期间需要结果为止 。在这种情况下,不执行查询(因为它出现在 EXPLAIN语句中),因此永远不需要结果。

即使对于执行的查询,派生表实现的延迟也可能使优化器完全避免实现。发生这种情况时,执行实现所需的时间会更快地执行查询。请考虑以下查询,该查询将派生表的结果连接到另一个表:

SELECT *
  FROM t1 JOIN (SELECT t2.f1 FROM t2) AS derived_t2
          ON t1.f2=derived_t2.f1
  WHERE t1.f1 > 0;

如果优化进程t1首先并且WHERE子句产生空结果,则连接必须为空并且不需要实现派生表。

对于派生表需要实现的情况,优化器可以向实现表添加索引以加速对其的访问。如果这样的索引允许 ref访问表,则可以大大减少查询执行期间读取的数据量。请考虑以下查询:

SELECT *
 FROM t1 JOIN (SELECT DISTINCT f1 FROM t2) AS derived_t2
         ON t1.f1=derived_t2.f1;

优化器在列上构造索引 f1derived_t2如果这样做将允许使用ref最低成本执行计划的 访问。添加索引后,优化程序可以将具体化派生表视为具有索引的常规表,并且与生成的索引类似。与没有索引的查询执行成本相比,索引创建的开销可以忽略不计。如果 ref访问会导致比其他访问方法更高的成本,则优化程序不会创建任何索引并且不会丢失任何内容。

对于优化程序跟踪输出,合并的派生表或视图引用不会显示为节点。只有其基础表出现在顶部查询的计划中。

 

SELECT * from (
SELECT a.id,
a.uin uin,
a.Contact_Number contactNumber,
b.Add_Time addTime,
b.Rate rate,
Order_Num orderNum,
c.mq_server_id mqServerId,
b.Verify,
c.server_id serverId,
c.Nick_Name nickName,
a.contact_name contactName
FROM add_contact_details a
INNER JOIN wechat_user_add_settings b on b.uin = a.uin
INNER JOIN wechat_user c on c.uin = a.uin
WHERE
c.`Status` = 1
and a.Status = 0
and b.Is_Start = 1
and locate('Ipad',c.mq_server_id)
and b.addCount < b.Wx_Limit_Number
GROUP BY a.id
ORDER BY Order_Num

) tmp
GROUP BY tmp.uin,tmp.mqServerId;

### MySQLGROUP BY 导致索引失效的原因 在 MySQL 的查询过程中,`GROUP BY` 是一种常见的聚合操作方式。然而,在某些情况下,`GROUP BY` 可能会导致索引失效,从而影响查询性能。以下是可能导致 `GROUP BY` 索引失效的主要原因: #### 1. **未遵循最左前缀原则** 如果定义的复合索引未能满足最左前缀原则,则可能会导致索引无法被有效利用。例如,假设有一个复合索引 `(col1, col2)`,而查询条件仅涉及 `col2` 或者 `GROUP BY` 子句中的列顺序不符合索引定义的顺序,那么该索引可能不会被使用[^1]。 #### 2. **隐式的类型转换** 当查询条件或 `GROUP BY` 列涉及到隐式类型转换时,MySQL 将无法直接使用索引。例如,字符串与数值之间的自动转换或者日期时间格式化等问题都可能导致索引失效[^3]。 #### 3. **使用函数或表达式** 在 `GROUP BY` 子句中应用任何函数(如 `UPPER()`、`LOWER()`)、算术运算或其他复杂表达式都会使索引失效。这是因为 MySQL 需要计算这些表达式的值后再进行分组操作,而不是基于原始存储的数据[^3]。 #### 4. **数据分布特性** 即使存在适合的索引,但如果表内的数据分布非常稀疏或者重复度极低,MySQL 查询优化器也可能决定放弃使用索引转而采用全表扫描的方式完成分组操作,因为在这种情形下全表扫描反而更快[^1]。 --- ### 解决方案 针对以上提到的各种情况,可以采取以下措施来改善由 `GROUP BY` 引起的索引失效问题: #### 1. **确保符合最左前缀原则** 重新设计查询以充分利用已有的复合索引,并严格按照其定义次序指定过滤条件以及分组依据。如果当前索引不适合现有需求,则应考虑创建新的更适合特定查询模式的索引[^1]。 #### 2. **避免不必要的类型转换** 保持查询字段与其对应数据库列之间的一致性非常重要。尽量避免让 MySQL 执行额外的类型推断工作;对于日期范围查找等情况尤其如此——应当始终传递正确格式化的常量而非依赖内置函数来进行解析[^5]。 #### 3. **移除函数调用** 修改应用程序逻辑使得可以直接引用未经修饰的基础列名作为 `GROUP BY` 参数的一部分,而不附加任何形式的操作符或方法封装[^3]。 #### 4. **强制使用索引提示** 虽然一般建议信任 MySQL 自己的选择机制,但在特殊场景下确实有必要的话也可以显式地告诉它应该优先选用哪个具体索引(`FORCE INDEX`) 来加速目标查询过程[^1]。 #### 5. **升级到更高版本并注意行为差异** 不同版本间可能存在兼容性和实现细节上的区别,比如从 MySQL 5.6 升级至 5.7 后发现原本正常工作的嵌套子查询加上外部层面上施加了另一个 `GROUP BY` 结果却发生了变化。此时需查阅官方文档了解新旧版间的改动点并通过适当调整SQL语法结构规避此类风险[^4]。 ```sql -- 示例:解决 MySQL 5.7 版本中 ORDER BYGROUP BY 失效的问题 SELECT * FROM ( SELECT *, ROW_NUMBER() OVER(PARTITION BY sale_code ORDER BY data_month DESC) AS rn FROM risk ) a WHERE a.rn = 1; ``` 此代码片段展示了如何借助窗口函数替代传统嵌套查询形式达到相同效果的同时还能更好地控制排序规则的应用时机。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值