根据 MySQL 文档:
作为一般规则,您永远不应为用户变量赋值并在同一语句中读取该值。您可能会得到预期的结果,但这不能保证。
然而,在High Performance MySQL一书中,有几个使用这种策略来提高查询性能的例子。
以下是反模式吗?如果是,是否有更好的方法来编写查询,同时保持良好的性能?
set @last = null;
select tick, count-@last as delta, @last:=count from measurement;
为了澄清起见,我的目标是找出这一行和最后一行之间的区别。我的表在刻度上有一个主键,它是一个日期时间列。
更新:
在尝试了 Shlomi 的建议后,我恢复了原来的查询。事实证明,将 case 语句与聚合函数一起使用会产生意想不到的行为。参见例如:
case when (@delta := (max(measurement.count) - @lastCount)) AND 0 then null
when (@lastCount := measurement.count) AND 0 then null
else @delta end
似乎 mysql 在第一次遍历结果时评估不包含聚合函数的表达式,然后在第二次(分组)遍历时评估聚合表达式。它似乎在第二遍期间或之后评估 case 表达式,并在该评估中使用第一遍中的预先计算值。结果是第三行@delta 始终是@delta 的初始值(因为直到分组通过才发生分配)。我试图将一个组函数合并到 @delta 行中,但无法让它按预期运行。所以我最终回到了没有这个问题的原始查询。
我仍然希望听到更多关于如何更好地处理这样的查询的建议。
更新 2:
很抱歉没有对这个问题做出回应,直到现在我才有机会进一步调查。
使用 Shlomi 的解决方案,我似乎遇到了问题,因为我在读取 @last 变量时使用的是 group by 函数,但在设置它时却没有。我的代码看起来像这样:
CASE
WHEN (@delta := count - @last) IS NULL THEN NULL
WHEN (@last:= count ) IS NULL THEN NULL
ELSE (CASE WHEN cumulative THEN @delta ELSE avg(count) END)
END AS delta
MySQL 似乎在第一遍处理不包含聚合函数的表达式,而在第二遍处理包含聚合函数的表达式。上面代码中的奇怪之处在于,即使cumulative
计算结果为 true,MySQL 也必须看到子句AVG
中的聚合函数,并决定在第二遍中ELSE
计算整个内部表达式。CASE
由于@delta
在没有聚合函数的表达式中设置它似乎在第一次通过时被设置,并且在第二次通过时 MySQL 已经完成了对设置@delta
和的行的评估@last
。
最终,我似乎通过在第一个表达式中包含聚合函数找到了解决方法。像这样的东西:
CASE
WHEN (@delta := max(count) - @last) IS NULL THEN NULL
WHEN (@last:= max(count) ) IS NULL THEN NULL
ELSE (CASE WHEN cumulative THEN @delta ELSE avg(count) END)
END AS delta
由于我没有阅读源代码,因此我对 MySQL 所做的事情的理解纯粹基于测试和推测,但希望这将有助于其他可能遇到类似问题的人。
我将接受 Shlomi 的回答,因为它确实是一个很好的解决方案。请注意如何使用聚合函数。