MySQL:在同一个SELECT语句中重用用户定义的变量

时间:2012-03-15 16:52:17

标签: mysql variables select user-defined

我在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,然后使用另一个查询来组合结果。但我认为性能可能不如仅仅简单地复制和粘贴那些公式那样有效。

有没有人有任何建议他们会做什么?还是我坚持在可读性和性能之间做出选择?

提前致谢。

2 个答案:

答案 0 :(得分: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无法弄清楚子查询是否是常量,并且它会重复地将子查询重新评估为依赖子查询,即使这不是必需的。

答案 1 :(得分:0)

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

这是一个例子:

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 ;

我希望这可以给你一个提示