MySQL中用户定义变量的奇怪行为

MySQL中用户定义变量的奇怪行为,mysql,mysql-workbench,Mysql,Mysql Workbench,我正在构建一个报告,并使用用户定义的变量使查询尽可能紧凑。我在MySQL Workbench和PHP中注意到的是,它们并不总是正常工作 例如: SELECT @NewSales := SUM(CASE WHEN `v`.`new_used`='N' THEN 1 ELSE 0 END) AS `NewSales`, @UsedSales := SUM(CASE WHEN `v`.`new_used`='U' THEN 1 ELSE 0 END) AS `UsedSales`, @UsedSale

我正在构建一个报告,并使用用户定义的变量使查询尽可能紧凑。我在MySQL Workbench和PHP中注意到的是,它们并不总是正常工作

例如:

SELECT
@NewSales := SUM(CASE WHEN `v`.`new_used`='N' THEN 1 ELSE 0 END) AS `NewSales`,
@UsedSales := SUM(CASE WHEN `v`.`new_used`='U' THEN 1 ELSE 0 END) AS `UsedSales`,
@UsedSales + @NewSales AS `TotalSales`
FROM `fi_sales` `s`
LEFT JOIN `vehicles` `v` ON `v`.`stock_number`=`s`.`stock_number`
如果我在Workbench中运行上述查询,第一次运行将输出TotalSales=NULL:

NewSales, UsedSales, TotalSales
3418, 2889, NULL
如果刷新查询,输出将生成TotalSales的预期结果:

NewSales, UsedSales, TotalSales
3418, 2889, 6307.000000000000000000000000000000
有点奇怪;几乎就好像变量在设置它的同一个查询中不可用一样。我通常通过在不使用变量的情况下重现计算来解决这个问题

我的下一个问题是,如果我将相同的查询从工作台复制到我的应用程序(PHP)中,TotalSales输出将产生“0”零


我相信这里发生的事情有一个很好的解释,但我很难找到它。非常感谢您的回答。

您在查询中使用用户定义的变量来更改它们,这是一个不确定的领域,解释很简单:您得到的答案实际上来自同一查询的上一次运行

UDV的作用域为您的单个数据库连接。它们的值在查询之间保持不变,但不会在连接之间保持不变。此查询在运行查询之前(而不是之后)为您提供
@UsedSales+@NewSales
的值。(为什么?因为它就是这样。没有理由……它可以走任何一条路。见下文。)

SET@UsedSales=1、@NewSales=2
并再次运行查询
Total
在下一次运行中将为3,这显然是错误的答案(与您预期的结果相比),但从服务器可以按其喜欢的任何顺序自由解析这些值的意义上讲,这并不是错误的,因为它们看起来像常量

作为一般规则,除了在SET语句中,您永远不应该为用户变量赋值,而应该读取同一语句中的值

对于其他语句,例如SELECT,您可能会得到预期的结果,但这并不能保证

涉及用户变量的表达式的求值顺序未定义

你在试图解决一个其实不是问题的问题

相反,可以这样做:

SELECT
SUM(CASE WHEN `v`.`new_used`='N' THEN 1 ELSE 0 END) AS `NewSales`,
SUM(CASE WHEN `v`.`new_used`='U' THEN 1 ELSE 0 END) AS `UsedSales`,
SUM(CASE WHEN `v`.`new_used` IN ('N','U') THEN 1 ELSE 0 END) AS `TotalSales`
FROM `fi_sales` `s`
LEFT JOIN `vehicles` `v` ON `v`.`stock_number`=`s`.`stock_number`;
或者,如果您坚持,则创建一个派生表(此处命名为
x
),并额外添加结果列

SELECT
x.NewSales, 
x.UsedSales, 
x.NewSales + x.UsedSales AS TotalSales
FROM (
  SELECT
  SUM(CASE WHEN `v`.`new_used`='N' THEN 1 ELSE 0 END) AS `NewSales`,
  SUM(CASE WHEN `v`.`new_used`='U' THEN 1 ELSE 0 END) AS `UsedSales`
  FROM `fi_sales` `s`
  LEFT JOIN `vehicles` `v` ON `v`.`stock_number`=`s`.`stock_number`
) x;
这会将内部结果具体化为一个临时表,在查询完成后立即丢弃该临时表


或者,如果你真的想要聪明和短小,那么就这样做:

SELECT
  COUNT(`v`.`new_used`='N' OR NULL) AS `NewSales`,
  COUNT(`v`.`new_used`='U' OR NULL) AS `UsedSales`,
  COUNT(`v`.`new_used` IN ('N','U') OR NULL) AS `TotalSales`
FROM `fi_sales` `s`
LEFT JOIN `vehicles` `v` ON `v`.`stock_number`=`s`.`stock_number`;

这是可行的,因为
COUNT()
只计算具有非空参数的行,任何表达式
expr或null
强制将
expr
作为布尔表达式计算,因此在逻辑上等同于
当expr[为真]则1 ELSE null END
时的情况,因此只能计算为
1
(如果
expr
为真)…或
NULL
(如果
expr
为假或空)…哪一个与如何
COUNT()精确匹配
works.

其他软件包信息…#MySQL MySQL server/now 5.7.16-0ubuntu0.16.04.1#PHP php7.0-common/now 7.0.8-0ubuntu0.16.04.3#Workbench版本6.3.9非常感谢您的详细解释Michael。我想我可能使用了不恰当的变量;我习惯在同一个函数中使用它们来保存te临时数据,MySQL中的这个实现对我来说似乎有悖常理,但事实就是如此。我非常喜欢你提供的第三个示例,并将开始使用这个方法。再次感谢。