今天的故事是我得到了一些错误的结果,我找到了空洞日的原因
原因是我试图使用 BIGINT id 来等于 VARCHAR(64) id。
错误的SQL是:
select t1.id,t1.name,
t2.id,t2.name
from location t1
left join location t2 on t2.parent_id = t1.id
where id = 1649972899018952705
我不知道这个表是谁创建的,但事实是id的类型是BIGINT,parent_id的类型是VARCHAR(64)。
但是为什么?我试过这些:
select 1649972899018952705 = '1649972899018952706' # returns 1
select 164997289901895270 = '164997289901895271' # returns 1
select 164997289901895271 = '164997289901895261' # returns 1
select 16499728990189527 = '164997289901895261' # returns 0
select 164997289901895271 = '16499728990189526' # returns 0
select 16499728990189527 = '16499728990189526' # returns 0
select 1 = '2' # returns 0
我猜MySQL会改变'1649972899018952706'或1649972899018952705的类型来操作,但是参数是怎么改变的,好像MySQL会截断它们。
我可以通过使用不同的 MySQL 版本得到不同的结果吗?
在 MySQL 5.7(以及 MySQL 8.0)中,当一个参数是字符串而另一个是数字时,比较运算符的参数确实会转换为浮点数(双精度),如 12.3 表达式求值中的类型转换中所定义:
以下规则描述了比较操作如何进行转换:
- [...]
- 在所有其他情况下,参数作为浮点(双精度)数字进行比较。例如,字符串和数字操作数的比较是作为浮点数的比较进行的。
所以这就像在参数上调用
CAST(value AS DOUBLE)
(在 MySQL 8.0 中)然后比较它们。显式使用CAST()
函数时看到如下结果:
SELECT CAST(1649972899018952705 AS DOUBLE), CAST('1649972899018952706' AS DOUBLE), CAST(1649972899018952705 AS DOUBLE) = CAST('1649972899018952706' AS DOUBLE);
+-------------------------------------+---------------------------------------+-----------------------------------------------------------------------------+
| CAST(1649972899018952705 AS DOUBLE) | CAST('1649972899018952706' AS DOUBLE) | CAST(1649972899018952705 AS DOUBLE) = CAST('1649972899018952706' AS DOUBLE) |
+-------------------------------------+---------------------------------------+-----------------------------------------------------------------------------+
| 1.6499728990189527e18 | 1.6499728990189527e18 | 1 |
+-------------------------------------+---------------------------------------+-----------------------------------------------------------------------------+
文档还提到了一个解决方案,您可以在其中将一个参数转换为
UNSIGNED
:
(...) 避免此类问题的一种方法是使用 CAST(),这样一个值就不会隐式转换为浮点数:
mysql> SELECT CAST('9223372036854775807' AS UNSIGNED) = 9223372036854775806; -> 0
所以您可以在查询中使用这样的
CAST()
函数:
select t1.id,t1.name,
t2.id,t2.name
from location t1
left join location t2 on CAST(t2.parent_id AS UNSIGNED) = t1.id
where id = 1649972899018952705