0

我正在尝试将 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 子句的子查询中定义和分配。

有谁知道原因?
4

1 回答 1

0

当派生表没有首先具体化并且可能是优化器使用派生条件下推的影响时,就会发生这种情况。它基本上将您的查询重写为

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

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

你能做什么?

  • 您可以使用优化器提示禁用该优化,例如尝试

    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子句,例如

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

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

于 2021-08-05T10:46:54.193 回答