【问题标题】:MySQL: Re-using user-defined variables in the same SELECT statementMySQL:在同一个 SELECT 语句中重用用户定义的变量
【发布时间】:2012-04-01 05:40:33
【问题描述】:

我在 stackoverflow 中进行了搜索,但没有完全找到我的问题的确切答案,所以如果可以的话,请帮助我。

我“想要”构建这样的 SQL 语句:

SELECT
  @tax1 := (complicated calculation formula),
  @owe := (another complicated calculation formula),
  IF(@owe=0, 0,@tax1/@owe)
FROM ...

然而,在 MySQL 关于user-defined variables 的文档中,它建议不要这样说:

作为一般规则,您永远不应为用户变量赋值并在同一语句中读取该值。您可能会得到预期的结果,但这不能保证。涉及用户变量的表达式的求值顺序是未定义的,并且可能会根据给定语句中包含的元素而改变;此外,不保证此顺序在 MySQL 服务器的版本之间是相同的。在 SELECT @a, @a:=@a+1, ... 中,您可能认为 MySQL 会先评估 @a,然后再进行赋值。但是,更改语句(例如,通过添加 GROUP BY、HAVING 或 ORDER BY 子句)可能会导致 MySQL 选择具有不同评估顺序的执行计划。

我的目标是避免复制和粘贴那些非常复杂的公式,因为它会妨碍可读性,而且如果公式每次都发生变化,我需要确保它在所有地方都发生了变化 - 但我确实知道这样做,它'会正常工作的。

此外,我知道 ALIAS 不起作用,因为别名仅在 GROUP BY、HAVING 和 ORDER 子句中起作用。

我阅读了其他一些帖子,首先进行子查询以计算@tax1 和@owe,然后使用另一个查询来组合结果。但我认为性能可能不如简单地复制和粘贴这些公式。

有人有什么建议吗?还是我在可读性和性能之间做出选择?

提前致谢。

【问题讨论】:

    标签: mysql variables select user-defined


    【解决方案1】:

    是的,在没有用户变量的情况下在 SQL 中执行此操作的唯一方法是编写 派生表 子查询。然后你可以使用列别名来引用那些复杂表达式的结果:

    SELECT tax1, owe, IF(owe=0, 0,tax1/owe) AS ratio
    FROM (
      SELECT
        (complicated calculation formula) AS tax1,
        (another complicated calculation formula) AS owe
      FROM ...
    ) AS _sub
    

    MySQL 在优化子查询时存在一些问题,但派生表的情况并不是坏情况之一。

    关于 MySQL 和子查询的警告是关于在 WHERE 子句的范围条件中使用子查询。 MySQL 无法确定子查询是恒定的,它会重复重新评估子查询作为 依赖子查询,即使这不是必需的。

    【讨论】:

      【解决方案2】:

      我经常在选择语句中使用变量进行顺序评估、聚合、组统计和数据分类。

      这是一个例子:

      MySQL 列出每个类别的前 X 条记录,将每条记录与其之前的差异结合起来

      create table if not exists 
              closemovers engine=memory 
      select 
                code 
              , date 
              , close 
              , rank 
              , prevclose 
              , sign 
              , cumm 
      from 
              ( select 
                        `code` 
                      , `date` 
                      , `close` 
                      , @rank := if( @code = code , @rank + 1 , 1) as rank 
                      , @prevclose := if( @code = code , cast( @prclose as decimal(10,3) ), null) as prevclose 
                      , if(@code = code, sign( @prclose - close), NULL) as sign 
                      , @cumm := if(@code = code and @psign = sign(@prclose - close), @cumm + 1 , 1) as cumm
                      , @psign := sign(@prclose - close) 
                      , @code := code
                      , @prclose := close 
              from 
                  companyhistory 
              order by 
                  code, date 
               ) as ranked 
       join 
              pricefeed 
                      using (code) 
      where 
              rank < 10 
              and sign is not null ;
      

      希望能给你一个提示

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-01-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-05-18
        • 2022-11-11
        • 2013-03-12
        相关资源
        最近更新 更多