在MySQL中使用变量赋值从最后一行减去值

在MySQL中使用变量赋值从最后一行减去值,mysql,design-patterns,query-optimization,Mysql,Design Patterns,Query Optimization,根据MySQL文档: 作为一般规则,决不应将值分配给用户变量,并在同一语句中读取该值。你可能会 你期望的结果,但这不是保证 然而,在HighperformanceMySQL一书中,有几个例子说明了如何使用这种策略来提高查询性能 以下是一种反模式吗?如果是,有没有更好的方法来编写查询,同时保持良好的性能 set @last = null; select tick, count-@last as delta, @last:=count from measurement; 为了澄清,我的目标是找出

根据MySQL文档:

作为一般规则,决不应将值分配给用户变量,并在同一语句中读取该值。你可能会 你期望的结果,但这不是保证

然而,在HighperformanceMySQL一书中,有几个例子说明了如何使用这种策略来提高查询性能

以下是一种反模式吗?如果是,有没有更好的方法来编写查询,同时保持良好的性能

set @last = null;
select tick, count-@last as delta, @last:=count from measurement;
为了澄清,我的目标是找出这一行和上一行的区别。我的表在tick上有一个主键,它是datetime列

更新:

在尝试了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
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似乎在第一次处理中不包含聚合函数的表达式,在第二次处理中不包含聚合函数的表达式。上面代码中的奇怪之处在于,即使当
cumulative
的计算结果为true时,MySQL也必须在
ELSE
子句中查看
AVG
聚合函数,并决定在第二遍中计算整个内部
CASE
表达式。由于
@delta
是在一个没有聚合函数的表达式中设置的,它似乎是在第一次传递时设置的,在第二次传递发生时,MySQL将评估设置
@delta
@last
的行

最终,我似乎找到了一个解决办法,在第一个表达式中也包含了聚合函数。大概是这样的:

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
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的理解完全基于测试和猜测,因为我没有阅读源代码,但希望这能帮助其他可能遇到类似问题的人


我将接受希洛米的回答,因为这确实是一个很好的解决方案。注意如何使用聚合函数。

我已经深入研究了这个问题,并在上面写了一些改进

我在中提供了一个解决方案,它使用的函数的顺序是可以预期的。也考虑去年。

CASE
这样的结构和像
COALESCE
这样的函数已经知道了底层行为(至少在这种行为改变之前是这样的,对吧?)

例如,
CASE
子句按定义顺序逐个检查
WHEN
条件

考虑重写原始查询:

select 
  tick,
  CASE
    WHEN (@delta := count-@last) IS NULL THEN NULL
    WHEN (@last:=count ) IS NULL THEN NULL
    ELSE @delta
  END AS delta
from 
  measurement,
  (select @last := 0) s_init
;
CASE
子句有三个
WHEN
条件。它按顺序执行它们,直到遇到第一个成功的。我编写它们时,前两个总是会失败。因此,它执行第一个,然后转向执行第二个,最后返回第三个。总是

因此,我克服了期望求值顺序的问题,这是一个真实的问题,当您开始添加更复杂的子句,例如
GROUP BY
DISTINCT
order BY
等时,这一问题最为明显


最后请注意,我的解决方案在结果集的第一行与您的不同——使用您的“返回
NULL
,使用我的解决方案返回
0
count
之间的增量。如果我使用了
NULL
,我就需要在
条件下以其他方式更改
,确保它们在
NULL
值上失败

我在中提供了一个解决方案,它使用的函数的顺序是可以预期的。也考虑去年。

CASE
这样的结构和像
COALESCE
这样的函数已经知道了底层行为(至少在这种行为改变之前是这样的,对吧?)

例如,
CASE
子句按定义顺序逐个检查
WHEN
条件

考虑重写原始查询:

select 
  tick,
  CASE
    WHEN (@delta := count-@last) IS NULL THEN NULL
    WHEN (@last:=count ) IS NULL THEN NULL
    ELSE @delta
  END AS delta
from 
  measurement,
  (select @last := 0) s_init
;
CASE
子句有三个
WHEN
条件。它按顺序执行它们,直到遇到第一个成功的。我编写它们时,前两个总是会失败。因此,它执行第一个,然后转向执行第二个,最后返回第三个。总是

因此,我克服了期望求值顺序的问题,这是一个真实的问题,当您开始添加更复杂的子句,例如
GROUP BY
DISTINCT
order BY
等时,这一问题最为明显

最后请注意,我的解决方案在结果集的第一行与您的不同——使用您的“返回
NULL
,使用我的解决方案返回
0
count
之间的增量。H