我们有一个表,其中包含一列foo
。
show create table
显示`foo` bit(1) DEFAULT b'0'
,因此该列应包含binary strings的第0和第1个字符。
select ascii(foo),
ord(foo),
foo=char(1),
foo=char(0),
char(1)=char(0)
from table_name
group by 1,2,3,4,5
收益
ascii(foo) ord(foo) foo=char(1) foo=char(0) char(1)=char(0)
0 0 1 1 0
1 1 0 0 0
我希望它会产生作用
ascii(foo) ord(foo) foo=char(1) foo=char(0) char(1)=char(0)
0 0 0 1 0
1 1 1 0 0
有人可以解释发生了什么吗?
Nor仅限于select
子句。它也发生在where
子句中:select distinct ascii(foo) from table_name where foo=char(0)
和select distinct ascii(foo) from table_name where foo=char(1)
都只返回0。
select @@version
5.7.21-20-57-log
答案 0 :(得分:0)
MySQL认为将位域列中的值视为integer
,并向CHAR(1)
执行隐式类型转换以进行比较。
这是MySQL实际执行的步骤:
SELECT `foo` = CHAR(1) -- Need to cast incompatible data types
SELECT 0 = CHAR(1) -- Bitfield is treated as integer
SELECT 0 = CAST(CHAR(1) AS INT) -- char 0x01 gets cast to integer
SELECT 0 = 0 -- result: 1 (boolean TRUE for MySQL)
但是隐式类型转换取决于数据。由于很难检测到,因此可以快速导致意外结果。例如:
SELECT `foo` = CHAR(49) -- Need to cast incompatible data types
SELECT 0 = CHAR(49) -- Bitfield is treated as integer
SELECT 0 = CAST(CHAR(49) AS INT) -- char 0x31 gets cast to integer
SELECT 0 = CAST("1" AS INT) -- but: char 0x31 equals "1"
SELECT 0 = 1 -- result: 0 (boolean FALSE for MySQL)
为什么会这样?数字类型是位域和(二进制)字符串都可以与使用相等运算符进行比较的唯一一种类型。因此,位域列将从cast到integer
:
在数字上下文中,MySQL将位文字视为整数。
还需要将角色强制转换为these rules之后的integer
。令人惊讶的是,为了将二进制字符转换为整数,MySQL似乎首先将其转换为常规字符,如下所示:
SELECT CAST(CAST(CHAR(49) AS CHAR) AS INT) -- result 1
MySQL在这里必须执行两次隐式类型转换,因为:
SELECT CHAR(49) -- result 0x31
在这里,CHAR函数返回不带任何编码的二进制char,但是仅当可以使用编码解释字符时,才可以强制转换为integer
。通过CHAR(49)
获得的结果可能与我与MySQL的连接的编码设置有关。