这是怎么回事?
MySQL 通过加入和排序进行隐式转换,查询正在被位。
解决方案
让我们先解决问题,然后讨论我们是如何到达那里的。注意1/var_fld
to1.0/var_fld
和5000
to的变化5000.0
。
SELECT g.calc_var * d.dtl_var as new_var, g.calc_var
FROM (
SELECT unq_id, IF (var_fld > 5000, 1.0/var_fld, 5000.0) calc_var
FROM test_grids_1
) g
INNER JOIN test_grid_dtl_1 d ON d.unq_id = g.unq_id
ORDER BY 1
+---------------+------------+
| new_var | calc_var |
+---------------+------------+
| 0.0003833 | 0.00017 |
| 10000.0000000 | 5000.00000 |
| 12000.0000000 | 5000.00000 |
+---------------+------------+
您也可以使用cast
. 请注意,我在最后一列中包含了十六进制值。当您继续阅读时,它将很有用:
SELECT g.calc_var * d.dtl_var as new_var, g.calc_var, hex(g.calc_var) as hcalc_var
FROM (
SELECT unq_id, IF (var_fld > 5000, cast(1/var_fld as decimal(15,5)), 5000.0) calc_var
FROM test_grids_1
) g
INNER JOIN test_grid_dtl_1 d ON d.unq_id = g.unq_id
ORDER BY 1
+---------------+------------+-----------+
| new_var | calc_var | hcalc_var |
+---------------+------------+-----------+
| 0.0003910 | 0.00017 | 0 |
| 10000.0000000 | 5000.00000 | 1388 |
| 12000.0000000 | 5000.00000 | 1388 |
+---------------+------------+-----------+
其他解决方案
注意if
withcase
语句的替换。
SELECT g.calc_var * d.dtl_var as new_var, g.calc_var
FROM (
SELECT unq_id, case when var_fld > 5000 then 1/var_fld else 5000 end calc_var
FROM test_grids_1
) g
INNER JOIN test_grid_dtl_1 d ON d.unq_id = g.unq_id
ORDER BY 1
+--------------+-----------+
| new_var | calc_var |
+--------------+-----------+
| 0.000383 | 0.0002 |
| 10000.000000 | 5000.0000 |
| 12000.000000 | 5000.0000 |
+--------------+-----------+
请注意case
语句如何不需要任何类型的转换即可获得几乎相同的结果。但是,要获得与第一个查询完全相同的结果,您必须执行以下操作 -
还有一个
注意1.0/var_fld
and5000.0
和 withcast
而不是if
SELECT g.calc_var * d.dtl_var as new_var, g.calc_var
FROM (
SELECT unq_id, case when var_fld > 5000 then 1.0/var_fld else 5000.0 end calc_var
FROM test_grids_1
) g
INNER JOIN test_grid_dtl_1 d ON d.unq_id = g.unq_id
ORDER BY 1
这是如何突出的?
让我们看一下原始查询;我添加了一个新字段hex(g.calc_var)
,它是g.calc_var
.
SELECT g.calc_var * d.dtl_var as new_var, g.calc_var, hex(g.calc_var) as hcalc_var
FROM (
SELECT unq_id, IF (var_fld > 5000, 1/var_fld, 5000) calc_var
FROM test_grids_1
) g
INNER JOIN test_grid_dtl_1 d ON d.unq_id = g.unq_id
ORDER BY 1
+--------------+----------+-----------+
| new_var | calc_var | hcalc_var |
+--------------+----------+-----------+
| 0.000383 | 0.0002 | 0 |
| 10000.000000 | 99.9999 | 1388 |
| 12000.000000 | 99.9999 | 1388 |
+--------------+----------+-----------+
将结果与解决方案部分中的第一个查询进行比较
SELECT g.calc_var * d.dtl_var as new_var, g.calc_var, hex(g.calc_var) as hcalc_var
FROM (
SELECT unq_id, IF (var_fld > 5000, 1.0/var_fld, 5000.0) calc_var
FROM test_grids_1
) g
INNER JOIN test_grid_dtl_1 d ON d.unq_id = g.unq_id
ORDER BY 1
+---------------+------------+-----------+
| new_var | calc_var | hcalc_var |
+---------------+------------+-----------+
| 0.0003833 | 0.00017 | 0 |
| 10000.0000000 | 5000.00000 | 1388 |
| 12000.0000000 | 5000.00000 | 1388 |
+---------------+------------+-----------+
请注意,两个查询中的十六进制值完全相同,但十进制值不同。
5000 怎么会变成 99.9999?
select cast(5000 as decimal(6,4)) as test;
+---------+
| test |
+---------+
| 99.9999 |
+---------+
1 row in set, 1 warning (0.00 sec)
show warnings;
+---------+------+-----------------------------------------------+
| Level | Code | Message |
+---------+------+-----------------------------------------------+
| Warning | 1264 | Out of range value for column 'test' at row 1 |
+---------+------+-----------------------------------------------+
像那样!当 5000 转换为长度为 6 的小数(包括 4 个小数)时,结果是适合小数 (6,4) 的最大值。哎哟。
在这种情况下,会引发警告,这很好。在测试过程中可能会发现它。但是,有问题的查询不会引发任何警告。这不好。
这导致了多个问题
- 为什么在没有 的情况下正确进行投射
order by
?
- 是什么
order by
导致铸造以我们注意到的方式发生?
- 为什么
case...end
显示的结果比if(...)
不进行强制转换时效果更好?
- 为什么在完成这样的投射时不会发出警告?
您可能想向 MySQL 人员提交错误报告。我没有安装最新的 MariaDB,所以我不能说 MariaDB 中是否也存在这个问题。我很好奇并安装了 10.0.25-MariaDB-0ubuntu0.16.04.1。MariaDB 10.0.25 中似乎也存在同样的问题。
这可以预防吗?
是的。每当处理 int 到 float/double/decimal 时,隐式转换以获得可预测的结果。我仍然希望 MySQL 能够研究这种极端情况。如果有人遇到解释此行为的文档,请在此答案中添加评论,以便我进行自我教育。