【问题标题】:Replacing user variables in MySQL 8 query替换 MySQL 8 查询中的用户变量
【发布时间】:2021-01-15 00:14:04
【问题描述】:

我有一个由查询生成器生成的查询,它依赖于查询中用户变量的计算来影响查询中其他输出的生成。作为查询的一个简单示例,它可能看起来像这样:

SELECT * FROM (
    SELECT a, b, c,
           @d := a * b AS d,
           @e := (SELECT MAX(x) FROM t2 WHERE x > @d) AS e,
           @f := @e * c AS f,
           ...
    FROM t1
    WHERE <column-filtering>
) tt
WHERE <variable-filtering>

这个系统的数据库服务器最近升级到了 MySQL8,这个查询现在产生了很多 1287 警告:

在表达式中设置用户变量已被弃用,并将在未来的版本中删除。考虑替代方案:“SET variable=expression, ...”或“SELECT expression(s) INTO variables(s)”。

客户不满意。

查询是以这种方式构建的(而不是将其实现为多个查询),以允许根据计算值和表列过滤结果。由于所涉及的表可能非常大,但过滤后的结果要小得多,因此需要在数据库服务器上过滤结果,以避免将大型结果集传输到计算服务器并在那里过滤的开销(和成本)。

我创建了一个small fiddle 作为包含真实数据的实际查询的精简版本(有 14 个变量,有些变量多达 ~100 个)。如何重构此查询以删除变量?

【问题讨论】:

  • 你试过SELECT 2 into @a而不是@a:=2吗?
  • 您不能在一个查询中执行多个查询,结果取决于先前的计算。此外,您只能将SELECT ... INTO 与 1 行数据一起使用
  • 如果您打算尽可能少地使用这些内部表达式进行更改,您可以按照警告的建议进行操作并使用 SET 语句——在游标循环中。否则,您可能会首先挑选出那些真正依赖于其他变量的表达式的最小部分,然后将这些计算提取到外部SELECT
  • 这个查询无论如何都被破坏了。它在不同的表达式中分配和使用变量——甚至早期版本的 MySQL 也警告过。
  • @GordonLinoff 同意,但它一直在工作。我对你关于如何重构它的建议感兴趣。

标签: mysql sql mysql-8.0


【解决方案1】:

您也可以通过在子查询的更多级别中定义别名来使用别名:

SELECT a, b, c, d, e, e*c AS f
FROM (
  SELECT a, b, c, d, (SELECT MAX(x) FROM t2 WHERE x > tt.d) AS e
  FROM (
    SELECT a, b, c, a*b AS d
    FROM t1
    WHERE ...
  ) AS tt
) AS tt2
WHERE ...

(我没有测试,这只是一个演示概念的例子)

【讨论】:

  • 一些查询有大约 100 个变量 - 这可能会遇到嵌套限制吗?您认为这种嵌套级别会如何影响性能?
  • 您的问题并不清楚。必须有一个实际限制,但没有记录。如果我是您,我会考虑您是否真的需要在单个查询中执行此操作,或者您是否应该查询原始数据并在您的应用程序中进行计算。如果任务太复杂而无法有效编码,请退后一步,重新审视您的假设。
  • 这个问题背后的部分原因是得到了这样的良好反馈。在 db 服务器上进行计算的原因是因为将大量数据从 db 服务器传输到计算服务器的成本。我发布的“缩减”演示确实有 14 个变量,但我还在问题中添加了一个注释,即最多可达 ~100 个。
【解决方案2】:

这个查询可以被单独的 CTE 重写如下:

WITH R1 AS (
    SELECT a, b, c, a * b AS d
    FROM t1
    WHERE <column-filtering>
), R2 AS (
    SELECT *, (SELECT MAX(x) FROM t2 WHERE x > d) AS e
    FROM R1
), R3 AS (
    SELECT *, e * c AS f
    FROM R2
), ..., Rn AS (...)
SELECT * FROM Rn
WHERE <variable-filtering>

fiddle

【讨论】:

  • 这看起来是个不错的方法,我将不得不研究是否可以重写查询生成器以自动生成适当的 CTE。否则查询构建器的所有输入都需要重写,这是一项艰巨的任务。
  • @Nick,我从 5.6 切换到 8 后也遇到了类似的问题。但是我没有使用查询生成器,计算依赖统计的表达式作为字符串存储在表中。这是一个非常简化的fiddle 示例,我不知道它对你有多大用处。
  • 谢谢。我们使用的查询生成器是一种内部产品,可以将 XML 文件转换为 SQL 查询,因此它并不完全适用,但所有输入都值得赞赏。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多