【问题标题】:Question about differences in user variable usage between mysql 5.6 and mysql 8.0关于mysql 5.6和mysql 8.0用户变量使用差异的问题
【发布时间】:2021-08-05 18:46:54
【问题描述】:

我正在尝试将 mysql 版本从 5.6 升级到 8.0

一些旧版 SQL 语句包含用户定义的变量。

我的问题是以下查询的结果在两个版本之间是不同的。 (是问题的总结)

SELECT @t 
  FROM ( SELECT @t:=0 ) T
 WHERE @t IS NOT NULL

在版本 5.6 的情况下,
结果表上显示 0。


但是,在 8.0 版上 结果表中没有行。
看起来 @t 在 WHERE 子句中仍然为 NULL(@t 未定义)。
我想知道为什么 @t 没有在 FROM 子句的子查询中定义和分配。

有谁知道原因?

【问题讨论】:

  • 看起来以这种方式使用变量现在已被弃用。显示警告的example。此外,不确定您拥有哪个确切版本的 MySQL,但在关于用户变量的第二个项目符号项中也提到了 8.0.13 更改。

标签: mysql mysql-8.0


【解决方案1】:

当派生表没有首先具体化并且 可能是优化器使用derived condition pushdown 的效果。它基本上将您的查询重写为

SELECT @t:=0 WHERE @t IS NOT NULL

更明显的是不返回任何行(如果 @t 开头为 null),因为 where 条件找不到任何行,因此设置部分将永远不会被执行。

你能做什么?

  • 您可以使用 optimizer hints 禁用该优化,例如试试

    SELECT /*+ NO_DERIVED_CONDITION_PUSHDOWN() */ @t  
    FROM ( SELECT @t:=0 ) T
    WHERE @t IS NOT NULL
    
  • 您可以使用optimizer_switch 配置和设置derived_condition_pushdown=off 完全禁用它。如果您经常使用它并且不想调整每个查询,这可能很有用,但当然也会禁用可能从中受益的查询的优化。

  • 您可以在运行该查询之前初始化变量,例如首先运行set @t := 0。但并非适用于所有情况。

  • 您可以强制 MySQL 实现派生表。即使导致效果的实际优化不是派生条件下推,这也应该始终有效。实现它的一种简单方法是添加任意limit-clause,例如

    SELECT @t  
    FROM ( SELECT @t:=0 limit 10000000 ) T
    WHERE @t IS NOT NULL
    

一般来说,请注意,变量的这种使用(例如,在查询中设置它们)是deprecated(还有其他原因也是由于您的问题中的影响),并且在 MySQL 的某些未来版本中将不再起作用。您通常可以使用窗口函数或递归 ctes 重写这些查询,参见例如以this question 为例。

【讨论】:

  • 你的答案完全正确。从 8.0.22 中包含了 derived_condition_pushdown 优化器,我在 8.0.23 上进行了测试。谢谢你的帮助!
猜你喜欢
  • 2016-04-22
  • 1970-01-01
  • 2023-03-21
  • 1970-01-01
  • 2019-07-19
  • 2014-08-01
  • 2019-02-08
  • 1970-01-01
  • 2013-02-23
相关资源
最近更新 更多